From dotnet-ai
Creates C# MCP servers using .NET SDK and templates. Scaffolds projects with dotnet new mcpserver, implements tools/prompts/resources, configures stdio/HTTP transport.
npx claudepluginhub dotnet/skills --plugin dotnet-aiThis skill uses the workspace's default tool permissions.
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.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
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.
Confirm .NET 10+ SDK: dotnet --version (install from https://dotnet.microsoft.com if < 10.0)
Check if the MCP server template is already installed:
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:
dotnet 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:
dotnet 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]:
using 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:
Method parameter injection (static class): DI services appear as method parameters. The SDK resolves them automatically — they do not appear in the tool schema.
Constructor injection (non-static class): Use when tools need shared state or multiple services:
[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:
var 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:
[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:
[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:
using 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:
using 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.
cd <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.