![]() |
|
[DevBlog MS] Bring Your Own Model to the GitHub Copilot SDK with C# - Printable Version +- Sick Gaming (https://sickgaming.net) +-- Forum: Programming (https://sickgaming.net/forum-76.html) +--- Forum: C#, Visual Basic & .NET (https://sickgaming.net/forum-79.html) +--- Thread: [DevBlog MS] Bring Your Own Model to the GitHub Copilot SDK with C# (/thread-113983.html) |
[DevBlog MS] Bring Your Own Model to the GitHub Copilot SDK with C# - xSicKxBot - 10-19-2026 Hi, I know, I keep coming back to the same theme on this blog: agent harnesses. In Build Your Own AI Agent Harness in C# we built one from scratch. In From dotnet run to Foundry Hosted Agent in 3 lines of C# we took an agent and hosted it. Today we use a harness we do not have to build at all: the one GitHub already ships, through the GitHub Copilot SDK. One of the most popular questions we received during the Let’s Learn GitHub Copilot SDK sessions was: Quote:“Can I use the Copilot SDK with my own model deployment?” The short answer is yes. The longer answer is, the GitHub Copilot SDK gives your application the Copilot agent runtime, while Code: ProviderConfigThe complete sample is now part of Generative AI for Beginners .NET. It is a small .NET 10 console application: read configuration, create a BYOK provider, start the Copilot runtime, create one session, and ask one question. Get the complete BYOK C# sample What BYOK means in this sample BYOK means Bring Your Own Key, but in this context the important idea is broader than the key itself: you bring your own model provider. The Copilot SDK still supplies the runtime that manages the client, session, system message, and agent configuration. Code: ProviderConfigBYOK is not customer-managed encryption keys In this article, BYOK means using your own model endpoint and credentials for inference. It does not refer to customer-managed encryption keys or a storage-encryption configuration. The sample intentionally keeps the session minimal. It disables tools, skills, configuration discovery, and host Git operations so that the first run demonstrates one thing clearly: a Copilot SDK session can send a model request to your own Foundry-compatible deployment. The runtime and the model provider are separate parts of the application. The project The sample is a regular .NET 10 console application. These are the packages it uses: Code: [code]<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>genai-beginners-dotnet</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.21.0" />
<PackageReference Include="GitHub.Copilot.SDK" Version="1.0.11" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.3" />
</ItemGroup>
</Project>[/code]Code: GitHub.Copilot.SDKCode: Azure.IdentityCode: AzureCliCredentialCode: az loginStep 1: read the model configuration The sample uses the shared User Secrets values already used by the repository: Code: [code]// User Secrets keeps the endpoint and credentials outside the repository.
var configuration = new ConfigurationBuilder()
.AddUserSecrets<Program>(optional: true)
.Build();
// This sample uses the repo's shared AzureOpenAI secrets.
var modelName = GetRequiredValue(configuration, "AzureOpenAI:Deployment");
var endpoint = GetRequiredValue(configuration, "AzureOpenAI:Endpoint");
var apiKey = configuration["AzureOpenAI:ApiKey"]?.Trim();[/code]There are two required values:
Code: AzureOpenAI:ApiKeyCode: AzureCliCredentialCode: api-keyStep 2: configure the provider This is the core of the sample: Code: [code]// Microsoft Foundry exposes an API compatible with OpenAI Responses.
var provider = new ProviderConfig
{
Type = "openai",
BaseUrl = NormalizeEndpoint(endpoint),
WireApi = "responses",
ModelId = modelName,
WireModel = modelName,
MaxPromptTokens = 8000,
MaxOutputTokens = 512
};[/code]The configuration tells the Copilot runtime how to communicate with the provider:
For a reasoning model, Code: MaxOutputTokensCode: MaxOutputTokensIn this sample, Code: ModelIdCode: WireModelThe endpoint helper accepts either the resource URL or a URL that already includes Code: /openai/v1/Code: [code]static string NormalizeEndpoint(string endpoint)
{
var candidate = endpoint.Trim();
if (!Uri.TryCreate(candidate, UriKind.Absolute, out var uri) ||
uri.Scheme != Uri.UriSchemeHttps ||
string.IsNullOrWhiteSpace(uri.Host))
{
throw new ArgumentException("The configured AzureOpenAI:Endpoint must be an HTTPS URL.");
}
// Accept the resource URL or a URL that already ends in /openai/v1.
// Trimming first means a trailing slash does not need a separate check.
var trimmed = candidate.TrimEnd('/');
if (trimmed.EndsWith("/openai/v1", StringComparison.OrdinalIgnoreCase))
{
return trimmed + "/";
}
return $"{uri.GetLeftPart(UriPartial.Authority).TrimEnd('/')}/openai/v1/";
}[/code]The helper also requires HTTPS. It is better to reject a malformed endpoint before creating a session than to discover it after the first model request. Step 3: use Entra ID by default The default authentication path uses the Azure CLI login: Code: [code]if (string.IsNullOrWhiteSpace(apiKey))
{
// AzureCliCredential reuses the session created with `az login`.
var credential = new AzureCliCredential();
var scope = "https://cognitiveservices.azure.com/.default";
#pragma warning disable GHCP001 // BYOK auth callbacks are experimental in the Copilot SDK.
provider.BearerTokenProvider = async _ =>
{
var token = await credential.GetTokenAsync(
new TokenRequestContext([scope]),
cancellation.Token);
return token.Token;
};
#pragma warning restore GHCP001
}[/code]Before signing in, make sure your identity has the Cognitive Services OpenAI User role on the resource. Then run: Code: [code]az login[/code]Code: BearerTokenProviderStep 4: use an API key when one is configured Some deployments or environments still require an API key. The sample supports that path without changing the rest of the provider configuration: Code: [code]else
{
provider.Headers = new Dictionary<string, string>
{
["api-key"] = apiKey
};
}[/code]Set the optional secret: Code: [code]dotnet user-secrets set "AzureOpenAI:ApiKey" "<your-api-key>"[/code]When the value exists, the sample uses the Code: api-keyCode: az loginKeep credentials out of source control Use User Secrets for local development, environment-managed configuration for deployed applications, and never paste an API key into the source file or a README. Step 5: start the Copilot runtime The provider controls model inference, but the Copilot runtime is still required: Code: [code]await using var client = new CopilotClient(new CopilotClientOptions
{
Connection = RuntimeConnection.ForStdio("copilot"),
WorkingDirectory = AppContext.BaseDirectory
});
await client.StartAsync(cancellation.Token);[/code]This is an important distinction. BYOK does not mean “the Copilot SDK without Copilot.” The local Copilot CLI runtime still provides the orchestration process. BYOK changes where the model request goes. Prerequisites Install the .NET 10 SDK and the GitHub Copilot CLI, and make sure Code: dotnet --versionCode: copilot --versionCode: az --versionCode: RuntimeConnection.ForStdio("copilot")Step 6: create a deliberately small session The session configuration connects the model name and provider, then disables capabilities that are outside the scope of this first example: Code: [code]// Disable tools, skills, and Git operations to isolate the model request.
await using var session = await client.CreateSessionAsync(new SessionConfig
{
ClientName = "github-copilot-sdk-byok",
Model = modelName,
Provider = provider,
SystemMessage = new SystemMessageConfig
{
Mode = SystemMessageMode.Replace,
Content = "Answer every user question in English. Be concise and factual."
},
AvailableTools = [],
Tools = [],
EnableConfigDiscovery = false,
EnableSkills = false,
EnableHostGitOperations = false
}, cancellation.Token);[/code]The empty tool collections and disabled features are intentional. They make the sample easier to reason about and safer to run while learning the provider configuration. The system message also makes the output predictable for a multilingual live-learning sample. Code: SystemMessageMode.ReplaceStep 7: send one question Finally, the application sends one prompt and waits for the final response: Code: [code]var response = await session.SendAndWaitAsync(
new MessageOptions { Prompt = question },
TimeSpan.FromMinutes(3),
cancellation.Token);
var content = response?.Data.Content;
if (string.IsNullOrWhiteSpace(content))
{
throw new InvalidOperationException("The model returned an empty response.");
}
Console.WriteLine();
Console.WriteLine("Answer:");
Console.WriteLine(content);[/code]The default question is: Code: [code]What is Microsoft Foundry, and how does it relate to the GitHub Copilot SDK?[/code]You can provide another question as command-line arguments: Code: [code]dotnet run -- "Explain BYOK in one sentence."[/code]Run the sample From the sample directory, configure the required values: Code: [code]dotnet user-secrets set "AzureOpenAI:Endpoint" "https://<resource>.services.ai.azure.com"
dotnet user-secrets set "AzureOpenAI:Deployment" "<your-deployment-name>"[/code]Then use the default Entra ID path: Code: [code]az login
dotnet run[/code]The output looks like this: Code: [code]Model: <your-deployment-name>
Authentication: AzureCliCredential
Question: What is Microsoft Foundry, and how does it relate to the GitHub Copilot SDK?
Answer:
<model response>[/code]The sample prints the selected model and authentication mode, but not the endpoint or credentials. So, what did we actually learn here? Honestly, the part I like most is how little code this takes. ![]() The Copilot SDK runtime and the model provider are two different things, and once that clicks, the rest is easy. You keep the runtime that GitHub gives you: the client, the session, the system message, and the agent plumbing. Then you just point inference somewhere else. That “somewhere else” is your Foundry deployment, your OpenAI key, your local Ollama, whatever you need. Entra ID is the default path here, and I think it should be. No key to rotate, no key to leak, no key to accidentally commit. Code: az loginAnd the session is deliberately boring. No tools, no skills, no MCP, no Git access. That is on purpose: the first run has exactly one job, which is proving that your model is answering. Once you see that answer in the console, you have a working baseline. That is the fun part. From here you can start adding things one at a time: tools, MCP servers, skills, approvals, and streaming. When something breaks, you will know exactly which piece broke it. Go try it with your own deployment. ![]() Learn more
Explore the complete GitHub Copilot SDK sample Happy coding! Bruno The post Bring Your Own Model to the GitHub Copilot SDK with C# appeared first on .NET Blog. |