| name | mcp-csharp-create |
| description | Use when creating or extending a C# MCP server with tools, prompts, resources, stateless HTTP, MRTR, Apps, Tasks, AOT, or Microsoft.Extensions.AI. |
| license | MIT |
| version | 0.2.0 |
C# MCP Server Creation
Derived from dotnet/skills under MIT; substantially updated for ModelContextProtocol 2.0.0 and MCP 2026-07-28. See ../../THIRD-PARTY-NOTICES.md.
Packages
Install core first, then add extension packages only when the server uses them:
dotnet add package ModelContextProtocol --version 2.0.0
dotnet add package ModelContextProtocol.Extensions.Apps --version 2.0.0
dotnet add package ModelContextProtocol.Extensions.Tasks --version 2.0.0
Apps and Tasks are not included in the core ModelContextProtocol package.
Start with a transport
- Stdio:
AddMcpServer().WithStdioServerTransport(); write protocol data only to stdout and logs to stderr.
- New HTTP:
AddMcpServer().WithHttpTransport(options => options.Stateless = true) then app.MapMcp().
- Do not use
sessionIdGenerator, a session map, or Mcp-Session-Id in a new endpoint. HTTP requests carry per-request metadata.
using ModelContextProtocol.AspNetCore;
using ModelContextProtocol.Server;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
.WithHttpTransport(options => options.Stateless = true)
.WithToolsFromAssembly();
var app = builder.Build();
app.MapMcp();
app.MapGet("/health", () => Results.Ok());
app.Run();
WithHttpTransport(Action<HttpServerTransportOptions>?) configures transport services. MapMcp() is the endpoint mapper. Treat a 2026 request's _meta as request-scoped; do not retain it as session state.
Tools and header-promotion
Use [McpServerTool], [Description], and cancellation. [McpHeader] promotes a primitive tool input to Mcp-Param-{Name} for HTTP intermediaries. The input must remain statically reachable under the root object schema's properties; no object/array/union header values. The client mirrors it into Mcp-Param-{Name}; reject a mismatch instead of choosing one value.
[McpServerTool, Description("Returns inventory for a tenant.")]
public static string GetInventory(
[McpHeader, Description("Routing tenant identifier")] string tenantId,
CancellationToken cancellationToken = default) => "...";
MRTR versus Tasks
MRTR returns InputRequiredResult from an alternate-result tool filter or throws InputRequiredException from a handler. Include server-owned requestState when the retry needs integrity-bound state; the client sends inputResponses on a new invocation. Never encode a task lifecycle as MRTR.
Tasks require the separate ModelContextProtocol.Extensions.Tasks package and registration with WithTasks(IMcpTaskStore store) (optionally with task options). Task creation/update implementations forward inputResponses to the store. They are an extension capability, not a [McpServerTool] shortcut.
MCP Apps
WithMcpApps() requires the separate ModelContextProtocol.Extensions.Apps package and is gated by experimental diagnostic MCPEXP003. For a supported UI-resource convention, use McpAppUiAttribute; for raw metadata, set McpMeta / McpMetaAttribute. Existing raw ui metadata wins—McpAppUiAttribute does not modify a tool that already has a ui key. ResourceUri must use ui://.
[McpServerTool, McpAppUi(ResourceUri = "ui://inventory/picker.html")]
public static string PickInventory() => "...";
Use McpUiToolVisibility only to control which principals can invoke a tool, not as browser authorization.
MEAI bridge and AOT
The Microsoft.Extensions.AI bridge converts MCP tools to AIFunction; use it when an MEAI agent consumes MCP tools, not as a replacement for the MCP server API. WithToolsFromAssembly() relies on reflection and is not Native AOT safe. Register explicit types with WithTools<TTool>() for AOT.
API facts
McpHeaderAttribute, InputRequiredException, MetaKeys, and DiscoverResult are public. McpProtocolVersions is internal. DiscoverResult is the modern discovery result; avoid legacy initialization in modern code.
Verify
dotnet build
dotnet run
Use $mcp-conformance against the deployed HTTP endpoint and $mcp-csharp-test for behavioral coverage.