Skip to content

Latest commit

 

History

History
250 lines (182 loc) · 11 KB

File metadata and controls

250 lines (182 loc) · 11 KB
title Transports
author jeffhandley
description How to configure stdio, Streamable HTTP, and SSE transports for MCP communication.
uid transports

Transports

MCP uses a transport layer to handle the communication between clients and servers. Three transport mechanisms are supported: stdio, Streamable HTTP, and SSE (Server-Sent Events, legacy).

stdio transport

The stdio transport communicates over standard input and output streams. It is best suited for local integrations, as the MCP server runs as a child process of the client.

stdio client

Use xref:ModelContextProtocol.Client.StdioClientTransport to launch a server process and communicate over its stdin/stdout. This example connects to the NuGet MCP Server:

var transport = new StdioClientTransport(new StdioClientTransportOptions
{
    Command = "dnx",
    Arguments = ["NuGet.Mcp.Server"],
    ShutdownTimeout = TimeSpan.FromSeconds(10)
});

await using var client = await McpClient.CreateAsync(transport);

Key xref:ModelContextProtocol.Client.StdioClientTransportOptions properties:

Property Description
Command The executable to launch (required)
Arguments Command-line arguments for the process
WorkingDirectory Working directory for the server process
EnvironmentVariables Environment variables (merged with current; null values remove variables)
ShutdownTimeout Graceful shutdown timeout (default: 5 seconds)
StandardErrorLines Callback for stderr output from the server process
Name Optional transport identifier for logging

stdio server

Use xref:ModelContextProtocol.Server.StdioServerTransport for servers that communicate over stdin/stdout:

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddMcpServer()
    .WithStdioServerTransport()
    .WithTools<MyTools>();

await builder.Build().RunAsync();

Streamable HTTP transport

The Streamable HTTP transport uses HTTP for bidirectional communication with optional streaming. This is the recommended transport for remote servers.

Streamable HTTP client

Use xref:ModelContextProtocol.Client.HttpClientTransport with xref:ModelContextProtocol.Client.HttpTransportMode.StreamableHttp:

var transport = new HttpClientTransport(new HttpClientTransportOptions
{
    Endpoint = new Uri("https://my-mcp-server.example.com/mcp"),
    TransportMode = HttpTransportMode.StreamableHttp,
    ConnectionTimeout = TimeSpan.FromSeconds(30),
    AdditionalHeaders = new Dictionary<string, string>
    {
        ["X-Custom-Header"] = "value"
    }
});

await using var client = await McpClient.CreateAsync(transport);

The client also supports automatic transport detection with xref:ModelContextProtocol.Client.HttpTransportMode.AutoDetect (the default), which tries Streamable HTTP first and falls back to SSE if the server does not support it:

var transport = new HttpClientTransport(new HttpClientTransportOptions
{
    Endpoint = new Uri("https://my-mcp-server.example.com/mcp"),
    // TransportMode defaults to AutoDetect
});

Resuming sessions

Streamable HTTP supports session resumption. Save the session ID, server capabilities, and server info from the original session, then use xref:ModelContextProtocol.Client.McpClient.ResumeSessionAsync* to reconnect:

var transport = new HttpClientTransport(new HttpClientTransportOptions
{
    Endpoint = new Uri("https://my-mcp-server.example.com/mcp"),
    KnownSessionId = previousSessionId
});

await using var client = await McpClient.ResumeSessionAsync(transport, new ResumeClientSessionOptions
{
    ServerCapabilities = previousServerCapabilities,
    ServerInfo = previousServerInfo
});

Streamable HTTP server (ASP.NET Core)

Use the ModelContextProtocol.AspNetCore package to host an MCP server over HTTP. The xref:Microsoft.AspNetCore.Builder.McpEndpointRouteBuilderExtensions.MapMcp* method maps the Streamable HTTP endpoint at the specified route (root by default). It also maps legacy SSE endpoints at {route}/sse and {route}/message for backward compatibility.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMcpServer()
    .WithHttpTransport(options =>
    {
        // Recommended for servers that don't need server-to-client requests.
        options.Stateless = true;
    })
    .WithTools<MyTools>();

var app = builder.Build();
app.MapMcp();
app.Run();

By default, the HTTP transport uses stateful sessions — the server assigns an Mcp-Session-Id to each client and tracks session state in memory. For most servers, stateless mode is recommended instead. It simplifies deployment, enables horizontal scaling without session affinity, and avoids issues with clients that don't send the Mcp-Session-Id header. See Sessions for a detailed guide on when to use stateless vs. stateful mode and how to configure session options.

A custom route can be specified. For example, the AspNetCoreMcpPerSessionTools sample uses a route parameter:

