AI-powered applications often need more than a model that accepts a prompt and returns text. They may also need conversation management, tool execution, session handling, and an agent runtime that coordinates multiple steps.
Building all these components from scratch can take considerable effort. The GitHub Copilot SDK provides a way to integrate the Copilot agent runtime into a C# application while allowing developers to configure their own supported model provider.
This approach is useful when an application needs to use a model hosted in Microsoft Foundry, a compatible OpenAI endpoint, or a supported local inference service. It separates the agent runtime from the model that performs inference.
In this article, we will build a small C# console application that connects the GitHub Copilot SDK to a custom model endpoint. We will cover installation, provider configuration, authentication, session creation, error handling, and security considerations.
Understand How the GitHub Copilot SDK Works
The GitHub Copilot SDK provides programmatic access to the agent runtime used by Copilot CLI. A C# application communicates with the SDK, which manages communication with the Copilot CLI runtime. The runtime coordinates model requests and, when enabled, agent tools and other capabilities.
When using your own model, the SDK still provides the agent runtime. The configured provider determines where model inference takes place.
This arrangement is commonly called BYOK, or Bring Your Own Key. In this context, it means configuring your own model provider and its authentication credentials rather than relying on GitHub Copilot authentication for inference.
The SDK supports provider configurations for supported services, including OpenAI-compatible endpoints, Microsoft Foundry, Anthropic, and local model services such as Ollama, subject to the provider's API compatibility and model capabilities.
The basic architecture is:
C# Application
|
v
GitHub Copilot SDK
|
v
Copilot CLI Runtime
|
v
Configured Model Provider
|
v
Model Response
The runtime and model provider have different responsibilities. The SDK manages the agent session and orchestration, while the provider processes the model request.
This distinction is important because configuring a custom model does not remove the requirement for the Copilot runtime.
Prepare the Development Environment
Before writing the application, make sure the required development tools and model endpoint are available.
You will need:
- A compatible .NET SDK.
- The GitHub Copilot SDK NuGet package.
- The Copilot CLI runtime supported by the SDK.
- Access to a model provider that supports the API format configured in the application.
- The model or deployment identifier expected by that provider.
- Valid authentication credentials when the endpoint requires them.
The following example targets .NET 10 and uses an OpenAI-compatible endpoint. Your endpoint must support the Responses API for the configuration shown below. Other providers may require a different API mode or provider-specific settings.
Create a console application and install the SDK:
dotnet new console -n CustomModelApp
cd CustomModelApp
dotnet add package GitHub.Copilot.SDK
The SDK package provides the .NET client. Its supported setup manages the Copilot CLI runtime, but the application still needs a valid runtime configuration and an accessible model endpoint.
For a real project, use a compatible SDK package version and keep the package version under normal dependency management.
Configure the Model Endpoint
The model endpoint and model identifier should be configuration values rather than hardcoded strings scattered throughout the application.
For local development, environment variables are one straightforward option. For an Azure-hosted model, you might configure values such as:
MODEL_ENDPOINT=https://your-resource.example/openai/v1/
MODEL_NAME=your-model-deployment
MODEL_API_KEY=your-secret-key
These are illustrative configuration values, not a working endpoint or credential.
The endpoint must be replaced with the actual API endpoint supplied by your provider. The model name must also match the identifier expected by the provider.
For production deployments, use an appropriate secret manager or managed configuration system. Do not commit API keys to source control or include them in application logs.
Configure the Provider in C#
The ProviderConfig object tells the Copilot SDK how to connect to the model provider.
For an OpenAI-compatible endpoint that supports the Responses API, the configuration can follow this pattern:
using GitHub.Copilot;
var endpoint =
Environment.GetEnvironmentVariable("MODEL_ENDPOINT");
var modelName =
Environment.GetEnvironmentVariable("MODEL_NAME");
var apiKey =
Environment.GetEnvironmentVariable("MODEL_API_KEY");
if (string.IsNullOrWhiteSpace(endpoint))
{
throw new InvalidOperationException(
"MODEL_ENDPOINT is not configured.");
}
if (string.IsNullOrWhiteSpace(modelName))
{
throw new InvalidOperationException(
"MODEL_NAME is not configured.");
}
if (!Uri.TryCreate(
endpoint,
UriKind.Absolute,
out var endpointUri) ||
endpointUri.Scheme != Uri.UriSchemeHttps)
{
throw new InvalidOperationException(
"The model endpoint must be a valid HTTPS URL.");
}
var provider = new ProviderConfig
{
Type = "openai",
BaseUrl = endpoint.TrimEnd('/') + "/",
WireApi = "responses",
ModelId = modelName,
WireModel = modelName,
MaxPromptTokens = 8000,
MaxOutputTokens = 1024
};
if (!string.IsNullOrWhiteSpace(apiKey))
{
provider.Headers = new Dictionary<string, string>
{
["Authorization"] = $"Bearer {apiKey}"
};
}
This example assumes the endpoint uses bearer-token authentication and accepts the Responses API format. Some services use a different authentication header, such as an API-specific key header. Follow the provider's authentication requirements rather than assuming every endpoint accepts the same header.
The provider properties have distinct purposes:
| Property | Purpose |
|---|
| Type | Selects the provider protocol configuration |
| BaseUrl | Identifies the API endpoint |
| WireApi | Specifies the API format used for model requests |
| ModelId | Identifies the model to the Copilot runtime |
| WireModel | Specifies the model identifier sent to the provider |
| MaxPromptTokens | Sets a prompt-token limit where supported |
| MaxOutputTokens | Limits the configured output-token budget where supported |
ModelId and WireModel can contain the same value, but they represent different identifiers. This distinction is useful when the name exposed to the runtime differs from the name expected by the provider.
The token limits should match the capabilities of the selected model. For reasoning models, the output budget may include reasoning tokens, not only the visible answer. A limit that is too small can prevent the model from producing a complete response.
Provider configuration properties and supported API modes can change between SDK versions. Verify them against the version installed in your project.
Start the Copilot Runtime and Create a Session
After configuring the provider, start the SDK client and create a session.
A session represents the conversation context and configuration used for a particular interaction. It allows the application to send prompts and receive responses without manually implementing the entire agent orchestration layer.
For an initial test, disable tools and other optional capabilities. This keeps the first integration focused on verifying that the configured model can answer a prompt.
using GitHub.Copilot;
await using var client = new CopilotClient();
await client.StartAsync();
await using var session =
await client.CreateSessionAsync(new SessionConfig
{
Model = modelName,
Provider = provider,
AvailableTools = [],
Tools = [],
EnableConfigDiscovery = false,
EnableSkills = false,
EnableHostGitOperations = false
});
var response = await session.SendAndWaitAsync(
new MessageOptions
{
Prompt = "Explain dependency injection in C#."
},
TimeSpan.FromMinutes(3));
var answer = response?.Data.Content;
if (string.IsNullOrWhiteSpace(answer))
{
throw new InvalidOperationException(
"The model returned an empty response.");
}
Console.WriteLine(answer);
The client starts the Copilot runtime, while the session uses the configured provider for model inference. The SendAndWaitAsync call sends the prompt and waits for a response, subject to the supplied timeout.
The example uses await using to dispose the client and session asynchronously. This helps release resources when the application finishes or an exception occurs.
The exact session configuration options should be checked against the installed SDK version. If a provider or model does not support a configured capability, simplify the session and test the basic request before enabling additional features.
Choose the Right Authentication Method
Authentication depends on the provider and deployment environment.
For a provider that accepts API keys, supply the key through a secret store or environment-managed configuration. Do not print the key or include it in exception messages.
For Microsoft Foundry or Azure OpenAI, Microsoft Entra ID may be preferable when supported by the chosen endpoint and SDK configuration. It can avoid managing a long-lived API key, but it requires the correct identity, permissions, and token acquisition configuration.
| Authentication method | Suitable use | Consideration |
|---|
| API key | Providers that support key-based access | Protect, rotate, and restrict the key |
| Microsoft Entra ID | Supported Azure-hosted endpoints | Requires identity configuration and appropriate permissions |
| Local unauthenticated endpoint | Isolated local development environments | Must not be exposed to untrusted networks |
Do not assume that the GitHub authentication used by the Copilot runtime automatically authenticates requests to your custom model provider. Provider authentication must be configured separately.
Add Error Handling and Operational Monitoring
A successful local request is only the first step. A production application must also handle unavailable endpoints, invalid credentials, throttling, timeouts, and incomplete responses.
Start with a bounded timeout and handle cancellation explicitly.
using var cancellation =
new CancellationTokenSource(TimeSpan.FromMinutes(3));
try
{
var response = await session.SendAndWaitAsync(
new MessageOptions
{
Prompt = "Summarize this technical requirement."
},
TimeSpan.FromMinutes(3),
cancellation.Token);
var answer = response?.Data.Content;
if (string.IsNullOrWhiteSpace(answer))
{
throw new InvalidOperationException(
"The model returned no usable content.");
}
Console.WriteLine(answer);
}
catch (OperationCanceledException)
{
Console.Error.WriteLine(
"The request was canceled or exceeded its time limit.");
throw;
}
In this example, both the cancellation token and the SDK wait timeout are configured for three minutes. In a real application, use a consistent timeout policy and ensure that cancellation is propagated through the surrounding request workflow.
Also record operational metrics such as request duration, success rate, provider errors, and token usage when available. Avoid logging prompts or responses that may contain confidential information.
Retries should be limited and should account for the provider's rate limits. Do not automatically retry every failure, particularly when the request may have triggered tools or other side effects.
Security Considerations When Enabling Tools
A model that can generate text has a different risk profile from an agent that can execute commands or modify business data.
The initial example deliberately disables tools, skills, configuration discovery, and host Git operations. This reduces the number of moving parts while you verify the provider connection.
When adding tools, expose only the capabilities required for the task. Review permission callbacks, tool configuration, and execution policies before allowing the agent to access files, terminals, databases, or external services.
Do not treat a model's decision to invoke a tool as authorization. The application must independently check permissions before executing sensitive operations.
For production use, run the agent with restricted operating-system permissions, protect credentials, and log significant tool actions. Add capabilities incrementally and test each one independently.
Common Problems and Troubleshooting
The Model Endpoint Returns an Authentication Error
Check that the credential is valid, the expected authentication header is being sent, and the identity has the necessary permissions. Do not assume that a GitHub login grants access to an unrelated provider.
The Provider Rejects the API Request
Verify the endpoint path, API mode, model identifier, and supported request format. A provider that supports an OpenAI-compatible API may not support every API variant.
The Model Returns an Empty Response
Check provider logs, response metadata, output limits, and the model's capabilities. Reasoning models may consume a configured output budget before generating visible text.
The Copilot Runtime Does Not Start
Confirm that the SDK package restored successfully and that the bundled or configured CLI runtime is available for the current environment. Check process permissions and runtime compatibility.
The Application Works Locally but Fails After Deployment
Review the deployed endpoint, secret configuration, network access, operating-system permissions, and runtime dependencies. Local authentication may rely on credentials that are not available to a server process.
Advantages and Disadvantages
The GitHub Copilot SDK allows C# developers to reuse an existing agent runtime instead of implementing session management and orchestration from scratch. The ability to configure a supported model provider can also give teams more control over model hosting, authentication, and provider selection.
However, the application inherits dependencies on the SDK and its runtime. Custom providers may differ in API compatibility, available model capabilities, authentication, and operational behavior. Tool-enabled agents also require careful security controls.
The SDK does not eliminate the need to monitor model usage, validate responses, protect credentials, or test the complete application workflow.
Summary
Connecting a C# application to a custom model using the GitHub Copilot SDK involves configuring a supported provider, starting the Copilot runtime, creating a session, and sending a prompt.
Begin with a minimal session and verify that the model endpoint, authentication, and API format work correctly. Once the basic request succeeds, add tools and other agent capabilities gradually, with explicit permissions and monitoring.
By separating the agent runtime from the model provider, developers can build C# applications around their chosen inference service while retaining the SDK's orchestration capabilities. A reliable implementation still depends on correct provider configuration, secure credential handling, clear error management, and careful control over agent permissions.
Join the conversation! Your thoughts help the community grow.