diff --git a/README.md b/README.md
index be9b4694b..53605777c 100644
--- a/README.md
+++ b/README.md
@@ -108,6 +108,7 @@ Please use the [GitHub Issues](https://github.com/github/copilot-sdk/issues) pag
## Quick Links
- **[Getting Started](./docs/getting-started.md)** – Tutorial to get up and running
+- **[Practical .NET Integration Guide](./docs/guides/dotnet-integration.md)** – Step-by-step C# walkthrough for Visual Studio / VS Code
- **[Authentication](./docs/auth/index.md)** – GitHub OAuth, BYOK, and more
- **[Cookbook](https://github.com/github/awesome-copilot/blob/main/cookbook/copilot-sdk)** – Practical recipes for common tasks across all languages
- **[More Resources](https://github.com/github/awesome-copilot/blob/main/collections/copilot-sdk.md)** – Additional examples, tutorials, and community resources
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 05bbde8dc..b00e08e43 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -1401,6 +1401,7 @@ await using var session = await client.CreateSessionAsync(new()
- [Python SDK Reference](../python/README.md)
- [Go SDK Reference](../go/README.md)
- [.NET SDK Reference](../dotnet/README.md)
+- [**Practical .NET Integration Guide**](./guides/dotnet-integration.md) - Step-by-step C# walkthrough (project setup, streaming, tools, hooks, IDE tips)
- [Using MCP Servers](./mcp) - Integrate external tools via Model Context Protocol
- [GitHub MCP Server Documentation](https://github.com/github/github-mcp-server)
- [MCP Servers Directory](https://github.com/modelcontextprotocol/servers) - Explore more MCP servers
diff --git a/docs/guides/dotnet-integration.md b/docs/guides/dotnet-integration.md
new file mode 100644
index 000000000..a73c1099d
--- /dev/null
+++ b/docs/guides/dotnet-integration.md
@@ -0,0 +1,691 @@
+# Practical Guide: Integrating the Copilot SDK into a C# Project
+
+This guide walks you through every step needed to embed the GitHub Copilot SDK into a C# application — from a blank project to a fully working AI-powered assistant — using Visual Studio or VS Code with the C# Dev Kit.
+
+---
+
+## Prerequisites
+
+| Requirement | Version | Notes |
+|---|---|---|
+| **.NET SDK** | 8.0 or later | `dotnet --version` to check |
+| **GitHub Copilot CLI** | latest | [Installation guide](https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli) |
+| **GitHub Copilot subscription** | — | Free tier available; required unless using BYOK |
+| **IDE** | Visual Studio 2022 17.8+ or VS Code with C# Dev Kit | |
+
+Verify the CLI is installed and authenticated:
+
+```bash
+copilot --version
+copilot auth status
+```
+
+---
+
+## Step 1 — Create a New C# Project
+
+### Visual Studio
+
+1. Open Visual Studio → **Create a new project**
+2. Select **Console App** (.NET) → click **Next**
+3. Enter a project name, e.g. `MyCopilotApp` → click **Next**
+4. Select **.NET 8.0** → click **Create**
+
+### VS Code
+
+```bash
+mkdir MyCopilotApp && cd MyCopilotApp
+dotnet new console --framework net8.0
+code .
+```
+
+### .NET CLI (any terminal)
+
+```bash
+mkdir MyCopilotApp && cd MyCopilotApp
+dotnet new console --framework net8.0
+```
+
+---
+
+## Step 2 — Install the NuGet Package
+
+### Visual Studio — NuGet Package Manager
+
+1. Right-click the project → **Manage NuGet Packages**
+2. Search for `GitHub.Copilot.SDK`
+3. Click **Install**
+
+### .NET CLI
+
+```bash
+dotnet add package GitHub.Copilot.SDK
+```
+
+Verify the package is referenced in your `.csproj`:
+
+```xml
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+```
+
+---
+
+## Step 3 — Write the Minimal Integration
+
+Replace the contents of `Program.cs` with:
+
+```csharp
+using GitHub.Copilot.SDK;
+
+// 1. Create the client (auto-starts the Copilot CLI process)
+await using var client = new CopilotClient();
+
+// 2. Open a session
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ Model = "gpt-4.1",
+ OnPermissionRequest = PermissionHandler.ApproveAll
+});
+
+// 3. Subscribe to events
+var done = new TaskCompletionSource();
+
+session.On(evt =>
+{
+ switch (evt)
+ {
+ case AssistantMessageEvent msg:
+ Console.WriteLine($"Copilot: {msg.Data.Content}");
+ break;
+ case SessionIdleEvent:
+ done.SetResult();
+ break;
+ case SessionErrorEvent err:
+ Console.Error.WriteLine($"Error: {err.Data.Message}");
+ done.TrySetResult();
+ break;
+ }
+});
+
+// 4. Send a message and wait for the response
+Console.Write("You: ");
+var prompt = Console.ReadLine() ?? "Hello!";
+
+await session.SendAsync(new MessageOptions { Prompt = prompt });
+await done.Task;
+```
+
+### Run it
+
+```bash
+dotnet run
+```
+
+You will be prompted for input and receive a Copilot response in the terminal.
+
+---
+
+## Step 4 — Interactive Chat Loop
+
+Extend the example into a multi-turn conversation:
+
+```csharp
+using GitHub.Copilot.SDK;
+
+await using var client = new CopilotClient();
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ Model = "gpt-4.1",
+ OnPermissionRequest = PermissionHandler.ApproveAll
+});
+
+Console.WriteLine("Chat with Copilot — press Ctrl+C to exit\n");
+
+while (true)
+{
+ Console.Write("You: ");
+ var input = Console.ReadLine()?.Trim();
+ if (string.IsNullOrEmpty(input)) continue;
+
+ // SendAndWaitAsync blocks until the session becomes idle
+ var reply = await session.SendAndWaitAsync(new MessageOptions { Prompt = input });
+ Console.WriteLine($"\nCopilot: {reply?.Data.Content}\n");
+}
+```
+
+---
+
+## Step 5 — Enable Streaming Responses
+
+Receive partial chunks as the model generates them:
+
+```csharp
+using GitHub.Copilot.SDK;
+
+await using var client = new CopilotClient();
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ Model = "gpt-4.1",
+ Streaming = true,
+ OnPermissionRequest = PermissionHandler.ApproveAll
+});
+
+var done = new TaskCompletionSource();
+
+session.On(evt =>
+{
+ switch (evt)
+ {
+ case AssistantMessageDeltaEvent delta:
+ // Print each chunk as it arrives (no newline yet)
+ Console.Write(delta.Data.DeltaContent);
+ break;
+ case AssistantReasoningDeltaEvent reasoning:
+ Console.ForegroundColor = ConsoleColor.DarkGray;
+ Console.Write(reasoning.Data.DeltaContent);
+ Console.ResetColor();
+ break;
+ case AssistantMessageEvent:
+ // Full message received — newline after the stream
+ Console.WriteLine();
+ break;
+ case SessionIdleEvent:
+ done.SetResult();
+ break;
+ }
+});
+
+Console.Write("You: ");
+var prompt = Console.ReadLine() ?? "Explain async/await in C#";
+
+await session.SendAsync(new MessageOptions { Prompt = prompt });
+await done.Task;
+```
+
+---
+
+## Step 6 — Add Custom Tools
+
+Expose your own C# methods to the model using `AIFunctionFactory` (from the `Microsoft.Extensions.AI` package, already included transitively):
+
+```csharp
+using GitHub.Copilot.SDK;
+using Microsoft.Extensions.AI;
+using System.ComponentModel;
+
+// Simulated data source
+static async Task GetOrderStatus(string orderId)
+{
+ await Task.Delay(50); // simulate async I/O
+ return orderId switch
+ {
+ "ORD-001" => "Shipped — arriving tomorrow",
+ "ORD-002" => "Processing",
+ _ => "Order not found"
+ };
+}
+
+await using var client = new CopilotClient();
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ Model = "gpt-4.1",
+ Tools =
+ [
+ AIFunctionFactory.Create(
+ async ([Description("Order identifier, e.g. ORD-001")] string orderId) =>
+ await GetOrderStatus(orderId),
+ "get_order_status",
+ "Retrieve the current status of a customer order")
+ ],
+ OnPermissionRequest = PermissionHandler.ApproveAll
+});
+
+var done = new TaskCompletionSource();
+session.On(evt =>
+{
+ switch (evt)
+ {
+ case ToolExecutionStartEvent tool:
+ Console.ForegroundColor = ConsoleColor.Cyan;
+ Console.WriteLine($" [calling tool: {tool.Data.ToolName}]");
+ Console.ResetColor();
+ break;
+ case AssistantMessageEvent msg:
+ Console.WriteLine($"Copilot: {msg.Data.Content}");
+ break;
+ case SessionIdleEvent:
+ done.SetResult();
+ break;
+ }
+});
+
+await session.SendAsync(new MessageOptions
+{
+ Prompt = "What is the status of order ORD-001 and ORD-002?"
+});
+await done.Task;
+```
+
+---
+
+## Step 7 — Attach File Context
+
+Send files alongside your prompt so the model can read, analyse, or edit them:
+
+```csharp
+await session.SendAsync(new MessageOptions
+{
+ Prompt = "Review this file for potential bugs and suggest fixes.",
+ Attachments =
+ [
+ new UserMessageDataAttachmentsItem
+ {
+ Type = UserMessageDataAttachmentsItemType.File,
+ Path = "/absolute/path/to/MyService.cs",
+ DisplayName = "MyService.cs"
+ }
+ ]
+});
+```
+
+---
+
+## Step 8 — Session Hooks
+
+Intercept events to add logging, validation, or policy enforcement:
+
+```csharp
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ Model = "gpt-4.1",
+ OnPermissionRequest = PermissionHandler.ApproveAll,
+ Hooks = new SessionHooks
+ {
+ // Inspect (and optionally block) every tool call
+ OnPreToolUse = async (input, _) =>
+ {
+ Console.WriteLine($"[pre-tool] {input.ToolName}");
+ return new PreToolUseHookOutput { PermissionDecision = "allow" };
+ },
+
+ // Post-process tool results
+ OnPostToolUse = async (input, _) =>
+ {
+ Console.WriteLine($"[post-tool] {input.ToolName} finished");
+ return new PostToolUseHookOutput();
+ },
+
+ // Modify or log user prompts before they reach the model
+ OnUserPromptSubmitted = async (input, _) =>
+ {
+ Console.WriteLine($"[prompt] {input.Prompt}");
+ return new UserPromptSubmittedHookOutput { ModifiedPrompt = input.Prompt };
+ }
+ }
+});
+```
+
+---
+
+## Step 9 — Persist Sessions (Infinite Sessions)
+
+By default sessions persist their workspace to `~/.copilot/session-state/{sessionId}/`. You can resume them later:
+
+```csharp
+// --- First run: create and use a session ---
+await using var client = new CopilotClient();
+
+string sessionId;
+
+{
+ await using var session = await client.CreateSessionAsync(new SessionConfig
+ {
+ Model = "gpt-4.1",
+ OnPermissionRequest = PermissionHandler.ApproveAll
+ });
+
+ sessionId = session.SessionId;
+ Console.WriteLine($"Session ID: {sessionId}");
+
+ var reply = await session.SendAndWaitAsync(new MessageOptions
+ {
+ Prompt = "Remember: the project is called 'Orion'"
+ });
+ Console.WriteLine(reply?.Data.Content);
+} // session disposed — data stays on disk
+
+// --- Later (or next app run): resume the same session ---
+{
+ await using var resumed = await client.ResumeSessionAsync(sessionId);
+
+ var reply = await resumed.SendAndWaitAsync(new MessageOptions
+ {
+ Prompt = "What was the project name I told you earlier?"
+ });
+ Console.WriteLine(reply?.Data.Content);
+}
+```
+
+---
+
+## Step 10 — User Input Requests (ask_user)
+
+Let the model ask the user for clarification:
+
+```csharp
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ Model = "gpt-4.1",
+ OnPermissionRequest = PermissionHandler.ApproveAll,
+ OnUserInputRequest = async (request, _) =>
+ {
+ Console.WriteLine($"\nCopilot asks: {request.Question}");
+
+ if (request.Choices?.Count > 0)
+ {
+ Console.WriteLine("Options: " + string.Join(" / ", request.Choices));
+ }
+
+ Console.Write("Your answer: ");
+ var answer = Console.ReadLine() ?? string.Empty;
+
+ return new UserInputResponse
+ {
+ Answer = answer,
+ WasFreeform = true
+ };
+ }
+});
+```
+
+---
+
+## Error Handling
+
+```csharp
+try
+{
+ await using var client = new CopilotClient();
+ await using var session = await client.CreateSessionAsync();
+
+ var reply = await session.SendAndWaitAsync(new MessageOptions
+ {
+ Prompt = "Hello"
+ });
+ Console.WriteLine(reply?.Data.Content);
+}
+catch (IOException ex)
+{
+ // CLI process communication failure
+ Console.Error.WriteLine($"Communication error: {ex.Message}");
+}
+catch (InvalidOperationException ex)
+{
+ // SDK misuse (e.g. session already disposed)
+ Console.Error.WriteLine($"Usage error: {ex.Message}");
+}
+catch (Exception ex)
+{
+ Console.Error.WriteLine($"Unexpected error: {ex.Message}");
+}
+```
+
+---
+
+## IDE Setup Tips
+
+### Visual Studio 2022
+
+- Install the **GitHub Copilot** extension (built-in from 17.10+) for inline suggestions while writing SDK code.
+- Use **F12** to navigate to SDK source or generated types.
+- Use **Quick Actions (Ctrl+.)** to auto-import `GitHub.Copilot.SDK` namespaces.
+- Enable **nullable reference types** in your `.csproj` (`enable`) — the SDK ships with full nullability annotations.
+
+### VS Code with C# Dev Kit
+
+1. Install extensions:
+ - **C# Dev Kit** (`ms-dotnettools.csdevkit`)
+ - **GitHub Copilot** (`GitHub.copilot`)
+2. Open the project folder: `code .`
+3. Restore packages: `dotnet restore` (or the Dev Kit does this automatically).
+4. Run/debug with **F5** — select **C#: Launch** profile.
+5. Use `Ctrl+Space` for IntelliSense on SDK types.
+
+### Rider (JetBrains)
+
+1. Open the `.csproj` or solution file.
+2. NuGet packages are restored automatically.
+3. Use **Alt+Enter** → **Import namespace** to add `using GitHub.Copilot.SDK;`.
+4. Attach the debugger with **Shift+F9**.
+
+---
+
+## Common Patterns
+
+### Pattern: Fire-and-Collect
+
+Collect all assistant messages after a single send:
+
+```csharp
+var messages = new List();
+var done = new TaskCompletionSource();
+
+session.On(evt =>
+{
+ if (evt is AssistantMessageEvent msg)
+ messages.Add(msg.Data.Content);
+ else if (evt is SessionIdleEvent)
+ done.SetResult();
+});
+
+await session.SendAsync(new MessageOptions { Prompt = "List five SOLID principles" });
+await done.Task;
+
+foreach (var m in messages)
+ Console.WriteLine(m);
+```
+
+### Pattern: Cancellation
+
+Abort a long-running response:
+
+```csharp
+var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+cts.Token.Register(async () =>
+{
+ await session.AbortAsync();
+ Console.WriteLine("Request aborted.");
+});
+
+await session.SendAsync(new MessageOptions { Prompt = "Write me a 10 000-word essay" });
+```
+
+### Pattern: Multiple Parallel Sessions
+
+```csharp
+await using var client = new CopilotClient();
+
+var sessionA = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-4.1" });
+var sessionB = await client.CreateSessionAsync(new SessionConfig { Model = "claude-sonnet-4.5" });
+
+// Run both concurrently
+var taskA = sessionA.SendAndWaitAsync(new MessageOptions { Prompt = "What is 2 + 2?" });
+var taskB = sessionB.SendAndWaitAsync(new MessageOptions { Prompt = "What is 3 + 3?" });
+
+var results = await Task.WhenAll(taskA, taskB);
+Console.WriteLine($"GPT: {results[0]?.Data.Content}");
+Console.WriteLine($"Claude: {results[1]?.Data.Content}");
+```
+
+---
+
+## Authentication Options
+
+### Default (signed-in CLI user)
+
+No extra configuration needed — the SDK uses the credentials stored by `copilot auth login`.
+
+### GitHub Token via environment variable
+
+```bash
+export COPILOT_GITHUB_TOKEN=ghp_...
+dotnet run
+```
+
+### GitHub Token in code
+
+```csharp
+var client = new CopilotClient(new CopilotClientOptions
+{
+ GitHubToken = Environment.GetEnvironmentVariable("MY_TOKEN")
+});
+```
+
+### BYOK (Bring Your Own Key)
+
+```csharp
+var session = await client.CreateSessionAsync(new SessionConfig
+{
+ Provider = new ProviderConfig
+ {
+ Type = "openai",
+ BaseUrl = "https://api.openai.com/v1",
+ ApiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
+ }
+});
+```
+
+See the [Authentication Guide](../auth/index.md) and [BYOK Guide](./setup/byok.md) for more details.
+
+---
+
+## Full Project Example
+
+A ready-to-run project structure:
+
+```
+MyCopilotApp/
+├── MyCopilotApp.csproj
+├── Program.cs
+└── Services/
+ └── WeatherService.cs
+```
+
+**`MyCopilotApp.csproj`**
+
+```xml
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+```
+
+**`Services/WeatherService.cs`**
+
+```csharp
+namespace MyCopilotApp.Services;
+
+public static class WeatherService
+{
+ private static readonly Dictionary _data = new()
+ {
+ ["Kyiv"] = "12°C, partly cloudy",
+ ["London"] = "8°C, rainy",
+ ["Tokyo"] = "22°C, sunny",
+ };
+
+ public static Task GetWeatherAsync(string city) =>
+ Task.FromResult(_data.TryGetValue(city, out var w) ? w : "No data available");
+}
+```
+
+**`Program.cs`**
+
+```csharp
+using GitHub.Copilot.SDK;
+using Microsoft.Extensions.AI;
+using MyCopilotApp.Services;
+using System.ComponentModel;
+
+await using var client = new CopilotClient();
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ Model = "gpt-4.1",
+ Streaming = true,
+ OnPermissionRequest = PermissionHandler.ApproveAll,
+ Tools =
+ [
+ AIFunctionFactory.Create(
+ async ([Description("City name")] string city) =>
+ await WeatherService.GetWeatherAsync(city),
+ "get_weather",
+ "Get current weather for a city")
+ ]
+});
+
+Console.WriteLine("Weather Assistant — Ctrl+C to exit\n");
+
+while (true)
+{
+ Console.Write("You: ");
+ var input = Console.ReadLine()?.Trim();
+ if (string.IsNullOrEmpty(input)) continue;
+
+ Console.Write("Copilot: ");
+ var done = new TaskCompletionSource();
+
+ using var _ = session.On(evt =>
+ {
+ switch (evt)
+ {
+ case AssistantMessageDeltaEvent delta:
+ Console.Write(delta.Data.DeltaContent);
+ break;
+ case AssistantMessageEvent:
+ Console.WriteLine();
+ break;
+ case ToolExecutionStartEvent tool:
+ Console.ForegroundColor = ConsoleColor.Cyan;
+ Console.Write($"[{tool.Data.ToolName}] ");
+ Console.ResetColor();
+ break;
+ case SessionIdleEvent:
+ done.TrySetResult();
+ break;
+ }
+ });
+
+ await session.SendAsync(new MessageOptions { Prompt = input });
+ await done.Task;
+ Console.WriteLine();
+}
+```
+
+---
+
+## What to Read Next
+
+- **[.NET API Reference](../../dotnet/README.md)** — Full API surface, all options and overloads
+- **[Session Persistence](./session-persistence.md)** — Long-running and resumable sessions
+- **[Custom Agents](./custom-agents.md)** — Define specialized sub-agents
+- **[Authentication Guide](../auth/index.md)** — Auth options for production deployments
+- **[Setup Guides](./setup/index.md)** — Deployment patterns (local, backend, bundled)
diff --git a/docs/guides/setup/index.md b/docs/guides/setup/index.md
index 2613fe29d..7b809f0cf 100644
--- a/docs/guides/setup/index.md
+++ b/docs/guides/setup/index.md
@@ -89,6 +89,7 @@ Use this table to find the right guides based on what you need to do:
| Azure BYOK with Managed Identity (no API keys) | [Azure Managed Identity](./azure-managed-identity.md) |
| Run the SDK on a server | [Backend Services](./backend-services.md) |
| Serve multiple users / scale horizontally | [Scaling & Multi-Tenancy](./scaling.md) |
+| Build a C# / .NET application end-to-end | [Practical .NET Integration Guide](../dotnet-integration.md) |
## Configuration Comparison
@@ -138,6 +139,8 @@ All guides assume you have:
If you're brand new, start with the **[Getting Started tutorial](../../getting-started.md)** first, then come back here for production configuration.
+If you are building a C# / .NET application, see the **[Practical .NET Integration Guide](../dotnet-integration.md)** for step-by-step instructions covering project creation, NuGet setup, streaming, tools, hooks, and IDE tips.
+
## Next Steps
Pick the guide that matches your situation from the [decision matrix](#decision-matrix) above, or start with the persona description closest to your role.