Use when the user needs guidance on copilot-sdk-csharp. GitHub Copilot instruction from the awesome-copilot collection.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill ghcopilot-instr-copilot-sdk-csharp --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Ghcopilot Instr Copilot Sdk Csharp?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-ghcopilot-instr-copilot-sdk-csharp)More formats (shields.io, HTML) on the badges page.
---
name: ghcopilot-instr-copilot-sdk-csharp
description: "Use when the user needs guidance on copilot-sdk-csharp. GitHub Copilot instruction from the awesome-copilot collection."
license: CC-BY-NC-SA-4.0
metadata:
version: "1.0.0"
tags: [copilot-instruction, community, copilot-sdk-csharp]
applyTo: "**.cs, **.csproj"
source: "awesome-copilot"
---
## Core Principles
- The SDK is in technical preview and may have breaking changes
- Requires .NET 10.0 or later
- Requires GitHub Copilot CLI installed and in PATH
- Uses async/await patterns throughout
- Implements IAsyncDisposable for resource cleanup
## Installation
Always install via NuGet:
```bash
dotnet add package GitHub.Copilot.SDK
```
## Client Initialization
### Basic Client Setup
```csharp
await using var client = new CopilotClient();
await client.StartAsync();
```
### Client Configuration Options
When creating a CopilotClient, use `CopilotClientOptions`:
- `CliPath` - Path to CLI executable (default: "copilot" from PATH)
- `CliArgs` - Extra arguments prepended before SDK-managed flags
- `CliUrl` - URL of existing CLI server (e.g., "localhost:8080"). When provided, client won't spawn a process
- `Port` - Server port (default: 0 for random)
- `UseStdio` - Use stdio transport instead of TCP (default: true)
- `LogLevel` - Log level (default: "info")
- `AutoStart` - Auto-start server (default: true)
- `AutoRestart` - Auto-restart on crash (default: true)
- `Cwd` - Working directory for the CLI process
- `Environment` - Environment variables for the CLI process
- `Logger` - ILogger instance for SDK logging
### Manual Server Control
For explicit control:
```csharp
var client = new CopilotClient(new CopilotClientOptions { AutoStart = false });
await client.StartAsync();
// Use client...
await client.StopAsync();
```
Use `ForceStopAsync()` when `StopAsync()` takes too long.
## Session Management
### Creating Sessions
Use `SessionConfig` for configuration:
```csharp
await using var session = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
Model = "gpt-5",
Streaming = true,
Tools = [...],
SystemMessage = new SystemMessageConfig { ... },
AvailableTools = ["tool1", "tool2"],
ExcludedTools = ["tool3"],
Provider = new ProviderConfig { ... }
});
```
### Session Config Options
- `SessionId` - Custom session ID
- `Model` - Model name ("gpt-5", "claude-sonnet-4.5", etc.)
- `Tools` - Custom tools exposed to the CLI
- `SystemMessage` - System message customization
- `AvailableTools` - Allowlist of tool names
- `ExcludedTools` - Blocklist of tool names
- `Provider` - Custom API provider configuration (BYOK)
- `Streaming` - Enable streaming response chunks (default: false)
### Resuming Sessions
```csharp
var session = await client.ResumeSessionAsync(sessionId, new ResumeSessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
// ...
});
```
### Session Operations
- `session.SessionId` - Get session identifier
- `session.SendAsync(new MessageOptions { Prompt = "...", Attachments = [...] })` - Send message
- `session.AbortAsync()` - Abort current processing
- `session.GetMessagesAsync()` - Get all events/messages
- `await session.DisposeAsync()` - Clean up resources
## Event Handling
### Event Subscription Pattern
ALWAYS use TaskCompletionSource for waiting on session events:
```csharp
var done = new TaskCompletionSource();
session.On(evt =>
{
if (evt is AssistantMessageEvent msg)
{
Console.WriteLine(msg.Data.Content);
}
else if (evt is SessionIdleEvent)
{
done.SetResult();
}
});
await session.SendAsync(new MessageOptions { Prompt = "..." });
await done.Task;
```
### Unsubscribing from Events
The `On()` method returns an IDisposable:
```csharp
var subscription = session.On(evt => { /* handler */ });
// Later...
subscription.Dispose();
```
### Event Types
Use pattern matching or switch expressions for event handling:
```csharp
session.On(evt =>
{
switch (evt)
{
case UserMessageEvent userMsg:
// Handle user message
break;
case AssistantMessageEvent assistantMsg:
Console.WriteLine(assistantMsg.Data.Content);
break;
case ToolExecutionStartEvent toolStart:
// Tool execution started
break;
case ToolExecutionCompleteEvent toolComplete:
// Tool execution completed
break;
case SessionStartEvent start:
// Session started
break;
case SessionIdleEvent idle:
// Session is idle (processing complete)
break;
case SessionErrorEvent error:
Console.WriteLine($"Error: {error.Data.Message}");
break;
}
});
```
## Streaming Responses
### Enabling Streaming
Set `Streaming = true` in SessionConfig:
```csharp
var session = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
Model = "gpt-5",
Streaming = true
});
```
### Handling Streaming Events
Handle both delta events (incremental) and final events:
```csharp
var done = new TaskCompletionSource();
session.On(evt =>
{
switch (evt)
{
case AssistantMessageDeltaEvent delta:
// Incremental text chunk
Console.Write(delta.Data.DeltaContent);
break;
case AssistantReasoningDeltaEvent reasoningDelta:
// Incremental reasoning chunk (model-dependent)
Console.Write(reasoningDelta.Data.DeltaContent);
break;
case AssistantMessageEvent msg:
// Final complete message
Console.WriteLine("\n--- Final ---");
Console.WriteLine(msg.Data.Content);
break;
case AssistantReasoningEvent reasoning:
// Final reasoning content
Console.WriteLine("--- Reasoning ---");
Console.WriteLine(reasoning.Data.Content);
break;
case SessionIdleEvent:
done.SetResult();
break;
}
});
await session.SendAsync(new MessageOptions { Prompt = "Tell me a story" });
await done.Task;
```
Note: Final events (`AssistantMessageEvent`, `AssistantReasoningEvent`) are ALWAYS sent regardless of streaming setting.
## Custom Tools
### Defining Tools with AIFunctionFactory
Use `Microsoft.Extensions.AI.AIFunctionFactory.Create` for type-safe tools:
```csharp
using Microsoft.Extensions.AI;
using System.ComponentModel;
var session = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
Model = "gpt-5",
Tools = [
AIFunctionFactory.Create(
async ([Description("Issue ID")] string id) => {
var issue = await FetchIssueAsync(id);
return issue;
},
"lookup_issue",
"Fetch issue details from tracker"),
]
});
```
### Tool Return Types
- Return any JSON-serializable value (automatically wrapped)
- Or return `ToolResultAIContent` wrapping `ToolResultObject` for full control over metadata
### Tool Execution Flow
When Copilot invokes a tool, the client automatically:
1. Runs your handler function
2. Serializes the return value
3. Responds to the CLI
## System Message Customization
### Append Mode (Default - Preserves Guardrails)
```csharp
var session = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
Model = "gpt-5",
SystemMessage = new SystemMessageConfig
{
Mode = SystemMessageMode.Append,
Content = @"
<workflow_rules>
- Always check for security vulnerabilities
- Suggest performance improvements when applicable
</workflow_rules>
"
}
});
```
### Replace Mode (Full Control - Removes Guardrails)
```csharp
var session = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
Model = "gpt-5",
SystemMessage = new SystemMessageConfig
{
Mode = SystemMessageMode.Replace,
Content = "You are a helpful assistant."
}
});
```
## File Attachments
Attach files to messages using `UserMessageDataAttachmentsItem`:
```csharp
await session.SendAsync(new MessageOptions
{
Prompt = "Analyze this file",
Attachments = new List<UserMessageDataAttachmentsItem>
{
new UserMessageDataAttachmentsItem
{
Type = UserMessageDataAttachmentsItemType.File,
Path = "/path/to/file.cs",
DisplayName = "My File"
}
}
});
```
## Message Delivery Modes
Use the `Mode` property in `MessageOptions`:
- `"enqueue"` - Queue message for processing
- `"immediate"` - Process message immediately
```csharp
await session.SendAsync(new MessageOptions
{
Prompt = "...",
Mode = "enqueue"
});
```
## Multiple Sessions
Sessions are independent and can run concurrently:
```csharp
var session1 = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
Model = "gpt-5",
});
var session2 = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
Model = "claude-sonnet-4.5",
});
await session1.SendAsync(new MessageOptions { Prompt = "Hello from session 1" });
await session2.SendAsync(new MessageOptions { Prompt = "Hello from session 2" });
```
## Bring Your Own Key (BYOK)
Use custom API providers via `ProviderConfig`:
```csharp
var session = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
Provider = new ProviderConfig
{
Type = "openai",
BaseUrl = "https://api.openai.com/v1",
ApiKey = "your-api-key"
}
});
```
## Session Lifecycle Management
### Listing Sessions
```csharp
var sessions = await client.ListSessionsAsync();
foreach (var metadata in sessions)
{
Console.WriteLine($"Session: {metadata.SessionId}");
}
```
### Deleting Sessions
```csharp
await client.DeleteSessionAsync(sessionId);
```
### Checking Connection State
```csharp
var state = client.State;
```
## Error Handling
### Standard Exception Handling
```csharp
try
{
var session = await client.CreateSessionAsync(new SessionConfig { OnPermissionRequest = PermissionHandler.ApproveAll });
await session.SendAsync(new MessageOptions { Prompt = "Hello" });
}
catch (StreamJsonRpc.RemoteInvocationException ex)
{
Console.Error.WriteLine($"JSON-RPC Error: {ex.Message}");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error: {ex.Message}");
}
```
### Session Error Events
Monitor `SessionErrorEvent` for runtime errors:
```csharp
session.On(evt =>
{
if (evt is SessionErrorEvent error)
{
Console.Error.WriteLine($"Session Error: {error.Data.Message}");
}
});
```
## Connectivity Testing
Use PingAsync to verify server connectivity:
```csharp
var response = await client.PingAsync("test message");
```
## Resource Cleanup
### Automatic Cleanup with Using
ALWAYS use `await using` for automatic disposal:
```csharp
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig { OnPermissionRequest = PermissionHandler.ApproveAll });
// Resources automatically cleaned up
```
### Manual Cleanup
If not using `await using`:
```csharp
var client = new CopilotClient();
try
{
await client.StartAsync();
// Use client...
}
finally
{
await client.StopAsync();
}
```
## Best Practices
1. **Always use `await using`** for CopilotClient and CopilotSession
2. **Use TaskCompletionSource** to wait for SessionIdleEvent
3. **Handle SessionErrorEvent** for robust error handling
4. **Use pattern matching** (switch expressions) for event handling
5. **Enable streaming** for better UX in interactive scenarios
6. **Use AIFunctionFactory** for type-safe tool definitions
7. **Dispose event subscriptions** when no longer needed
8. **Use SystemMessageMode.Append** to preserve safety guardrails
9. **Provide descriptive tool names and descriptions** for better model understanding
10. **Handle both delta and final events** when streaming is enabled
## Common Patterns
### Simple Query-Response
```csharp
await using var client = new CopilotClient();
await client.StartAsync();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
Model = "gpt-5"
});
var done = new TaskCompletionSource();
session.On(evt =>
{
if (evt is AssistantMessageEvent msg)
{
Console.WriteLine(msg.Data.Content);
}
else if (evt is SessionIdleEvent)
{
done.SetResult();
}
});
await session.SendAsync(new MessageOptions { Prompt = "What is 2+2?" });
await done.Task;
```
### Multi-Turn Conversation
```csharp
await using var session = await client.CreateSessionAsync(new SessionConfig { OnPermissionRequest = PermissionHandler.ApproveAll });
async Task SendAndWait(string prompt)
{
var done = new TaskCompletionSource();
var subscription = session.On(evt =>
{
if (evt is AssistantMessageEvent msg)
{
Console.WriteLine(msg.Data.Content);
}
else if (evt is SessionIdleEvent)
{
done.SetResult();
}
});
await session.SendAsync(new MessageOptions { Prompt = prompt });
await done.Task;
subscription.Dispose();
}
await SendAndWait("What is the capital of France?");
await SendAndWait("What is its population?");
```
### Tool with Complex Return Type
```csharp
var session = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
Tools = [
AIFunctionFactory.Create(
([Description("User ID")] string userId) => {
return new {
Id = userId,
Name = "John Doe",
Email = "john@example.com",
Role = "Developer"
};
},
"get_user",
"Retrieve user information")
]
});
```
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!