mirror of
https://github.com/github/awesome-copilot.git
synced 2026-05-15 11:11:48 +00:00
2c275f2ef9
* feat(skills): add dotnet-mcp-builder, deprecate csharp-mcp-server-generator Adds a comprehensive skill for building MCP (Model Context Protocol) servers in C#/.NET against the official ModelContextProtocol 1.x NuGet packages. Covers both transports (STDIO, Streamable HTTP — SSE is deprecated) and every primitive in the current MCP spec (2025-11-25): tools, prompts, resources, elicitation (form + URL mode), sampling, roots, completions, logging, and MCP Apps. Includes a thin .NET MCP client reference and testing guidance (MCP Inspector + in-memory transport for unit tests). Steers the model toward the current stable 1.x packages instead of the 0.x previews it tends to pin by default, and enforces the STDIO stdout/stderr trap. Also deprecates the existing csharp-mcp-server-generator skill, which predates ModelContextProtocol 1.0 and only covered a subset of the current spec. Its SKILL.md now redirects users to dotnet-mcp-builder so existing install URLs keep working without surprises. * fix: address PR review from aaronpowell - Delete csharp-mcp-server-generator skill (rather than deprecating it) - Update mcp-apps.md pitfalls section to reference .NET Tool.Meta type instead of the serialized _meta JSON property names - Rebuild docs/README.skills.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: remove C# MCP development plugin files * chore: remove csharp-mcp-development plugin entry from marketplace --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
163 lines
4.9 KiB
Markdown
163 lines
4.9 KiB
Markdown
# STDIO transport
|
|
|
|
STDIO is the right choice when the server runs as a child process of the client (Claude Desktop, VS Code, MCP Inspector, a custom CLI). The client launches your executable; you read JSON-RPC frames from stdin and write them to stdout.
|
|
|
|
## When to choose STDIO
|
|
|
|
- Local-first server (file-system access, dev tools, CLI integrations).
|
|
- Distributing as a single executable or a `dnx`-runnable NuGet package.
|
|
- You want the simplest possible deployment story (no network, no auth).
|
|
- You need server-to-client features (sampling, elicitation, roots) — STDIO always supports them, no `Stateless` flag to worry about.
|
|
|
|
If the user wants a remote/multi-tenant server, use [HTTP Streamable](./transport-http.md) instead.
|
|
|
|
## Minimal server
|
|
|
|
```bash
|
|
dotnet new console -n MyStdioServer -f net10.0
|
|
cd MyStdioServer
|
|
dotnet add package ModelContextProtocol
|
|
dotnet add package Microsoft.Extensions.Hosting
|
|
```
|
|
|
|
```csharp
|
|
// Program.cs
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Logging;
|
|
using ModelContextProtocol.Server;
|
|
using System.ComponentModel;
|
|
|
|
var builder = Host.CreateApplicationBuilder(args);
|
|
|
|
// CRITICAL: stdout is the JSON-RPC channel. Send all logs to stderr.
|
|
builder.Logging.AddConsole(o => o.LogToStandardErrorThreshold = LogLevel.Trace);
|
|
|
|
builder.Services
|
|
.AddMcpServer()
|
|
.WithStdioServerTransport()
|
|
.WithToolsFromAssembly();
|
|
|
|
await builder.Build().RunAsync();
|
|
|
|
[McpServerToolType]
|
|
public static class EchoTool
|
|
{
|
|
[McpServerTool, Description("Echoes the message back to the client.")]
|
|
public static string Echo(string message) => $"hello {message}";
|
|
}
|
|
```
|
|
|
|
## The stdout/stderr trap
|
|
|
|
The single most common bug in STDIO servers is something writing to stdout that isn't a JSON-RPC frame. The client will then drop the connection with a parse error.
|
|
|
|
**Things that silently break STDIO:**
|
|
- `Console.WriteLine(...)` anywhere in your code.
|
|
- A logger configured with the default console sink (writes to stdout).
|
|
- `Trace.WriteLine(...)` if a default trace listener is attached.
|
|
- Third-party libraries that print banners on startup.
|
|
|
|
**Defensive checklist:**
|
|
1. Configure logging to stderr **before** anything else (the snippet above does this).
|
|
2. Don't `Console.Write*` from tools or startup code. Use `ILogger` injected into the tool class.
|
|
3. If a dependency is noisy, redirect its logs through `ILogger` or suppress them at startup.
|
|
|
|
## Server identity
|
|
|
|
The SDK sends `serverInfo` (name + version) in the `initialize` response. By default it derives them from your assembly. To override:
|
|
|
|
```csharp
|
|
builder.Services
|
|
.AddMcpServer(options =>
|
|
{
|
|
options.ServerInfo = new()
|
|
{
|
|
Name = "my-stdio-server",
|
|
Version = "1.0.0",
|
|
Title = "My STDIO MCP Server" // optional human-readable name
|
|
};
|
|
})
|
|
.WithStdioServerTransport()
|
|
.WithToolsFromAssembly();
|
|
```
|
|
|
|
## Reading args/env from the client
|
|
|
|
Clients (e.g. Claude Desktop config) typically launch your server with arguments and environment variables. Read them like any other .NET app:
|
|
|
|
```csharp
|
|
string apiKey = Environment.GetEnvironmentVariable("MY_API_KEY")
|
|
?? throw new InvalidOperationException("MY_API_KEY not set");
|
|
|
|
string configPath = args.ElementAtOrDefault(0)
|
|
?? Path.Combine(Environment.CurrentDirectory, "config.json");
|
|
```
|
|
|
|
Document the expected vars/args in the README so users know what to put in their client config.
|
|
|
|
## Wiring to Claude Desktop
|
|
|
|
In `claude_desktop_config.json`:
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"my-server": {
|
|
"command": "dotnet",
|
|
"args": ["run", "--project", "C:/path/to/MyStdioServer"],
|
|
"env": {
|
|
"MY_API_KEY": "..."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
For a published self-contained executable, replace `command`/`args` with the executable path. For a NuGet-distributed server using `dnx`:
|
|
|
|
```json
|
|
"command": "dnx",
|
|
"args": ["MyMcpServer", "--version", "1.2.3"]
|
|
```
|
|
|
|
## Wiring to VS Code (GitHub Copilot Chat)
|
|
|
|
In `.vscode/mcp.json`:
|
|
|
|
```json
|
|
{
|
|
"servers": {
|
|
"my-server": {
|
|
"type": "stdio",
|
|
"command": "dotnet",
|
|
"args": ["run", "--project", "${workspaceFolder}/src/MyMcpServer"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Local debugging
|
|
|
|
The cleanest workflow is [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
|
|
|
|
```bash
|
|
npx @modelcontextprotocol/inspector dotnet run --project ./MyStdioServer
|
|
```
|
|
|
|
Inspector launches your server, opens a UI, and lets you call tools / list resources / fire elicitations interactively. See [`testing.md`](./testing.md) for more.
|
|
|
|
## Graceful shutdown
|
|
|
|
`builder.Build().RunAsync()` already handles SIGINT/SIGTERM. If you have background work to flush, use `IHostApplicationLifetime`:
|
|
|
|
```csharp
|
|
var host = builder.Build();
|
|
var lifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
|
|
lifetime.ApplicationStopping.Register(() =>
|
|
{
|
|
// flush, close handles, etc. — keep it fast (<5s) so the client doesn't hang.
|
|
});
|
|
await host.RunAsync();
|
|
```
|