app.MapMcp("/mcp");

When using a custom route, Streamable HTTP clients should connect directly to that route (e.g., https://host/mcp), while SSE clients should connect to {route}/sse (e.g., https://host/mcp/sse).

SSE transport (legacy)

The SSE (Server-Sent Events) transport is a legacy mechanism that uses unidirectional server-to-client streaming with a separate HTTP endpoint for client-to-server messages. New implementations should prefer Streamable HTTP.

Note

The SSE transport is considered legacy. The Streamable HTTP transport is the recommended approach for HTTP-based communication and supports bidirectional streaming.

SSE client

Use xref:ModelContextProtocol.Client.HttpClientTransport with xref:ModelContextProtocol.Client.HttpTransportMode.Sse:

var transport = new HttpClientTransport(new HttpClientTransportOptions
{
    Endpoint = new Uri("https://my-mcp-server.example.com/sse"),
    TransportMode = HttpTransportMode.Sse,
    MaxReconnectionAttempts = 5,
    DefaultReconnectionInterval = TimeSpan.FromSeconds(1)
});

await using var client = await McpClient.CreateAsync(transport);

SSE-specific configuration options:

Property Description
MaxReconnectionAttempts Maximum number of reconnection attempts on stream disconnect (default: 5)
DefaultReconnectionInterval Wait time between reconnection attempts (default: 1 second)

SSE server (ASP.NET Core)

The ASP.NET Core integration supports SSE transport alongside Streamable HTTP. The same MapMcp() endpoint handles both protocols — clients connecting with SSE are automatically served using the legacy SSE mechanism. SSE requires stateful mode (the default); legacy SSE endpoints are not mapped when Stateless = true.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMcpServer()
    .WithHttpTransport(options =>
    {
        // SSE requires stateful mode (the default). Set explicitly for forward compatibility.
        options.Stateless = false;
    })
    .WithTools<MyTools>();

var app = builder.Build();

// MapMcp() serves both Streamable HTTP and legacy SSE.
// SSE clients connect to /sse (or {route}/sse for custom routes).
app.MapMcp();
app.Run();

No additional configuration is needed. When a client connects using the SSE protocol, the server responds with an SSE stream for server-to-client messages and accepts client-to-server messages via a separate POST endpoint.

Transport mode comparison

Feature stdio Streamable HTTP (stateless) Streamable HTTP (stateful) SSE (legacy, stateful)
Process model Child process Remote HTTP Remote HTTP Remote HTTP
Direction Bidirectional Request-response Bidirectional Server→client stream + client→server POST
Sessions Implicit (one per process) None — each request is independent Mcp-Session-Id tracked in memory Session ID via query string, tracked in memory
Server-to-client requests ✗ (see MRTR proposal)
Unsolicited notifications
Session resumption N/A N/A
Horizontal scaling N/A No constraints Requires session affinity Requires session affinity
Authentication Process-level HTTP auth (OAuth, headers) HTTP auth (OAuth, headers) HTTP auth (OAuth, headers)
Best for Local tools, IDE integrations Remote servers, production deployments Local HTTP debugging, server-to-client features Legacy client compatibility

For a detailed comparison of stateless vs. stateful mode — including deployment trade-offs, security considerations, and configuration — see Sessions.

In-memory transport

The xref:ModelContextProtocol.Server.StreamServerTransport and xref:ModelContextProtocol.Protocol.StreamClientTransport types work with any Stream, including in-memory pipes. This is useful for testing, embedding an MCP server in a larger application, or running a client and server in the same process without network overhead.

The following example creates a client and server connected via System.IO.Pipelines (from the InMemoryTransport sample):

using ModelContextProtocol.Client;
using ModelContextProtocol.Server;
using System.IO.Pipelines;

Pipe clientToServerPipe = new(), serverToClientPipe = new();

// Create a server using a stream-based transport over an in-memory pipe.
await using McpServer server = McpServer.Create(
    new StreamServerTransport(clientToServerPipe.Reader.AsStream(), serverToClientPipe.Writer.AsStream()),
    new McpServerOptions
    {
        ToolCollection = [McpServerTool.Create((string message) => $"Echo: {message}", new() { Name = "echo" })]
    });
_ = server.RunAsync();

// Connect a client using a stream-based transport over the same in-memory pipe.
await using McpClient client = await McpClient.CreateAsync(
    new StreamClientTransport(clientToServerPipe.Writer.AsStream(), serverToClientPipe.Reader.AsStream()));

// List and invoke tools.
var tools = await client.ListToolsAsync();
var echo = tools.First(t => t.Name == "echo");
Console.WriteLine(await echo.InvokeAsync(new() { ["arg"] = "Hello World" }));