Skip to content

MCP C# SDK v1.0

Key Points

  • ModelContextProtocol NuGet 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.AI so MCP tools become AIFunction automatically.

Setup

<PackageReference Include="ModelContextProtocol" Version="1.*" />

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.

Cross-references