|
| 1 | +--- |
| 2 | +name: dotnet-mcp-builder |
| 3 | +description: 'Build Model Context Protocol (MCP) servers in C#/.NET against the current ModelContextProtocol 1.x NuGet packages. Especially helps with cases the model often gets wrong without guidance — stale preview versions (it tends to pick 0.3 or 0.4 preview), MCP Apps (interactive UI rendered in the host), elicitation URL mode, per-session HTTP wiring, OAuth and reverse-proxy deploy specifics, and debugging concrete MapMcp / STDIO / Streamable-HTTP errors. Also covers the routine work — STDIO and Streamable HTTP transports (SSE is deprecated), tools, prompts, resources, sampling, roots, completions, logging — and a basic .NET MCP client. Trigger when the user says or implies any .NET MCP server work: ModelContextProtocol, McpServerTool, MapMcp, WithStdioServerTransport, "MCP server in C#", "MCP tool in dotnet", "expose this as MCP", or names a primitive (prompt/resource/elicitation/MCP App) in a .NET context. Skip for MCP work in other languages.' |
| 4 | +--- |
| 5 | + |
| 6 | +# Building MCP servers in .NET |
| 7 | + |
| 8 | +This skill helps you write production-quality MCP servers and basic clients in C#/.NET against the **official** [`ModelContextProtocol`](https://www.nuget.org/profiles/ModelContextProtocol) NuGet packages, maintained by Microsoft and the MCP project. It targets the **stable 1.x** line and the current spec (2025-11-25). |
| 9 | + |
| 10 | +## When this skill earns its keep |
| 11 | + |
| 12 | +The .NET MCP SDK had years of preview packages (`0.x-preview`) before reaching `1.0`. Without help, the model tends to: |
| 13 | +- Pin a stale preview version that won't compile against current samples. |
| 14 | +- Miss recent spec features (elicitation URL mode, MCP Apps, structured content blocks). |
| 15 | +- Get HTTP transport details wrong (stateful/stateless, proxy buffering, OAuth wiring). |
| 16 | +- Forget the STDIO stdout/stderr trap. |
| 17 | + |
| 18 | +If the task is one of those, *load the matching reference* and follow it. If it's truly trivial (e.g. "rename this tool method"), you don't need to read everything — the cardinal rules below are the minimum. |
| 19 | + |
| 20 | +## Mental model in 30 seconds |
| 21 | + |
| 22 | +A .NET MCP server is an ordinary `Microsoft.Extensions.Hosting` (or `WebApplication`) app that wires an MCP server through DI: |
| 23 | + |
| 24 | +```csharp |
| 25 | +builder.Services |
| 26 | + .AddMcpServer() |
| 27 | + .WithStdioServerTransport() // OR .WithHttpTransport(...) |
| 28 | + .WithToolsFromAssembly() // discover [McpServerToolType] classes |
| 29 | + .WithPrompts<MyPrompts>() // optional |
| 30 | + .WithResources<MyResources>(); // optional |
| 31 | +``` |
| 32 | + |
| 33 | +Primitives are plain C# methods on classes marked with attributes (`[McpServerToolType]` + `[McpServerTool]`, `[McpServerPromptType]` + `[McpServerPrompt]`, `[McpServerResourceType]` + `[McpServerResource]`). Parameters bind from JSON-RPC; the SDK builds the JSON Schema from the signature plus `[Description]` attributes. |
| 34 | + |
| 35 | +Server-to-client features (sampling, elicitation, roots, log/progress notifications) are methods on the injected `IMcpServer`. |
| 36 | + |
| 37 | +## Decision tree → which references to load |
| 38 | + |
| 39 | +Always load `references/packages.md` if you're creating a new project or unsure of the current package version. |
| 40 | + |
| 41 | +| Task | Load | |
| 42 | +|---|---| |
| 43 | +| New STDIO server | `references/transport-stdio.md` | |
| 44 | +| New HTTP (Streamable) server | `references/transport-http.md` | |
| 45 | +| Add/modify a tool | `references/tool-primitive.md` | |
| 46 | +| Add/modify a prompt | `references/prompt-primitive.md` | |
| 47 | +| Add/modify a resource | `references/resource-primitive.md` | |
| 48 | +| Ask the user a question mid-tool | `references/elicitation.md` | |
| 49 | +| Call the client's LLM from a tool | `references/sampling.md` | |
| 50 | +| Read the user's project roots | `references/roots.md` | |
| 51 | +| Return an interactive UI | `references/mcp-apps.md` | |
| 52 | +| Argument completions, log/progress notifications, filters, server instructions | `references/server-features.md` | |
| 53 | +| Write a .NET program that **consumes** an MCP server | `references/client.md` | |
| 54 | +| MCP Inspector, in-memory tests, mocks, CI | `references/testing.md` | |
| 55 | + |
| 56 | +For multi-primitive tasks, load several at once. For trivial edits in an existing file, you usually don't need any. |
| 57 | + |
| 58 | +## Cardinal rules (apply always; these prevent the highest-frequency breakages) |
| 59 | + |
| 60 | +1. **Pin the current stable package, not a preview.** Use `ModelContextProtocol` / `ModelContextProtocol.AspNetCore` / `ModelContextProtocol.Core` at the latest **1.x**. If you find yourself writing `0.3-preview` or `0.4-preview`, stop and check NuGet — preview APIs have breaking differences. |
| 61 | +2. **STDIO servers must not write to stdout.** Stdout is the JSON-RPC channel. Configure `LogToStandardErrorThreshold = LogLevel.Trace` before anything else and never `Console.WriteLine` from a tool. |
| 62 | +3. **HTTP defaults to stateful.** For horizontally-scaled deployments without server-initiated traffic, set `options.Stateless = true`. Server-to-client features (sampling, elicitation, roots, unsolicited notifications) require stateful HTTP **or** STDIO — `Stateless = true` will break them at runtime. |
| 63 | +4. **SSE-only is deprecated.** Use Streamable HTTP. Only enable legacy SSE (`EnableLegacySse = true`) for an old client you must support, and call it out. |
| 64 | +5. **Always `[Description]` tools and parameters.** This is what the LLM sees when picking and shaping calls. Vague descriptions are the #1 reason tools don't get used. |
| 65 | +6. **Show the registration line every time you add a primitive.** A new `[McpServerPromptType]` class without `.WithPrompts<...>()` (or `.WithPromptsFromAssembly()`) is invisible. |
| 66 | +7. **Don't invent APIs.** If you're unsure a method exists, say so and check the [API reference](https://csharp.sdk.modelcontextprotocol.io/api/ModelContextProtocol.html) — wrong method names cause silent failures. |
| 67 | + |
| 68 | +## Working style |
| 69 | + |
| 70 | +- **Make minimal, additive changes.** Add a method to the existing tool class rather than restructuring the project. |
| 71 | +- **For non-trivial setups, run `dotnet build`.** Catches missing usings, attribute typos, and TFM mismatches before the user sees them. |
| 72 | +- **Confirm transport + .NET version + primitives before scaffolding** if context doesn't already make them obvious. Default to **.NET 10** for new projects. |
| 73 | + |
| 74 | +## When the user is stuck |
| 75 | + |
| 76 | +Walk this checklist before guessing: |
| 77 | +1. **STDIO:** something is writing to stdout (logger sink, `Console.WriteLine`, library banner). |
| 78 | +2. **HTTP 404:** path mismatch — `app.MapMcp()` is root, `app.MapMcp("/mcp")` puts it under `/mcp`. |
| 79 | +3. **Tool not appearing:** missing `[McpServerToolType]` on the class, or no `.WithToolsFromAssembly()` / `.WithTools<T>()` registered. |
| 80 | +4. **Args not bound:** parameter names must match the JSON-RPC `arguments` keys; complex types bind via `System.Text.Json`. |
| 81 | +5. **Sampling/elicitation/roots failing:** transport is stateless HTTP, or the client doesn't advertise the capability. |
| 82 | + |
| 83 | +Still stuck? Point the user at the [`EverythingServer`](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/EverythingServer) sample — it exercises every feature. |
0 commit comments