Dotnet-skills dotnet-mcp
Build or consume Model Context Protocol (MCP) servers and clients in .NET using the official MCP C# SDK, including stdio, Streamable HTTP, tools, prompts, resources, and capability negotiation.
install
source · Clone the upstream repo
git clone https://github.com/managedcode/dotnet-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/managedcode/dotnet-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/catalog/Libraries/MCP/skills/dotnet-mcp" ~/.claude/skills/managedcode-dotnet-skills-dotnet-mcp && rm -rf "$T"
manifest:
catalog/Libraries/MCP/skills/dotnet-mcp/SKILL.mdsource content
MCP C# SDK for .NET
Trigger On
- building or consuming MCP servers from a .NET application or library
- choosing between stdio and HTTP transport for MCP
- exposing tools, resources, prompts, completions, or logging to an MCP host
- connecting a .NET app to an existing MCP server and passing discovered tools into
IChatClient - bootstrapping a minimal MCP client/server from the
quickstarts or publishing a server to the MCP Registry.NET AI - implementing capability-aware flows such as roots, sampling, elicitation, subscriptions, or session resumption
Use This Skill Instead Of
- Use
when protocol interoperability is the requirement.dotnet-mcp - Use
when you only need model/provider abstraction or local tool orchestration without the MCP wire protocol.dotnet-microsoft-extensions-ai - Use
when the main problem is agent orchestration; combine it withdotnet-microsoft-agent-framework
only when those agents must consume or expose MCP endpoints.dotnet-mcp - Use the
quickstarts for the very first vertical slice, then come back here to harden transport, capability negotiation, publishing, and host interoperability..NET AI
Documentation
- MCP C# SDK overview
- Getting Started
- API reference
- Conceptual docs
- Versioning policy
- Experimental APIs
- MCP C# SDK repository
- Model Context Protocol specification
References
Load only what the task needs:
- current server/client patterns, transports, capabilities, filters, and chat-client integrationreferences/patterns.md
- safe error handling, auth boundaries, stdio logging hygiene, and defensive tool/resource patternsreferences/security.md
Package Selection
| Package | Choose when |
|---|---|
| You only need a client or low-level server APIs and want the smallest dependency set. |
| You want the main SDK package with hosting, DI, attribute discovery, and stdio server support. Start here for most projects. |
| You are hosting a remote MCP server in ASP.NET Core over HTTP. This includes the main package. |
Transport Selection
| Transport | Use when | Notes |
|---|---|---|
/ | The MCP server should run as a local child process. | Best for local tooling and editor/agent integrations. |
+ | The server is remote or should be reachable over HTTP. | Recommended HTTP transport; supports streaming and session resumption. |
| You must connect to an older SSE-only server. | Legacy compatibility only; do not choose this for new servers. |
flowchart LR A["Need MCP interoperability in .NET"] --> B{"Role?"} B -->|"Expose MCP surface"| C{"Where will it run?"} B -->|"Consume an MCP server"| D{"Transport?"} C -->|"Local child process"| E["ModelContextProtocol\nAddMcpServer()\nWithStdioServerTransport()"] C -->|"Remote HTTP endpoint"| F["ModelContextProtocol.AspNetCore\nAddMcpServer()\nWithHttpTransport()\nMapMcp()"] D -->|"stdio"| G["StdioClientTransport\nMcpClient.CreateAsync()"] D -->|"HTTP"| H["HttpClientTransport\nAutoDetect or StreamableHttp"] E --> I["Register tools/resources/prompts"] F --> I G --> J["Check ServerCapabilities\nbefore optional features"] H --> J
Workflow
-
Pick the package and transport first.
- Local child-process server:
+ModelContextProtocol
.WithStdioServerTransport() - Remote server:
+ModelContextProtocol.AspNetCore
+WithHttpTransport()
.MapMcp() - Client-only app: start with
orModelContextProtocol
.ModelContextProtocol.Core - Registry distribution: pair a minimal server with the MCP Registry publishing flow only after the server contract is stable.
- Local child-process server:
-
Model the MCP surface explicitly.
- Tools:
+[McpServerToolType][McpServerTool] - Resources:
+[McpServerResourceType][McpServerResource] - Prompts:
+[McpServerPromptType][McpServerPrompt] - Use custom handlers or filters only for cross-cutting behavior, protocol extensions, or advanced routing.
- Tools:
-
Prefer attribute discovery for straightforward servers.
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using ModelContextProtocol.Server; using System.ComponentModel; var builder = Host.CreateApplicationBuilder(args); builder.Logging.AddConsole(options => { options.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}"; }
- For HTTP servers, use the ASP.NET Core transport and map the endpoint directly.
using ModelContextProtocol.Server; using System.ComponentModel; var builder = WebApplication.CreateBuilder(args); builder.Services .AddMcpServer() .WithHttpTransport() .WithToolsFromAssembly(); var app = builder.Build(); app.MapMcp("/mcp"); app.Run(); [McpServerToolType] public static class EchoTool { [McpServerTool, Description("Echoes the message back to the client.")] public static string Echo(string message) => $"hello {message}"; }
- When consuming a server, use
and stay capability-aware.McpClient.CreateAsync(...)
using ModelContextProtocol.Client; using ModelContextProtocol.Protocol; var transport = new StdioClientTransport(new StdioClientTransportOptions { Name = "Everything", Command = "npx", Arguments = ["-y", "@modelcontextprotocol/server-everything"], }); await using var client = await McpClient.CreateAsync(transport); IList<McpClientTool> tools = await client.ListToolsAsync(); if (client.ServerCapabilities.Prompts is not null) { var prompts = await client.ListPromptsAsync(); }
-
Treat optional features as negotiated capabilities, not assumptions.
- Client capabilities: configure
for roots, sampling, and elicitation.McpClientOptions.Capabilities - Server capabilities are inferred from registered features.
- Check
before using completions, logging, prompt list-change notifications, or resource subscriptions.client.ServerCapabilities - Use
orclient.NegotiatedProtocolVersion
only when version-specific behavior matters.server.NegotiatedProtocolVersion
- Client capabilities: configure
-
Keep HTTP guidance current.
- Streamable HTTP is the recommended transport for remote servers.
also serves SSE compatibility endpoints for older clients.MapMcp()- HTTP clients can use
by default, or forceAutoDetect
/StreamableHttp
.Sse - Session resumption is available for Streamable HTTP through
.McpClient.ResumeSessionAsync(...)
-
Treat the
MCP quickstarts as bootstrap examples..NET AI
andbuild-mcp-client
are good starting points when the surrounding app is still MEAI-centric.build-mcp-server
is the distribution step, not the design step. Stabilize the protocol surface before publishing.publish-mcp-registry
-
Respect current error and serialization rules.
- Tool exceptions normally come back as
.CallToolResult.IsError == true - Throw
only for protocol-level JSON-RPC failures.McpProtocolException
inherits fromMcpClientTool
, so discovered tools can be passed directly intoAIFunction
.IChatClient- Experimental APIs use
diagnostics; suppress them intentionally, not globally by accident.MCPEXP... - If you use a custom
, prependJsonSerializerContext
so MCP protocol types keep the SDK's contract.McpJsonUtilities.DefaultOptions.TypeInfoResolver
- Tool exceptions normally come back as
Anti-Patterns To Avoid
| Anti-pattern | Why it causes trouble | Better approach |
|---|---|---|
| Picking HTTP transport for a purely local child-process scenario | Adds unnecessary hosting, auth, and deployment surface | Use stdio for local/editor-hosted integrations |
| Treating SSE as the default remote transport | Locks new work to legacy behavior | Prefer Streamable HTTP and keep SSE only for backward compatibility |
Writing tools without metadata | Hosts and models lose schema clarity | Describe tool purpose and parameters explicitly |
| Returning huge binary/text payloads from every tool call | Bloats context and slows hosts | Return focused content and move large data to resources |
| Logging to stdout on stdio servers | Corrupts the protocol stream | Send logs to stderr |
| Assuming prompts/resources/logging/completions exist | Breaks against partial implementations | Check negotiated capabilities first |
| Using filters for normal business logic | Makes handlers opaque and hard to reason about | Keep filters for cross-cutting policy, audit, or protocol plumbing |
Deliver
- a correctly packaged MCP server or client that matches the deployment topology
- explicit tool/resource/prompt definitions with descriptions and bounded payloads
- capability-aware handling for optional MCP features
- validation notes for transport, auth boundary, and host/client interoperability
Validate
- chosen package matches the topology:
,Core
, orModelContextProtocolAspNetCore - stdio servers do not write logs or diagnostics to stdout
- HTTP servers use
and are tested at the final route, for exampleMapMcp()/mcp - tools, resources, and prompts use current
attributes or documented handler/filter alternatives[McpServer*] - client code checks
before using subscriptions, completions, logging, or prompt/resource list-change flowsServerCapabilities - Streamable HTTP is the default for new remote servers; SSE is used only for legacy compatibility
- experimental APIs and custom serialization settings are reviewed intentionally rather than copied blindly