Update .NET Copilot SDK cookbook for GitHub.Copilot.SDK 1.0 (#2021)

* Update .NET Copilot SDK cookbook for GitHub.Copilot.SDK 1.0

Align the dotnet copilot-sdk cookbook recipes and docs with the 1.0.1 release:

- Namespace GitHub.Copilot.SDK -> GitHub.Copilot

- MCP config uses Dictionary<string, McpServerConfig> + McpStdioServerConfig (drop Type discriminator)

- StopAsync no longer returns an error list; wrap graceful shutdown in try/catch

- GetMessagesAsync -> GetEventsAsync with event pattern matching

- LogLevel string -> CopilotLogLevel.Error enum

* Address PR review: clarify package/namespace, default event case, MCP stdio wording

- Note that the GitHub.Copilot.SDK package exposes the GitHub.Copilot namespace in each recipe

- Add a default case + note to the GetEventsAsync history example so other event kinds are not silently dropped

- Refine accessibility-report docs to describe a local stdio MCP server (McpStdioServerConfig via npx)

* Address re-review: add using for event types, note StopAsync throw behavior
This commit is contained in:
Jon Galloway
2026-06-16 21:13:20 -07:00
committed by GitHub
parent fae6a92c9d
commit 1140812aaa
14 changed files with 65 additions and 36 deletions
@@ -32,7 +32,7 @@ dotnet run recipe/accessibility-report.cs
```csharp
#:package GitHub.Copilot.SDK@*
using GitHub.Copilot.SDK;
using GitHub.Copilot;
// Create and start client
await using var client = new CopilotClient();
@@ -65,12 +65,11 @@ await using var session = await client.CreateSessionAsync(new SessionConfig
Model = "claude-opus-4.6",
Streaming = true,
OnPermissionRequest = PermissionHandler.ApproveAll,
McpServers = new Dictionary<string, object>()
McpServers = new Dictionary<string, McpServerConfig>()
{
["playwright"] =
new McpLocalServerConfig
new McpStdioServerConfig
{
Type = "local",
Command = "npx",
Args = ["@playwright/mcp@latest"],
Tools = ["*"]
@@ -195,7 +194,7 @@ if (generateTests == "y" || generateTests == "yes")
## How it works
1. **Playwright MCP server**: Configures a local MCP server running `@playwright/mcp` to provide browser automation tools
1. **Playwright MCP server**: Configures a local stdio MCP server (`McpStdioServerConfig`, launched via `npx`) running `@playwright/mcp` to provide browser automation tools
2. **Streaming output**: Uses `Streaming = true` and `AssistantMessageDeltaEvent` for real-time token-by-token output
3. **Accessibility snapshot**: Playwright's `browser_snapshot` tool captures the full accessibility tree of the page
4. **Structured report**: The prompt engineers a consistent WCAG-aligned report format with emoji severity indicators
@@ -205,15 +204,14 @@ if (generateTests == "y" || generateTests == "yes")
### MCP server configuration
The recipe configures a local MCP server that runs alongside the session:
The recipe configures a local stdio MCP server (`McpStdioServerConfig`, launched via `npx`) that runs alongside the session:
```csharp
OnPermissionRequest = PermissionHandler.ApproveAll,
McpServers = new Dictionary<string, object>()
McpServers = new Dictionary<string, McpServerConfig>()
{
["playwright"] = new McpLocalServerConfig
["playwright"] = new McpStdioServerConfig
{
Type = "local",
Command = "npx",
Args = ["@playwright/mcp@latest"],
Tools = ["*"]
+12 -5
View File
@@ -15,7 +15,7 @@ You need to handle various error conditions like connection failures, timeouts,
## Basic try-catch
```csharp
using GitHub.Copilot.SDK;
using GitHub.Copilot;
var client = new CopilotClient();
@@ -134,16 +134,23 @@ Console.CancelKeyPress += async (sender, e) =>
e.Cancel = true;
Console.WriteLine("Shutting down...");
var errors = await client.StopAsync();
if (errors.Count > 0)
try
{
Console.WriteLine($"Cleanup errors: {string.Join(", ", errors)}");
await client.StopAsync();
}
catch (Exception ex)
{
Console.WriteLine($"Cleanup error: {ex.Message}");
}
Environment.Exit(0);
};
```
> In 1.0, `StopAsync()` throws if it encounters errors during cleanup rather than returning a
> list of cleanup errors, so wrap it in a try/catch to log failures instead of letting them
> crash shutdown. Use `ForceStopAsync()` if a graceful stop takes too long.
## Using await using for automatic disposal
```csharp
@@ -163,7 +170,7 @@ var session = await client.CreateSessionAsync(new SessionConfig
## Best practices
Starting with Copilot SDK v0.1.28, permission handling is opt-in. If a session may need tool, file, or system access, set `OnPermissionRequest` explicitly when creating it.
Permission handling is opt-in. If a session may need tool, file, or system access, set `OnPermissionRequest` explicitly when creating it.
1. **Always clean up**: Use try-finally or `await using` to ensure `StopAsync()` is called
2. **Handle connection errors**: The CLI might not be installed or running
@@ -16,7 +16,7 @@ You have a folder with many files and want to organize them into subfolders base
## Example code
```csharp
using GitHub.Copilot.SDK;
using GitHub.Copilot;
// Create and start client
await using var client = new CopilotClient();
@@ -15,7 +15,7 @@ You need to run multiple conversations in parallel, each with its own context an
## C #
```csharp
using GitHub.Copilot.SDK;
using GitHub.Copilot;
await using var client = new CopilotClient();
await client.StartAsync();
@@ -16,7 +16,7 @@ You want users to be able to continue a conversation even after closing and reop
### Creating a session with a custom ID
```csharp
using GitHub.Copilot.SDK;
using GitHub.Copilot;
await using var client = new CopilotClient();
await client.StartAsync();
@@ -74,16 +74,34 @@ await client.DeleteSessionAsync("user-123-conversation");
### Getting session history
Retrieve all messages from a session:
Retrieve all events from a session:
```csharp
var messages = await session.GetMessagesAsync();
foreach (var msg in messages)
using GitHub.Copilot; // UserMessageEvent, AssistantMessageEvent, etc. live in this namespace
var events = await session.GetEventsAsync();
foreach (var evt in events)
{
Console.WriteLine($"[{msg.Type}] {msg.Data.Content}");
switch (evt)
{
case UserMessageEvent user:
Console.WriteLine($"[user] {user.Data.Content}");
break;
case AssistantMessageEvent assistant:
Console.WriteLine($"[assistant] {assistant.Data.Content}");
break;
default:
// Sessions can also contain other events (tool calls, tool results, system events).
Console.WriteLine($"[{evt.GetType().Name}]");
break;
}
}
```
> A session's event stream may include event kinds beyond user and assistant messages
> (for example tool calls, tool results, and system events). Handle the ones you care
> about and fall back to a default case so nothing is silently dropped.
## Best practices
1. **Use meaningful session IDs**: Include user ID or context in the session ID
@@ -36,7 +36,7 @@ dotnet run -- --repo github/copilot-sdk
```csharp
using System.Diagnostics;
using GitHub.Copilot.SDK;
using GitHub.Copilot;
// ============================================================================
// Git & GitHub Detection
@@ -159,7 +159,7 @@ var owner = parts[0];
var repoName = parts[1];
// Create Copilot client - no custom tools needed!
await using var client = new CopilotClient(new CopilotClientOptions { LogLevel = "error" });
await using var client = new CopilotClient(new CopilotClientOptions { LogLevel = CopilotLogLevel.Error });
await client.StartAsync();
var session = await client.CreateSessionAsync(new SessionConfig
+2 -2
View File
@@ -42,7 +42,7 @@ A [Ralph loop](https://ghuntley.com/ralph/) is an autonomous development workflo
The minimal Ralph loop — the SDK equivalent of `while :; do cat PROMPT.md | copilot ; done`:
```csharp
using GitHub.Copilot.SDK;
using GitHub.Copilot;
var client = new CopilotClient();
await client.StartAsync();
@@ -96,7 +96,7 @@ This is all you need to get started. The prompt file tells the agent what to do;
The full Ralph pattern with planning and building modes, matching the [Ralph Playbook](https://github.com/ClaytonFarr/ralph-playbook) architecture:
```csharp
using GitHub.Copilot.SDK;
using GitHub.Copilot;
// Parse args: dotnet run [plan] [max_iterations]
var mode = args.Contains("plan") ? "plan" : "build";
@@ -1,6 +1,7 @@
#:package GitHub.Copilot.SDK@*
using GitHub.Copilot.SDK;
// The GitHub.Copilot.SDK package exposes the GitHub.Copilot namespace.
using GitHub.Copilot;
// Create and start client
await using var client = new CopilotClient();
@@ -33,12 +34,11 @@ await using var session = await client.CreateSessionAsync(new SessionConfig
Model = "claude-opus-4.6",
Streaming = true,
OnPermissionRequest = PermissionHandler.ApproveAll,
McpServers = new Dictionary<string, object>()
McpServers = new Dictionary<string, McpServerConfig>()
{
["playwright"] =
new McpLocalServerConfig
new McpStdioServerConfig
{
Type = "local",
Command = "npx",
Args = ["@playwright/mcp@latest"],
Tools = ["*"]
@@ -1,7 +1,8 @@
#:package GitHub.Copilot.SDK@*
#:property PublishAot=false
using GitHub.Copilot.SDK;
// The GitHub.Copilot.SDK package exposes the GitHub.Copilot namespace.
using GitHub.Copilot;
var client = new CopilotClient();
@@ -1,7 +1,8 @@
#:package GitHub.Copilot.SDK@*
#:property PublishAot=false
using GitHub.Copilot.SDK;
// The GitHub.Copilot.SDK package exposes the GitHub.Copilot namespace.
using GitHub.Copilot;
// Create and start client
await using var client = new CopilotClient();
@@ -1,7 +1,8 @@
#:package GitHub.Copilot.SDK@*
#:property PublishAot=false
using GitHub.Copilot.SDK;
// The GitHub.Copilot.SDK package exposes the GitHub.Copilot namespace.
using GitHub.Copilot;
await using var client = new CopilotClient();
await client.StartAsync();
@@ -1,7 +1,8 @@
#:package GitHub.Copilot.SDK@*
#:property PublishAot=false
using GitHub.Copilot.SDK;
// The GitHub.Copilot.SDK package exposes the GitHub.Copilot namespace.
using GitHub.Copilot;
await using var client = new CopilotClient();
await client.StartAsync();
@@ -2,7 +2,8 @@
#:property PublishAot=false
using System.Diagnostics;
using GitHub.Copilot.SDK;
// The GitHub.Copilot.SDK package exposes the GitHub.Copilot namespace.
using GitHub.Copilot;
// ============================================================================
// Git & GitHub Detection
@@ -126,7 +127,7 @@ var owner = parts[0];
var repoName = parts[1];
// Create Copilot client - no custom tools needed!
await using var client = new CopilotClient(new CopilotClientOptions { LogLevel = "error" });
await using var client = new CopilotClient(new CopilotClientOptions { LogLevel = CopilotLogLevel.Error });
await client.StartAsync();
var session = await client.CreateSessionAsync(new SessionConfig
@@ -1,6 +1,7 @@
#:package GitHub.Copilot.SDK@*
using GitHub.Copilot.SDK;
// The GitHub.Copilot.SDK package exposes the GitHub.Copilot namespace.
using GitHub.Copilot;
// Ralph loop: autonomous AI task loop with fresh context per iteration.
//