MCP C# SDK v1.0
Key Points
ModelContextProtocolNuGet package — Microsoft official C# SDK; v1.0 March 2026.- Supports MCP client and server modes.
- Multiple transports: stdio, HTTP/SSE, named pipes.
- Strong typing; reflection-based registration; OTel integration.
- Aligns with
Microsoft.Extensions.AIso MCP tools becomeAIFunctionautomatically.
Setup
Client connection
using ModelContextProtocol.Client;
// Stdio: spawn server as child process
var transport = new StdioClientTransport(new StdioClientTransportOptions
{
Command = "npx",
Arguments = ["-y", "@modelcontextprotocol/server-filesystem", "/safe/path"]
});
var client = await McpClient.ConnectAsync(transport);
// HTTP/SSE
var sseTransport = new SseClientTransport(new() { Endpoint = new Uri("https://mcp.contoso.com") });
var sseClient = await McpClient.ConnectAsync(sseTransport);
Discovering capabilities
var serverInfo = client.ServerInfo;
Console.WriteLine($"{serverInfo.Name} v{serverInfo.Version}");
var tools = await client.ListToolsAsync();
var resources = await client.ListResourcesAsync();
var prompts = await client.ListPromptsAsync();
Calling a tool
var result = await client.CallToolAsync("read_file", new Dictionary<string, object?>
{
["path"] = "/safe/path/data.txt"
});
foreach (var content in result.Content)
{
if (content is TextContent t) Console.WriteLine(t.Text);
}
As AIFunction
var tools = await client.ListToolsAsync();
var aiFunctions = tools.Select(t => t.AsAIFunction()).ToArray();
// Use in agent
var agent = new ChatClientAgent(chat) { Tools = aiFunctions };
Tool calls flow through automatically.
Reading a resource
var resource = await client.ReadResourceAsync(new Uri("file:///workspace/README.md"));
foreach (var c in resource.Contents) Console.WriteLine(c.Text);
Server side
using ModelContextProtocol.Server;
var server = new McpServer(new McpServerOptions
{
Name = "MyServer",
Version = "1.0.0",
Capabilities = new() { Tools = new() }
});
server.RegisterTool(new()
{
Name = "get_user",
Description = "Get user by ID",
InputSchema = JsonSchema.FromType<GetUserArgs>(),
Handler = async (args, ct) =>
{
var id = args.GetValue<int>("id");
var user = await _userService.GetAsync(id, ct);
return new ToolResult { Content = [new TextContent(JsonSerializer.Serialize(user))] };
}
});
await server.RunAsync(new StdioServerTransport());
Annotation-based registration
[McpServerToolType]
public class WeatherTools
{
[McpServerTool, Description("Get weather for a city")]
public async Task<Weather> GetWeather([Description("City")] string city)
=> await _api.GetAsync(city);
}
server.RegisterToolsFromType<WeatherTools>(serviceProvider);
Reflection auto-builds schema; method invocation handled.
Resources
server.RegisterResource(new()
{
UriTemplate = "user://{id}",
Handler = async (args, ct) =>
{
var id = int.Parse(args["id"]!);
var user = await _service.GetAsync(id, ct);
return new ResourceContents { /* ... */ };
}
});
Prompts
server.RegisterPrompt(new()
{
Name = "summarize",
Arguments = [new() { Name = "text", Required = true }],
Handler = async (args, ct) =>
{
var text = (string)args["text"]!;
return new GetPromptResult
{
Messages = [new(ChatRole.System, "Summarize concisely:"), new(ChatRole.User, text)]
};
}
});
Hosting MCP server in ASP.NET Core
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
var app = builder.Build();
await app.RunMcpServerAsync();
OpenTelemetry
SDK emits OTel spans for client calls and server invocations. Standard correlation across distributed traces.
DI integration
builder.Services.AddSingleton(sp =>
{
var transport = new StdioClientTransport(...);
return McpClient.ConnectAsync(transport).GetAwaiter().GetResult();
});
builder.Services.AddSingleton<IChatClient>(sp =>
{
var mcp = sp.GetRequiredService<IMcpClient>();
var chat = ...;
return chat.AsBuilder().UseFunctionInvocation().Build();
});
Senior considerations
- Don't trust untrusted servers: their tool descriptions reach the LLM.
- Bound tool ops: filesystem MCP server with whitelisted directories.
- Rate limiting: MCP server can DoS your agent.
- Versioning: pin SDK + server versions.