Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Create MCP servers using the C# SDK and .NET project templates. Covers scaffolding, tool/prompt/resource implementation, and transport configuration for stdio and HTTP. USE FOR: creating new MCP server projects, scaffolding with dotnet new mcpserver, adding MCP tools/prompts/resources, choosing stdio vs HTTP transport, configuring MCP hosting in Program.cs, setting up ASP.NET Core MCP endpoints with MapMcp. DO NOT USE FOR: debugging or running existing servers (use mcp-csharp-debug), writing tes
.claude/skills/managedcode-mcp-csharp-create/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 27% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 84% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 49% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 43% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 105% | 0% |
Create Model Context Protocol servers using the official C# SDK (ModelContextProtocol NuGet package) and the dotnet new mcpserver project template. Servers expose tools, prompts, and resources that LLMs can discover and invoke via the MCP protocol.
--transport local) and HTTP (--transport remote) transportmcp-csharp-debugmcp-csharp-testmcp-csharp-publish| Input | Required | Description | |-------|----------|-------------| | Transport type | Yes | stdio (local/CLI) or http (remote/web). Ask user if not specified — default to stdio | | Project name | Yes | PascalCase name for the project (e.g., WeatherMcpServer) | | .NET SDK version | Recommended | .NET 10.0+ required. Check with dotnet --version | | Service/API to wrap | Recommended | External API or service the tools will interact with |
> Commit strategy: Commit after completing each step so scaffolding and implementation are separately reviewable.
dotnet --version (install from https://dotnet.microsoft.com if < 10.0)bash dotnet new list mcpserver If "No templates found" → install: dotnet new install Microsoft.McpServer.ProjectTemplates
| Choose stdio if… | Choose HTTP if… | |----------------------|---------------------| | Local CLI tool or IDE plugin | Cloud/web service deployment | | Single user at a time | Multiple simultaneous clients | | Running as subprocess (VS Code, GitHub Copilot) | Cross-network access needed | | Simpler setup, no network config | Containerized deployment (Docker/Azure) |
Default: stdio — simpler, works for most local development. Users can add HTTP later.
stdio server:
bashdotnet new mcpserver -n <ProjectName>
If the template times out or is unavailable, use dotnet new console -n <ProjectName> and add dotnet add package ModelContextProtocol.
HTTP server:
bashdotnet new web -n <ProjectName> cd <ProjectName> dotnet add package ModelContextProtocol.AspNetCore
This is the recommended approach — faster and more reliable than the template. The template also supports HTTP via dotnet new mcpserver -n <ProjectName> --transport remote, but dotnet new web gives you more control over the project structure.
Template flags reference: --transport local (stdio, default), --transport remote (ASP.NET Core HTTP), --aot, --self-contained.
Tools are the primary way MCP servers expose functionality. Add a class with [McpServerToolType] and methods with [McpServerTool]:
csharpusing ModelContextProtocol.Server; using System.ComponentModel; [McpServerToolType] public static class MyTools { [McpServerTool, Description("Brief description of what the tool does.")] public static async Task<string> DoSomething( [Description("What this parameter controls")] string input, CancellationToken cancellationToken = default) { // Implementation return $"Result: {input}"; } }
Critical rules:
[Description] attribute — LLMs use this to decide when to call the tool[Description] attributeCancellationToken in all async tools[McpServerTool(Name = "custom_name")] only if the default method name is unclearDI injection patterns — the SDK supports two styles:
csharp[McpServerToolType] public class ApiTools(HttpClient httpClient, ILogger<ApiTools> logger) { [McpServerTool, Description("Fetch a resource by ID.")] public async Task<string> FetchResource( [Description("Resource identifier")] string id, CancellationToken cancellationToken = default) { logger.LogInformation("Fetching {Id}", id); return await httpClient.GetStringAsync($"/api/{id}", cancellationToken); } }
Register services in Program.cs:
csharpvar builder = Host.CreateApplicationBuilder(args); builder.Logging.AddConsole(options => options.LogToStandardErrorThreshold = LogLevel.Trace); builder.Services.AddHttpClient(); // registers IHttpClientFactory + HttpClient // ILogger<T> is registered by default — no extra setup needed. builder.Services.AddMcpServer() .WithStdioServerTransport() .WithToolsFromAssembly(); // discovers non-static [McpServerToolType] classes await builder.Build().RunAsync();
For the full attribute reference, return types, DI injection, and builder API patterns, see references/api-patterns.md.
Prompts — reusable LLM interaction templates:
csharp[McpServerPromptType] public static class MyPrompts { [McpServerPrompt, Description("Summarize content into one sentence.")] public static ChatMessage Summarize( [Description("Content to summarize")] string content) => new(ChatRole.User, $"Summarize this into one sentence: {content}"); }
Resources — data the LLM can read:
csharp[McpServerResourceType] public static class MyResources { [McpServerResource(UriTemplate = "config://app", Name = "App Config", MimeType = "application/json"), Description("Application configuration")] public static string GetConfig() => JsonSerializer.Serialize(AppConfig.Current); }
stdio transport:
csharpusing Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using ModelContextProtocol.Server; var builder = Host.CreateApplicationBuilder(args); builder.Logging.AddConsole(options => options.LogToStandardErrorThreshold = LogLevel.Trace); // CRITICAL: stderr only builder.Services.AddMcpServer() .WithStdioServerTransport() .WithToolsFromAssembly(); await builder.Build().RunAsync();
HTTP transport:
csharpusing ModelContextProtocol.Server; var builder = WebApplication.CreateBuilder(args); builder.Services.AddMcpServer() .WithHttpTransport() .WithToolsFromAssembly(); // Register services your tools need via DI // builder.Services.AddHttpClient(); // builder.Services.AddSingleton<IMyService, MyService>(); var app = builder.Build(); app.MapMcp(); // exposes MCP endpoint at /mcp (Streamable HTTP) app.MapGet("/health", () => "ok"); // health check for container orchestrators app.Run();
Key HTTP details: MapMcp() defaults to /mcp path. For containers, set ASPNETCORE_URLS=http://+:8080 and EXPOSE 8080. The MCP HTTP protocol uses Streamable HTTP — no special client config needed beyond the URL.
For transport configuration details (stateless mode, auth, path prefix, HttpContextAccessor), see references/transport-config.md.
bashcd <ProjectName> dotnet build dotnet run
For stdio: the process starts and waits for JSON-RPC input on stdin. For HTTP: the server listens on the configured port.
dotnet build)[McpServerToolType] attribute[McpServerTool] and [Description] attributes[Description] attributesapp.MapMcp() is called in Program.csdotnet run| Pitfall | Solution | |---------|----------| | stdio server outputs garbage or hangs | Logging to stdout corrupts JSON-RPC protocol. Set LogToStandardErrorThreshold = LogLevel.Trace | | Tool not discovered by LLM clients | Missing [McpServerToolType] on the class or [McpServerTool] on the method. Verify .WithToolsFromAssembly() in Program.cs | | LLM doesn't understand when to use a tool | Add clear [Description] attributes on both the method and all parameters | | WithToolsFromAssembly() fails in AOT | Reflection-based discovery is incompatible with Native AOT. Use .WithTools<MyTools>() instead | | Parameters not appearing in tool schema | CancellationToken, IMcpServer, and DI services are injected automatically — they do not appear in the schema. Only parameters with [Description] are exposed | | HTTP server returns 404 | app.MapMcp() must be called. Check the request path matches the configured route |
mcp-csharp-debug — Run, debug, and test with MCP Inspectormcp-csharp-test — Unit tests, integration tests, evaluationsmcp-csharp-publish — NuGet, Docker, Azure deploymentHttpContextAccessor, OpenTelemetry observability. Load when: configuring advanced transport options or authentication.Other measured skills in the registry, with their headline benchmark lift.