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.