mcp
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
.NET AIquickstarts or publishing a server to the MCP Registry - implementing capability-aware flows such as roots, sampling, elicitation, subscriptions, or session resumption
Use This Skill Instead Of
- Use
mcpwhen protocol interoperability is the requirement. - Use
microsoft-extensions-aiwhen you only need model/provider abstraction or local tool orchestration without the MCP wire protocol. - Use
microsoft-agent-frameworkwhen the main problem is agent orchestration; combine it withmcponly when those agents must consume or expose MCP endpoints. - Use the
.NET AIquickstarts for the very first vertical slice, then come back here to harden transport, capability negotiation, publishing, and host interoperability.
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:
references/patterns.md- current server/client patterns, transports, capabilities, filters, and chat-client integrationreferences/security.md- safe error handling, auth boundaries, stdio logging hygiene, and defensive tool/resource patterns
Package Selection
| Package | Choose when |
|---|---|
ModelContextProtocol.Core |
You only need a client or low-level server APIs and want the smallest dependency set. |
ModelContextProtocol |
You want the main SDK package with hosting, DI, attribute discovery, and stdio server support. Start here for most projects. |
ModelContextProtocol.AspNetCore |
You are hosting a remote MCP server in ASP.NET Core over HTTP. This includes the main package. |
Transport Selection
| Transport | Use when | Notes |
|---|---|---|
StdioClientTransport / WithStdioServerTransport() |
The MCP server should run as a local child process. | Best for local tooling and editor/agent integrations. |
HttpClientTransport + HttpTransportMode.StreamableHttp |
The server is remote or should be reachable over HTTP. | Recommended HTTP transport; supports streaming and session resumption. |
HttpTransportMode.Sse |
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
ModelContextProtocolorModelContextProtocol.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
McpClient.CreateAsync(...)and stay capability-aware.
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
McpClientOptions.Capabilitiesfor roots, sampling, and elicitation. - Server capabilities are inferred from registered features.
- Check
client.ServerCapabilitiesbefore using completions, logging, prompt list-change notifications, or resource subscriptions. - Use
client.NegotiatedProtocolVersionorserver.NegotiatedProtocolVersiononly when version-specific behavior matters.
- Client capabilities: configure
-
Keep HTTP guidance current.
- Streamable HTTP is the recommended transport for remote servers.
MapMcp()also serves SSE compatibility endpoints for older clients.- HTTP clients can use
AutoDetectby default, or forceStreamableHttp/Sse. - Session resumption is available for Streamable HTTP through
McpClient.ResumeSessionAsync(...).
-
Treat the
.NET AIMCP quickstarts as bootstrap examples.build-mcp-clientandbuild-mcp-serverare good starting points when the surrounding app is still MEAI-centric.publish-mcp-registryis the distribution step, not the design step. Stabilize the protocol surface before publishing.
-
Respect current error and serialization rules.
- Tool exceptions normally come back as
CallToolResult.IsError == true. - Throw
McpProtocolExceptiononly for protocol-level JSON-RPC failures. McpClientToolinherits fromAIFunction, so discovered tools can be passed directly intoIChatClient.- Experimental APIs use
MCPEXP...diagnostics; suppress them intentionally, not globally by accident. - If you use a custom
JsonSerializerContext, prependMcpJsonUtilities.DefaultOptions.TypeInfoResolverso MCP protocol types keep the SDK's contract.
- 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 [Description] 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,ModelContextProtocol, orAspNetCore - stdio servers do not write logs or diagnostics to stdout
- HTTP servers use
MapMcp()and are tested at the final route, for example/mcp - tools, resources, and prompts use current
[McpServer*]attributes or documented handler/filter alternatives - client code checks
ServerCapabilitiesbefore using subscriptions, completions, logging, or prompt/resource list-change flows - 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
More from managedcode/dotnet-skills
dotnet
Primary router skill for broad .NET work. Classify the repo by app model and cross-cutting concern first, then switch to the narrowest matching .NET skill instead of staying at a generic layer.
18dotnet-aspnet-core
Build, debug, modernize, or review ASP.NET Core applications with correct hosting, middleware, security, configuration, logging, and deployment patterns on current .NET.
13dotnet-entity-framework-core
Design, tune, or review EF Core data access with proper modeling, migrations, query translation, performance, and lifetime management for modern .NET applications.
12dotnet-code-review
Review .NET changes for bugs, regressions, architectural drift, missing tests, incorrect async or disposal behavior, and platform-specific pitfalls before you approve or merge them.
11dotnet-architecture
Design or review .NET solution architecture across modular monoliths, clean architecture, vertical slices, microservices, DDD, CQRS, and cloud-native boundaries without over-engineering.
11dotnet-signalr
Implement or review SignalR hubs, streaming, reconnection, transport, and real-time delivery patterns in ASP.NET Core applications.
10