Bridge stdio and remote MCP servers: mcp-remote, Supergateway, mcp-proxy and mcpo
Put a local stdio MCP server on HTTP so remote agents can reach it, or plug a remote server into a client that only speaks stdio. Covers mcp-remote, Supergateway, mcp-proxy and mcpo: which direction each one bridges, exact commands, how to lock the endpoint down, and two dependency breakages we hit.
Prerequisites
- Node.js (for npx: mcp-remote, Supergateway and most reference servers) and/or uv (for uvx: mcp-proxy, mcpo and Python servers)
- A stdio MCP server you want to expose, or a remote MCP URL you want to use from a stdio-only client
- For anything beyond localhost: a reverse proxy or tunnel that terminates HTTPS
MCP has two standard transports. stdio: the client starts the server as a child process and talks over stdin/stdout. Streamable HTTP (and the older SSE transport): the server is a web endpoint that any client on the network can reach. Many useful servers still ship stdio only, and a few clients still only launch stdio servers. A bridge translates between the two, so you don't have to rewrite either side.
Which direction do you need?
| You have | You want | Use |
|---|---|---|
| A stdio server on your machine | Remote agents, a cloud IDE or a hosted client to call it over HTTP | Supergateway --stdio or mcp-proxy server mode |
| A remote MCP URL (possibly OAuth-protected) | A client that only launches stdio servers | mcp-remote, or Supergateway / mcp-proxy client mode |
| Any MCP server | Plain REST with an OpenAPI schema (Open WebUI, OpenAPI tool callers, curl) | mcpo |
flowchart LR
subgraph expose[Expose a stdio server]
S1[stdio server] -- stdin/stdout --> G[Supergateway / mcp-proxy] -- Streamable HTTP or SSE --> RC((remote client))
end
subgraph consume[Use a remote server from a stdio client]
LC((stdio-only client)) -- stdin/stdout --> R[mcp-remote] -- HTTPS + OAuth --> RS[remote MCP server]
end
subgraph rest[REST instead of MCP]
S2[any MCP server] --> O[mcpo] -- OpenAPI / REST --> T((OpenAPI tool caller))
endThe four bridges at a glance
| mcp-remote | Supergateway | mcp-proxy | mcpo | |
|---|---|---|---|---|
| Version we ran (2026-10-25) | 0.15.2 (npm) | 4.2.1 (npm) | 0.13.0 (PyPI, Python ≥3.10) | 0.0.20 (PyPI, Python ≥3.11) |
| stdio → network | no | SSE, WebSocket or Streamable HTTP | SSE and Streamable HTTP on one port | REST + OpenAPI (not MCP) |
| network → stdio | yes (HTTP first, SSE fallback by default) | yes (--sse / --streamableHttp) | yes (SSE, or --transport=streamablehttp) | n/a |
| OAuth to the upstream server | full browser flow, tokens in ~/.mcp-auth | static bearer token (--oauth2Bearer) or headers | OAuth2 client credentials, basic/digest, headers | OAuth 2.1 with dynamic client registration (Streamable HTTP upstreams) |
| Auth for your endpoint | n/a (local process) | --apiKey / --apiKeyFile | none documented | --api-key |
| Default listen address | n/a | every interface | 127.0.0.1, random port | 0.0.0.0:8000, CORS * |
| Several servers | one per process | --config with an mcpServers file, or several combined on one URL | --named-server / --named-server-config at /servers/<name>/ | --config (Claude Desktop format), one route per server, --hot-reload |
| License | MIT | MIT | MIT | MIT |
Versions are the latest on npm and PyPI on 2026-10-25. Defaults come from each README, except mcpo's listen address and CORS default, which come from its CLI source and matched what mcpo logged when we started it.
1. Expose a stdio server over Streamable HTTP (Supergateway)
Supergateway has the most complete server mode. The command below wraps the reference filesystem server (from the MCP reference servers), binds to localhost, requires a key and exposes only two read tools:
export MCP_API_KEY="$(openssl rand -hex 24)"
npx -y supergateway@4.2.1 \
--stdio "npx -y @modelcontextprotocol/server-filesystem /srv/shared" \
--outputTransport streamableHttp \
--host 127.0.0.1 --port 8000 \
--apiKey "$MCP_API_KEY" \
--tools list_directory --tools read_text_file
The endpoint is http://127.0.0.1:8000/mcp. When we ran this on 2026-10-25:
- An
initializerequest without a key got HTTP 401. - With
Authorization: Bearer <key>(orX-API-Key: <key>) it answeredinitialize, andtools/listreturned onlyread_text_fileandlist_directory. - A
tools/callforwrite_filecame back as JSON-RPC error-32602 "Unknown tool: write_file"and never reached the server, as the README describes.
Useful flags from the README:
--stateful(with--sessionTimeout) keeps legacy MCP sessions. The default stdio→Streamable HTTP mode is stateless.--healthEndpoint /healthz --healthCheck serveractually starts and pings the wrapped server instead of only reporting that the gateway is up.--toolPrefix github_renames tools so two servers' tools don't collide.--config servers.jsonreads the samemcpServersfile that desktop clients use and serves each server at/<name>. An entry with its ownmcpServerscombines several servers behind one URL.- Standalone executables (Homebrew
supercorp-ai/tap/supergateway, GitHub releases) and Docker images (supercorp/supergateway, plus:uvxand:denovariants) exist if you don't want Node on the host.
2. Use a remote server from a stdio-only client (mcp-remote)
Most current clients (Claude Code, Cursor, Codex CLI) connect to remote servers natively. Check that first: the guide Connect an agent to a remote MCP server covers it, and its README says mcp-remote is meant to be removed once your client supports remote, authorized servers. For a client that only launches commands, add mcp-remote as the command:
{
"mcpServers": {
"remote-example": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://remote.mcp.server/mcp"]
}
}
}
What the README documents:
- OAuth. mcp-remote runs the browser sign-in itself and caches credentials in
~/.mcp-auth(orMCP_REMOTE_CONFIG_DIR).--resourcesends an RFC 8707 resource indicator and keeps separate sessions per tenant.--authorize-paramadds provider-specific parameters (for exampleaccess_type=offlinefor Google).--disable-resource-parameteris for servers like Microsoft Entra ID that reject the resource parameter. - Static tokens. Use
--header "Authorization:${AUTH_HEADER}"with the value inenv, without spaces around the colon. The README says Cursor, Codex CLI and Claude Desktop on Windows mangle spaces insideargs. Better still, use--header-file, so the token doesn't show up in the process list. - Transport.
--transportishttp-first(default),sse-first,http-onlyorsse-only. - Plain HTTP is refused unless you pass
--allow-http, which is meant only for trusted private networks. - Debugging.
npx -p mcp-remote@latest mcp-remote-client <url>runs the whole auth flow and lists tools outside your client.--debugwrites logs to~/.mcp-auth/.
Round trip we ran. We pointed mcp-remote 0.15.2 at the Supergateway endpoint from step 1 (in --stateful mode, with --allow-http because it was on localhost and --header "Authorization:Bearer <key>"). Then we piped initialize and tools/list into its stdin. The filesystem server's answers came back on stdout: stdio → HTTP → stdio, with no code written.
3. The Python alternative (mcp-proxy)
mcp-proxy does both directions in one Python tool. To expose a server:
uv tool install mcp-proxy # or: pipx install mcp-proxy
mcp-proxy --port 8080 -- npx -y @modelcontextprotocol/server-filesystem /srv/shared
On 2026-10-25, version 0.13.0 listened on 127.0.0.1 and served both transports from the same port: Streamable HTTP at /mcp (it answered initialize with an mcp-session-id) and SSE at /sse. It also had a /status JSON endpoint. Pass --stateless for stateless Streamable HTTP. To use a remote server from a stdio client, give it the URL instead of a command: mcp-proxy --transport=streamablehttp https://host/mcp, with API_ACCESS_TOKEN or --headers, OAuth2 client credentials (--client-id, --client-secret, --token-url) or basic/digest auth.
Caveat: the README documents no authentication for the endpoint mcp-proxy serves. Keep it on 127.0.0.1 (the default) or put an authenticating reverse proxy in front of it. Don't bind it to 0.0.0.0 on an open network.
4. MCP as REST (mcpo)
mcpo isn't an MCP bridge. It turns each MCP tool into a REST endpoint with a generated OpenAPI schema and docs at /docs, for tools that speak OpenAPI rather than MCP (it comes from the Open WebUI project). Upstreams can be stdio, SSE or Streamable HTTP, and --config serves several servers at /<name>.
What we hit on 2026-10-25. The README command uvx mcpo ... failed at startup in two different ways, because mcpo 0.0.20 only sets lower bounds on its dependencies (mcp>=1.17.0, fastapi>=0.115.12):
- uv resolved
mcp2.3.0, and mcpo crashed withImportError: cannot import name 'streamablehttp_client'. Open issue #303 tracks this. - After pinning
mcp<2, it crashed again withTypeError: FastAPI.__init__() got an unexpected keyword argument 'ssl_certfile'(FastAPI 0.162.0 had been resolved).
This pin worked:
uvx --with "mcp<2" --with "fastapi<0.130" mcpo@0.0.20 \
--host 127.0.0.1 --port 8000 --api-key "$MCP_API_KEY" \
-- npx -y @modelcontextprotocol/server-filesystem /srv/shared
We picked <0.130 as a known-good bound and didn't track down the exact FastAPI release that breaks it. With the pin, a request without the key got 401. POST /read_text_file with Authorization: Bearer <key> returned the file. /openapi.json listed one path per filesystem tool, including write_file, edit_file and move_file, so use disabledTools in a config file to hide the ones you don't want exposed. Note that mcpo binds to 0.0.0.0 with CORS * by default, so always pass --host 127.0.0.1 and --api-key unless you really mean to serve the whole network. The README says "Python 3.8+", but PyPI lists Requires-Python >=3.11.
Lock it down before you expose anything
A bridge turns a process that only you could start into a network service. The MCP transport spec says servers must validate the Origin header (to stop DNS-rebinding attacks from web pages) and should bind only to localhost when running locally. Bridges sit outside your server, so these checks are on you:
- Bind to 127.0.0.1 (
--host) and publish through a reverse proxy or tunnel that terminates HTTPS. Supergateway's README says to use HTTPS so keys aren't sent in clear text. - Require a key (Supergateway
--apiKey, mcpo--api-key). For per-user access, audit logs and policy, put a real gateway in front instead. The MCP gateways comparison covers Docker MCP Gateway and ToolHive, and LiteLLM's MCP gateway also accepts stdio servers. For OAuth on your own server, see Add OAuth to your MCP server. - Expose the fewest tools you can (Supergateway
--tools, mcpodisabledTools). A filesystem or shell server reachable over HTTP is remote code execution waiting to happen. Scope its directory, and consider running it in a container or sandbox (see Run untrusted code in a sandbox). - Leave CORS off unless a browser client needs it, and then list origins explicitly. mcpo's default of
*is the one to override. - Pin versions (
supergateway@4.2.1,mcp-remote@0.15.2,mcpo@0.0.20). Unpinnednpx/uvxruns fetch whatever is newest, and the mcpo breakage above shows that a dependency's major release can break a bridge overnight. - Test the endpoint before giving it to an agent: MCP Inspector connects to Streamable HTTP and SSE URLs. The guide Test your MCP server has a checklist.
Choosing quickly
- Just need Claude Desktop-style stdio config to reach a hosted OAuth server: mcp-remote.
- Need to share a local stdio server with remote agents, with a key and a tool allowlist: Supergateway.
- Python-only environment, or you want SSE and Streamable HTTP from one process: mcp-proxy, kept on localhost behind an authenticating proxy.
- Your consumer speaks OpenAPI, not MCP: mcpo, with the dependency pin above until issue #303 is resolved.
- Many servers, many users, audit requirements: skip the bridges and use a gateway (MCP gateways compared).
To find stdio servers worth bridging, or a hosted server that makes the bridge unnecessary, search Index Agentica with the pick-an-mcp-server skill.
Directory entries in this guide
- mcp-remote: npx tool that lets an MCP client that only supports local stdio servers connect to a remote MCP server, including OAuth flows.
- Supergateway: Runs MCP stdio servers over Streamable HTTP or SSE, or bridges a remote SSE/Streamable HTTP server back to stdio, using a single npx command.
- mcp-proxy: Python bridge between MCP transports: it exposes a stdio server over SSE/Streamable HTTP, or connects a stdio client to a remote SSE/Streamable HTTP server.
- mcpo: Open WebUI's MCP-to-OpenAPI proxy that exposes any MCP server as a REST/OpenAPI HTTP server, so OpenAPI-speaking agents and tools can call it.
- Model Context Protocol (MCP): Open protocol that standardizes how AI applications connect to external tools, data sources and prompts via MCP servers.
- MCP Reference Servers: Reference MCP server implementations maintained by the MCP steering group: Everything, Fetch, Filesystem, Git, Memory, Sequential Thinking and Time.
- MCP Inspector: Official interactive developer tool for testing and debugging MCP servers in the browser, on the command line or in the terminal.
- MCP Authorization (OAuth 2.1 for MCP): The MCP specification's transport-level authorization flow for HTTP-based MCP servers, based on a subset of OAuth 2.1 and related RFCs.
- RFC 8707: Resource Indicators for OAuth 2.0: IETF Proposed Standard adding a `resource` parameter so a client can tell the authorization server which protected resource a token is meant for (audience binding).
- Docker MCP Gateway: Docker's open-source MCP gateway (the docker mcp CLI plugin behind MCP Toolkit): one endpoint that runs MCP servers in isolated containers and injects credentials.
- ToolHive: Stacklok's open-source platform that runs each MCP server in an isolated container with per-request identity and access policy and observability.
- LiteLLM MCP Gateway: The LiteLLM Proxy's MCP Gateway gives one fixed endpoint for MCP tools, controls access by key and team, and lets any LiteLLM-supported model call them.
- Claude Code: Anthropic's agentic coding tool that reads your codebase, edits files, runs commands and integrates with dev tools; available in terminal, IDE, desktop and browser.
- Cursor: AI code editor and coding agent with Agent mode, Rules, Skills, MCP servers and a CLI.
- OpenAI Codex CLI: OpenAI's lightweight open-source coding agent that runs in your terminal.
Related
- Connect an agent to a remote MCP server (Guide)
- Publish an MCP server so agents can find it (Guide)
- Test your MCP server with in-process tests and Inspector CI checks (Guide)
- MCP gateways compared (Comparison)
- Add OAuth authorization to your own MCP server (Guide)
- Run agent-generated code safely in a sandbox (Guide)
- Connect a remote MCP server (Skill)
Sources
- mcp-remote README (punkpeye/mcp-remote), accessed
- mcp-remote on npm, accessed
- Supergateway README, accessed
- supergateway on npm, accessed
- mcp-proxy README (sparfenyuk/mcp-proxy), accessed
- mcp-proxy on PyPI, accessed
- mcpo README (open-webui/mcpo), accessed
- mcpo on PyPI, accessed
- mcpo CLI defaults (src/mcpo/__init__.py), accessed
- mcpo issue #303: errors while starting (mcp 2 dependency), accessed
- MCP specification 2025-11-25: Transports (security warning), accessed
- MCP Python SDK (mcp) on PyPI, accessed