The dominant format for MCP server distribution is stdio. You install a package — npx @some/mcp-server or uvx some-mcp-server — and a process starts, reads JSON-RPC on stdin, and writes responses to stdout. This model is excellent for local use. It is simple, portable, and requires no network infrastructure.
It does not work for production. A production MCP deployment needs TLS, concurrent connections from multiple clients, authentication, health checks, and crash recovery. A stdio process gives you none of those things.
The question is: how do you take the hundreds of existing stdio MCP servers and run them in production without rewriting them?
The naive approach and why it fails
The obvious first attempt is to spawn a new stdio process per request. A connection arrives, the server starts a subprocess, handles the request, and kills the subprocess when done. This is simple and isolated, but it breaks MCP semantics in ways that matter.
MCP servers initialize state on startup. They run initialize, register their tools, and set up any internal context they need. This startup cost is paid on every request if you spawn per-request. For servers that connect to databases, load models, or authenticate to external APIs during initialization, this cost is significant — often several seconds.
It also breaks servers that maintain state across calls. A server that tracks conversation context, caches expensive computations, or holds an authenticated session cannot function correctly if each request gets a fresh process.
The Connector Runtime approach: spawn once
The ToolHost Connector Runtime spawns the stdio child process once and keeps it alive. A single subprocess handles all incoming requests through the runtime's internal message routing. From the subprocess's perspective, it is talking to one client via stdin/stdout. From the gateway's perspective, the runtime exposes a standard Streamable HTTP server.
The architecture looks like this:
ToolHost Gateway
└── Connector Runtime (HTTP server, :8081)
└── stdio subprocess (the actual MCP server)
stdin <-> stdout
The gateway connects to the Connector Runtime as a plain streamable_http backend. No special gateway code is required. The runtime handles the translation between HTTP and stdio, maintaining the subprocess lifetime independently of client connection state.
Process group management and crash recovery
Keeping a subprocess alive in production means handling failure. The Connector Runtime attaches to the subprocess as the process group leader. When the runtime exits, the subprocess exits with it — there are no orphaned processes left behind when the container restarts.
If the subprocess crashes, the runtime detects the exit, logs it, and restarts the process. The restart is clean: a new subprocess initializes from scratch, the runtime re-registers the tools it exposes, and the gateway's next request routes correctly. In-flight requests at the moment of crash receive an error — the runtime does not attempt to replay them, because MCP tool calls are not guaranteed idempotent.
In Docker environments, volume mounts that the subprocess needs to access require careful privilege management. The runtime supports a privilege drop pattern: the subprocess runs as a lower-privilege user after startup, with the runtime process retaining only the permissions needed to manage it. This contains the blast radius of a compromised tool server.
Adding a new stdio server
The configuration to expose a new stdio MCP server through the Connector Runtime is minimal:
{
"name": "filesystem-tools",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/data"],
"env": {
"NODE_ENV": "production"
},
"port": 8081
}
The Connector Runtime starts with this configuration, spawns the subprocess, and begins serving on port 8081. In the gateway backend configuration:
{
"name": "filesystem-tools",
"url": "http://localhost:8081/mcp",
"transport": "streamable_http"
}
Six lines of configuration to take a stdio-only MCP server and make it a production backend. The gateway does not know or care that there is a stdio process behind the runtime. It sees a Streamable HTTP endpoint.
What the runtime does not do
The Connector Runtime is a bridge, not a sandbox. It does not restrict what the subprocess can access on the filesystem, what network connections it can make, or what system calls it can invoke. Isolation is the responsibility of the container or VM running the runtime.
For third-party stdio MCP servers, run each one in its own container with a minimal filesystem view and outbound network restrictions appropriate to what the tool actually needs. The Connector Runtime manages the process lifecycle within that container. The container provides the security boundary.
The runtime also does not currently support resource templates that require bidirectional subscriptions — these require a persistent server-to-client channel that the stdio bridge does not expose to the gateway in its current form. Standard resource reads work correctly; subscription-based resources are a known limitation tracked for a future iteration.
The existing ecosystem
There are hundreds of stdio MCP servers in the wild — filesystem tools, database connectors, code search servers, calendar integrations. None of them need to be rewritten to work with the ToolHost gateway. The Connector Runtime is the adapter layer that makes the existing ecosystem usable in production.
This matters because rewriting servers is not a viable path. The value of the MCP ecosystem is that server authors write to a standard interface. A gateway that requires HTTP-native servers excludes most of that ecosystem. The Connector Runtime closes that gap.