Codesapien.McpTransportGateway
1.0.0
dotnet tool install --global Codesapien.McpTransportGateway --version 1.0.0
dotnet new tool-manifest
dotnet tool install --local Codesapien.McpTransportGateway --version 1.0.0
#tool dotnet:?package=Codesapien.McpTransportGateway&version=1.0.0
nuke :add-package Codesapien.McpTransportGateway --version 1.0.0
MCP Transport Gateway
A pluggable C# MCP transport gateway.
The project composes an incoming listener transport with an outgoing connector transport through a shared MCP message bridge. The current implementation supports stdio, streamable HTTP, and SSE.
Current status
The gateway is implemented as a minimal ASP.NET Core host with transport adapters and end-to-end tests.
Validated transport permutations:
streamable HTTP -> stdioSSE -> stdiostdio -> streamable HTTPstdio -> SSE
The bridge architecture is composition-based, so additional pairings are possible as more adapters are added or hardened.
Project layout
src\McpTransportGateway- gateway host, transport adapters, configuration, and bridge runtimetests\McpTransportGateway.Tests- integration tests for the initial transport matrixtests\McpTransportGateway.TestServer- a small stdio MCP echo server used by tests and local examplesMcpTransportGateway.slnx- solution entry point
Architecture overview
The runtime is split into a few core concepts:
ITransportListener- accepts inbound MCP sessionsITransportConnector- creates outbound MCP sessionsTransportRegistry- resolves the configured listener and connectorTransportBridge- relays JSON-RPC messages between the two transportsAcceptedSession- wraps a live inbound session and its lifetime
Current adapters:
- Incoming
StreamableHttp- Hosts
POST,GET, andDELETEon a configurable MCP path - Maintains in-memory session state keyed by
Mcp-Session-Id
- Hosts
- Incoming
Sse- Hosts a configurable SSE subscription endpoint and message endpoint
- Tracks sessions in memory by query-string
sessionId
- Incoming
Stdio- Accepts MCP messages on the gateway process's own stdin/stdout
- Outgoing
Stdio- Launches a child MCP server process per downstream session
- Outgoing
StreamableHttp- Connects to a remote MCP server over streamable HTTP
- Outgoing
Sse- Connects to a remote MCP server over SSE
The bridge also tracks per-request related transports so streamable HTTP responses are returned on the correct response stream.
Requirements
- .NET SDK 10.0 or newer
- Windows, Linux, or macOS with a working
dotnetCLI - If you use outgoing
stdio, the target MCP server command must be available on the machine running the gateway
Build and test
Build everything:
dotnet build McpTransportGateway.slnx
Run the test suite:
dotnet test McpTransportGateway.slnx
Package as a .NET tool
The gateway project is configured to pack as a .NET tool package with:
- package ID:
McpTransportGateway - tool command:
mcpgateway
Create the tool package:
dotnet pack src\McpTransportGateway\McpTransportGateway.csproj -c Release
By default, the package is written to:
artifacts\packages\
Test-install the package from the local output folder:
dotnet tool install --tool-path .\artifacts\tool-install --add-source .\artifacts\packages McpTransportGateway
.\artifacts\tool-install\mcpgateway --help
After publishing the package to NuGet.org or another feed, users can install it globally with:
dotnet tool install --global McpTransportGateway
Then run it as:
mcpgateway --help
To publish a packed version to a NuGet feed:
dotnet nuget push .\artifacts\packages\McpTransportGateway.<version>.nupkg --source <feed-url> --api-key <key>
How configuration works
The gateway reads configuration from the standard ASP.NET Core sources, including:
appsettings.jsonappsettings.{Environment}.json- environment variables
- command-line arguments
The gateway options live under the Gateway section.
A development example is checked in at src\McpTransportGateway\appsettings.Development.json.
For other environments or real deployments, supply your own Gateway section through configuration.
In addition to raw ASP.NET Core configuration keys, the gateway now supports a friendly CLI layer for common scenarios such as:
--stdio "dotnet my-server.dll"to expose a local stdio server--sse https://example.com/sseto connect to a remote SSE server--streamableHttp https://example.com/mcpto connect to a remote streamable HTTP server
Friendly CLI flags override appsettings, environment variables, and raw --Gateway:... command-line keys.
Friendly CLI arguments
Show the built-in help:
mcpgateway --help
Supported shorthand flags:
--stdio "<command line>"- launch a local stdio MCP server and expose it over HTTP transports--sse <url>- connect to a remote SSE MCP server and expose it through stdio--streamableHttp <url>- connect to a remote streamable HTTP MCP server and expose it through stdio--outputTransport <stdio|sse|streamableHttp>- selects the opposite side of the bridge for shorthand modes
Important default:
--stdiodefaults to--outputTransport streamableHttp
Additional supported flags:
--port <number>- binds the HTTP host tohttp://127.0.0.1:<port>--ssePath <path>- hosted SSE subscription path--messagePath <path>- hosted SSE message path--streamableHttpPath <path>- hosted streamable HTTP path--header "Name: Value"- repeatable header for outgoing remote HTTP/SSE connectors--oauth2Bearer <token>- setsAuthorization: Bearer <token>for outgoing remote HTTP/SSE connectors--connectionTimeout <timespan-or-ms>- outgoing remote HTTP/SSE connect timeout--workingDirectory <path>- working directory for outgoing stdio child processes--env NAME=VALUE- repeatable environment variable for outgoing stdio child processes--shutdownTimeout <timespan-or-ms>- outgoing stdio shutdown timeout
For advanced or less common cases, raw ASP.NET Core command-line configuration still works, for example:
mcpgateway --Gateway:Incoming:Kind StreamableHttp --Gateway:Outgoing:Kind Stdio --Gateway:Outgoing:Command dotnet
Configuration reference
Gateway:Incoming
| Setting | Required | Description |
|---|---|---|
Kind |
Yes | One of Stdio, StreamableHttp, or Sse |
Path |
Required for StreamableHttp |
Route used for streamable HTTP, default /mcp |
SsePath |
Required for Sse |
SSE subscribe route, default /sse |
MessagePath |
Required for Sse |
POST route used to send client messages, default /message |
Rules:
Path,SsePath, andMessagePathmust start with/SsePathandMessagePathmust be different whenIncoming.Kind = Sse
Gateway:Outgoing
| Setting | Required | Description |
|---|---|---|
Kind |
Yes | One of Stdio, StreamableHttp, or Sse |
Endpoint |
Required for StreamableHttp and Sse |
Remote MCP endpoint URL |
Command |
Required for Stdio |
Command used to launch the child MCP server |
Arguments |
Optional | Command arguments for outgoing stdio |
WorkingDirectory |
Optional | Working directory for outgoing stdio |
EnvironmentVariables |
Optional | Extra environment variables for outgoing stdio |
Headers |
Optional | Extra HTTP headers for outgoing StreamableHttp or Sse |
ConnectionTimeout |
Optional | Remote HTTP/SSE connect timeout, default 00:00:30 |
ShutdownTimeout |
Optional | Child-process shutdown timeout for outgoing stdio, default 00:00:05 |
Rules:
ConnectionTimeoutmust be greater than zeroShutdownTimeoutmust be greater than zeroCommandis required whenOutgoing.Kind = StdioEndpointis required whenOutgoing.Kind = StreamableHttporOutgoing.Kind = Sse
TransportKind values
Supported values are:
StdioStreamableHttpSse
HTTP endpoints
The host always exposes:
GET /healthz- returns{ "status": "ok" }
When Incoming.Kind = StreamableHttp:
POST {Gateway:Incoming:Path}- send JSON-RPC requests/notificationsGET {Gateway:Incoming:Path}- open the server-to-client stream for a sessionDELETE {Gateway:Incoming:Path}- close a session
When Incoming.Kind = Sse:
GET {Gateway:Incoming:SsePath}- open the SSE subscriptionPOST {Gateway:Incoming:MessagePath}?sessionId=...- send JSON-RPC messages into that session
When Incoming.Kind = Stdio:
- the gateway accepts MCP messages on its own stdin/stdout
- the ASP.NET Core host still exists because the executable is currently hosted in the web stack
Example configuration
Example 1: streamable HTTP → stdio
This is the closest equivalent to exposing a local stdio server over a network transport.
Add this to src\McpTransportGateway\appsettings.Development.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Gateway": {
"Incoming": {
"Kind": "StreamableHttp",
"Path": "/mcp"
},
"Outgoing": {
"Kind": "Stdio",
"Command": "dotnet",
"Arguments": [
"tests\\McpTransportGateway.TestServer\\bin\\Debug\\net10.0\\McpTransportGateway.TestServer.dll"
],
"WorkingDirectory": "C:\\Users\\facaval\\src\\mcpfe",
"ShutdownTimeout": "00:00:05"
}
}
}
The example above assumes the gateway process working directory is the repository root and the test server has already been built. Relative command arguments are resolved from the gateway process working directory, not from the location of the JSON file. If you prefer, use an absolute path for the
.dll.
Run it:
$env:ASPNETCORE_ENVIRONMENT = "Development"
dotnet build McpTransportGateway.slnx
dotnet run --project src\McpTransportGateway --launch-profile http
The gateway will listen on the launch-profile URL from src\McpTransportGateway\Properties\launchSettings.json, which is currently http://localhost:5246 for the http profile.
The MCP endpoint will be:
http://localhost:5246/mcp
Equivalent CLI-first example:
mcpgateway `
--stdio "dotnet tests\\McpTransportGateway.TestServer\\bin\\Debug\\net10.0\\McpTransportGateway.TestServer.dll" `
--port 8000
That command defaults to hosted streamable HTTP, so the MCP endpoint will be:
http://127.0.0.1:8000/mcp
Example 2: SSE → stdio
{
"Gateway": {
"Incoming": {
"Kind": "Sse",
"SsePath": "/sse",
"MessagePath": "/message"
},
"Outgoing": {
"Kind": "Stdio",
"Command": "dotnet",
"Arguments": [
"tests\\McpTransportGateway.TestServer\\bin\\Debug\\net10.0\\McpTransportGateway.TestServer.dll"
]
}
}
}
Subscribe at:
GET http://localhost:5246/sse
Send messages to:
POST http://localhost:5246/message?sessionId=<session-id>
Equivalent CLI-first example:
mcpgateway `
--stdio "dotnet tests\\McpTransportGateway.TestServer\\bin\\Debug\\net10.0\\McpTransportGateway.TestServer.dll" `
--outputTransport sse `
--port 8000 `
--ssePath /sse `
--messagePath /message
Example 3: stdio → remote streamable HTTP
{
"Gateway": {
"Incoming": {
"Kind": "Stdio"
},
"Outgoing": {
"Kind": "StreamableHttp",
"Endpoint": "http://localhost:3001/mcp",
"ConnectionTimeout": "00:00:30",
"Headers": {
"Authorization": "Bearer <token-if-needed>"
}
}
}
}
In this mode, an MCP client can launch the gateway over stdio and the gateway will proxy to a remote streamable HTTP MCP server.
Equivalent CLI-first example:
mcpgateway `
--streamableHttp "http://localhost:3001/mcp" `
--oauth2Bearer "<token-if-needed>"
Example 4: stdio → remote SSE
{
"Gateway": {
"Incoming": {
"Kind": "Stdio"
},
"Outgoing": {
"Kind": "Sse",
"Endpoint": "http://localhost:3001/sse",
"ConnectionTimeout": "00:00:30",
"Headers": {
"Authorization": "Bearer <token-if-needed>"
}
}
}
}
Equivalent CLI-first example:
mcpgateway `
--sse "http://localhost:3001/sse" `
--oauth2Bearer "<token-if-needed>"
Environment variable equivalents
You can configure the same values with environment variables.
Example for streamable HTTP -> stdio in PowerShell:
$env:ASPNETCORE_ENVIRONMENT = "Development"
$env:Gateway__Incoming__Kind = "StreamableHttp"
$env:Gateway__Incoming__Path = "/mcp"
$env:Gateway__Outgoing__Kind = "Stdio"
$env:Gateway__Outgoing__Command = "dotnet"
$env:Gateway__Outgoing__Arguments__0 = "tests\\McpTransportGateway.TestServer\\bin\\Debug\\net10.0\\McpTransportGateway.TestServer.dll"
$env:Gateway__Outgoing__WorkingDirectory = "C:\\Users\\facaval\\src\\mcpfe"
dotnet build McpTransportGateway.slnx
dotnet run --project src\McpTransportGateway --launch-profile http
Useful patterns:
Gateway__Outgoing__Arguments__0,Gateway__Outgoing__Arguments__1, ...Gateway__Outgoing__Headers__AuthorizationGateway__Outgoing__EnvironmentVariables__MY_SETTING
Running against your own stdio MCP server
To expose any local stdio MCP server over HTTP/SSE, point Outgoing.Kind to Stdio and set:
CommandArguments- optionally
WorkingDirectory - optionally
EnvironmentVariables
Examples of possible commands:
dotnetnodenpxpythonuvx
Because the outgoing stdio connector creates a new child process per session, each downstream client gets isolated server state.
Local development notes
- The host currently uses ASP.NET Core even when the active incoming transport is
Stdio - Session state for hosted
StreamableHttpandSselisteners is in memory - No authentication or authorization layer has been added yet
- No WebSocket transport has been implemented yet
Validation performed
The repository currently has end-to-end tests for:
StreamableHttp -> StdioSse -> StdioStdio -> StreamableHttpStdio -> Sse
Run them with:
dotnet test McpTransportGateway.slnx
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|