modelcontextprotocol / modelcontextprotocol/csharp-sdk
Sample: stdio-to-HTTP bridge for AI clients that only support stdio transport
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 4.5k
- Forks
- 814
- Avg merge
- 9d 19h
- Merged PRs (30d)
- 4
Description
Is your feature request related to a problem?
Many AI clients (Claude Desktop, Cursor, VS Code, etc.) only support stdio MCP servers configured as local processes. When the actual MCP server runs over Streamable HTTP (e.g. a remote or self-hosted server with API key authentication), users need a lightweight bridge process to proxy messages between the two transports.
Today there is no sample showing this pattern, so developers end up implementing HTTP manually (handling mcp-session-id, SSE parsing, notifications, error mapping, etc.) without knowing the SDK already provides everything needed.
Describe the solution you'd like
Add a samples/StdioToHttpBridge sample showing how to proxy messages transparently between StdioServerTransport and HttpClientTransport using the raw ITransport layer:
using System.CommandLine;
using ModelContextProtocol.Client;
using ModelContextProtocol.Server;
var urlOption = new Option<string>("--url", ["-u"])
{
Description = "MCP server endpoint URL. Env: MCP_URL",
DefaultValueFactory = (_) => Environment.GetEnvironmentVariable("MCP_URL") ?? string.Empty
};
var apiKeyOption = new Option<string>("--api-key", ["-k"])
{
Description = "API key for authentication. Env: MCP_API_KEY",
DefaultValueFactory = (_) => Environment.GetEnvironmentVariable("MCP_API_KEY") ?? string.Empty
};
var insecureOption = new Option<bool>("--insecure", ["-i"])
{
Description = "Disable SSL certificate validation. DANGEROUS: use only in development. Env: MCP_INSECURE",
DefaultValueFactory = (_) => Environment.GetEnvironmentVariable("MCP_INSECURE") is "1" or "true"
};
var rootCommand = new RootCommand("stdio-to-HTTP MCP bridge") { urlOption, apiKeyOption, insecureOption };
rootCommand.SetAction(async action =>
{
var mcpUrl = action.GetValue(urlOption);
var apiKey = action.GetValue(apiKeyOption);
var insecure = action.GetValue(insecureOption);
if (string.IsNullOrWhiteSpace(mcpUrl) || string.IsNullOrWhiteSpace(apiKey))
{
if (string.IsNullOrWhiteSpace(mcpUrl)) Console.Error.WriteLine("Error: --url is required.");
if (string.IsNullOrWhiteSpace(apiKey)) Console.Error.WriteLine("Error: --api-key is required.");
Environment.Exit(1);
}
if (insecure) Console.Error.WriteLine("WARNING: SSL validation is DISABLED");
var handler = insecure
? new HttpClientHandler { ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator }
: new HttpClientHandler();
var httpTransport = new HttpClientTransport(
new HttpClientTransportOptions
{
Endpoint = new Uri(mcpUrl!),
AdditionalHeaders = new Dictionary<string, string> { ["X-API-Key"] = apiKey! },
TransportMode = HttpTransportMode.StreamableHttp
},
new HttpClient(handler));
await using var remoteTransport = await httpTransport.ConnectAsync();
await using var stdioTransport = new StdioServerTransport("mcp-bridge");
// Proxy stdio → remote
var stdioToRemote = Task.Run(async () =>
{
await foreach (var message in stdioTransport.MessageReader.ReadAllAsync())
await remoteTransport.SendMessageAsync(message);
});
// Proxy remote → stdio
var remoteToStdio = Task.Run(async () =>
{
await foreach (var message in remoteTransport.MessageReader.ReadAllAsync())
await stdioTransport.SendMessageAsync(message);
});
await Task.WhenAny(stdioToRemote, remoteToStdio);
});
return await rootCommand.Parse(args).InvokeAsync();
Why this approach
- Transparent proxy: works at raw
ITransportlevel — all JSON-RPC messages (includinginitialize/initialized) are forwarded as-is, no double handshake - SDK handles complexity:
HttpClientTransportmanagesmcp-session-id, Streamable HTTP protocol, reconnections automatically - Authentication via
AdditionalHeaders: clean way to inject API keys or bearer tokens - Environment variable support:
MCP_URL,MCP_API_KEY,MCP_INSECUREfor container/CI use - Distributable: can be published as
PublishSingleFile=trueself-contained executable
Additional context
This is the pattern we use in cv4pve-admin to let Claude Desktop connect to our self-hosted Proxmox VE MCP server over HTTPS with API key authentication.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the proposed samples/StdioToHttpBridge entry point and the raw ITransport usage described in the issue. Run it against an HTTP MCP endpoint and a stdio client, checking bidirectional message forwarding, API-key headers, environment variables, and the insecure warning. Done means the sample transparently connects both transports without implementing protocol handling itself.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, cli
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100