Supergateway runs MCP stdio-based servers over SSE (Server-Sent Events) or WebSockets (WS) with one command. This is useful for remote access, debugging, or connecting to clients when your MCP server only supports stdio.
Questions, ideas or just want to chat? Join the community on Discord.
Supported by:
- Supercov — Coverage for coding agents and software factories 🌙
- Superinterface
- Supercorp
Run Supergateway via npx:
npx -y supergateway --stdio "uvx mcp-server-git"--stdio "command": Command that runs an MCP server over stdio--sse "https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app": SSE URL to connect to (SSE→stdio mode)--streamableHttp "https://mcp-server.example.com/mcp": Streamable HTTP URL to connect to (StreamableHttp→stdio mode)--outputTransport stdio | sse | ws | streamableHttp: Output MCP transport (default:ssewith--stdio,stdiowith--sseor--streamableHttp). A remote server given with--sseor--streamableHttpcan be served oversse,wsorstreamableHttptoo; see Remote server → SSE, WS or Streamable HTTP--port 8000: Port to listen on (stdio→SSE, stdio→WS or stdio→Streamable HTTP mode, default:8000)--host 127.0.0.1: Address to listen on, e.g.127.0.0.1or::1([::1]also works) (stdio→SSE, stdio→WS or stdio→Streamable HTTP mode, default: every interface).--baseUrldoes not control binding: only--hostlimits which addresses accept connections. Refused in SSE→stdio and Streamable HTTP→stdio mode, which listen on nothing--baseUrl "http://localhost:8000": Base URL for SSE clients (stdio→SSE mode; optional)--ssePath "/sse": Path for SSE subscriptions (stdio→SSE mode, default:/sse)--messagePath "/message": Path for messages (stdio→SSE or stdio→WS mode, default:/message)--streamableHttpPath "/mcp": Path for Streamable HTTP (stdio→Streamable HTTP mode, default:/mcp)--stateful: Run stdio→Streamable HTTP in stateful mode--sessionTimeout 60000: Session timeout in milliseconds (stateful stdio→Streamable HTTP mode only)--protocolVersion "2025-06-18": Protocol version the gateway uses when it initializes the server itself and the client's request doesn't name one (stateless stdio→Streamable HTTP mode, default:2024-11-05)--header "x-user-id: 123": Add one or more headers (stdio→SSE, stdio→Streamable HTTP, SSE→stdio, or Streamable HTTP→stdio mode; can be used multiple times). With a local server they go on the gateway's responses; with a remote one (--sse,--streamableHttp) they are sent to the remote server--oauth2Bearer "some-access-token": Adds anAuthorizationheader with the provided Bearer token--logLevel debug | info | none: Controls logging level (default:info). Usedebugfor more verbose logs,noneto suppress all logs.--logFormat text | json: Log line format (default:text).jsonwrites one JSON object per line withtime,level,msgand, when a log call carries values,data, for ELK and similar log pipelines. Logs go to the same streams astext, so stdio output still carries only MCP messages.--cors: Enable CORS (stdio→SSE or stdio→WS mode). Use--corswith no values to allow all origins, or supply one or more allowed origins (e.g.--cors "http://example.com"or--cors "/example\\.com$/"for regex matching).--healthEndpoint /healthz: Register one or more endpoints (every mode but stdio output; can be used multiple times) that respond with"ok"--healthCheck gateway | server: What the health endpoints check (default:gateway).gatewayanswers"ok"while the gateway is up.serveralso checks the MCP server: it starts one (or, for--sse/--streamableHttp, opens a session with the remote server), initializes and pings it, and stops it. It answers"ok"if the server responded within 10 seconds, and503with the reason otherwise (e.g.unhealthy: the server exited (code=1, signal=null)). The answer is reused for 10 seconds, so polling every second starts at most one server per 10 seconds. The startup log says when health turns bad and when it recovers--toolPrefix "github_": Put this before every tool name the server lists, sosearchbecomesgithub_search(all modes). Clients call the tool by that name, and the server still gets its own. It is used as given, so include a separator. Tool names may be letters, digits,_,-and., at most 128 characters; the gateway warns about a prefix or name outside that--tools search --tools get_issue: Expose only these tools, by the server's own names (all modes). The others are left out oftools/list, and a call to one is refused with-32602 Unknown tool, as a server refuses a tool it doesn't have, without reaching the server. A bare--toolsexposes none--apiKey "some-key": Require clients to present this key, asAuthorization: Bearer <key>orX-API-Key: <key>(stdio→SSE, stdio→WS or stdio→Streamable HTTP mode; can be used multiple times). AlsoSUPERGATEWAY_API_KEY=some-key. See Requiring an API key--apiKeyFile /run/secrets/keys: Accept the keys in this file, one per line (blank lines are skipped). AlsoSUPERGATEWAY_API_KEY_FILE=/run/secrets/keys--exitWithProcess <pid>: Shut down, stopping the MCP server, when process<pid>exits (all modes). Pass the launcher's PID (e.g.$$); it need not be the direct parent, so it works throughnpx. Checked about once a second. A launcher that spawns Supergateway with a stdin pipe doesn't need this: since 4.0 Supergateway exits when its stdin closes.--config servers.json: Read servers and settings from a config file instead of the server flags. See Several servers from a config file--checkConfig: With--config, check the file, list each server's path and output, and exit--printConfig: Print the resolved config and exit. Keys, bearer tokens, everyenvvalue, the query of every URL and headers with sensitive names are replaced; command lines (args,stdio) are printed as written, so read it over before sharing it. Without--configit prints the file equivalent to the command line given
Expose an MCP stdio server as an SSE server:
npx -y supergateway \
--stdio "npx -y @modelcontextprotocol/server-filesystem ./my-folder" \
--port 8000 --baseUrl http://localhost:8000 \
--ssePath /sse --messagePath /message- Subscribe to events:
GET http://localhost:8000/sse - Send messages:
POST http://localhost:8000/message - Each SSE connection gets its own server process.
Connect to a remote SSE server and expose locally via stdio:
npx -y supergateway --sse "https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app"Useful for integrating remote SSE MCP servers into local command-line environments.
You can also pass headers when sending requests. This is useful for authentication:
npx -y supergateway \
--sse "https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app" \
--oauth2Bearer "some-access-token" \
--header "X-My-Header: another-header-value"Connect to a remote Streamable HTTP server and expose locally via stdio:
npx -y supergateway --streamableHttp "https://mcp-server.example.com/mcp"This mode is useful for connecting to MCP servers that use the newer Streamable HTTP transport protocol. Like SSE mode, you can also pass headers for authentication:
npx -y supergateway \
--streamableHttp "https://mcp-server.example.com/mcp" \
--oauth2Bearer "some-access-token" \
--header "X-My-Header: another-header-value"Expose an MCP stdio server as a Streamable HTTP server.
Supports legacy MCP and 2026-07-28 when the client and stdio server support a common protocol version. Clients that support automatic negotiation can fall back to legacy when the server requires it.
--stateful preserves legacy sessions. MCP 2026-07-28 uses independent requests and does not create a transport session.
Interactive MCP 2026-07-28 operations can continue across requests. Continuations and explicit retries are available for up to five minutes of inactivity, with at most 64 saved continuation states. Older states may expire sooner when this limit is reached.
npx -y supergateway \
--stdio "npx -y @modelcontextprotocol/server-filesystem ./my-folder" \
--outputTransport streamableHttp \
--port 8000npx -y supergateway \
--stdio "npx -y @modelcontextprotocol/server-filesystem ./my-folder" \
--outputTransport streamableHttp --stateful \
--sessionTimeout 60000 --port 8000The Streamable HTTP endpoint defaults to http://localhost:8000/mcp (configurable via --streamableHttpPath).
Expose an MCP stdio server as a WebSocket server:
npx -y supergateway \
--stdio "npx -y @modelcontextprotocol/server-filesystem ./my-folder" \
--port 8000 --outputTransport ws --messagePath /message- WebSocket endpoint:
ws://localhost:8000/message - Each WebSocket connection gets its own server process.
Serve a remote MCP server to clients that need another transport, or behind your own API key:
npx -y supergateway \
--streamableHttp "https://mcp.example.com/mcp" \
--oauth2Bearer "$UPSTREAM_TOKEN" \
--outputTransport streamableHttp --stateful --apiKey "$MCP_API_KEY"- Each client session gets its own session with the remote server, ended when the client's ends, and at shutdown.
--headerand--oauth2Bearergo to the remote server. The client's ownAuthorizationand API key never do.- Requests from the remote server to the client (sampling, roots, elicitation) are passed through.
- A remote server that is down, refuses the client or goes away fails that session only.
- The 2026-07-28 protocol's stateless requests are passed on to a remote Streamable HTTP server that speaks that version. The gateway asks it once a minute (
server/discover). One that doesn't, and any remote SSE server, is served as before: its clients are told the version isn't supported, and those that negotiate fall back to an earlier one.
By default anyone who can reach the port can use the server. With --apiKey, every request to stdio→SSE, stdio→WS or stdio→Streamable HTTP must carry a key:
npx -y supergateway \
--stdio "npx -y @modelcontextprotocol/server-filesystem ./my-folder" \
--outputTransport streamableHttp --apiKey "$MCP_API_KEY"
curl -H "Authorization: Bearer $MCP_API_KEY" ... # or: -H "X-API-Key: $MCP_API_KEY"- A request without a valid key gets
401 Unauthorized. The--healthEndpointpaths and, with--cors, browser preflight requests stay open. - Keys from
--apiKey,--apiKeyFile,SUPERGATEWAY_API_KEYandSUPERGATEWAY_API_KEY_FILEare all accepted together, so a key can be rotated by adding the new one before removing the old. - An empty key, an unreadable key file or one with no keys stops the gateway at startup rather than running it without authentication.
- Keys are never logged. Use HTTPS (e.g. behind a reverse proxy) so they are not sent in clear text.
- To send a key to a remote server from SSE→stdio or Streamable HTTP→stdio, use
--headeror--oauth2Bearer;--apiKeyis refused there.
--config reads the mcpServers file that Claude Desktop and other MCP clients use, so a client's file works as-is. Each server is served at /<name> on one port:
npx -y supergateway --config servers.jsongitis served over SSE athttp://localhost:8000/git/sseand/git/message;filesover Streamable HTTP athttp://localhost:8000/files/mcp.- A server is
command+args(run without a shell, as clients run them),stdio(a shell command line, as--stdio), orurl+type(sseorhttp).envandcwdset its environment and directory. - Any option from the list above can be set on a server, by its flag name (
outputTransport,stateful,cors,headers,apiKey,healthEndpoint,toolPrefix,tools, ...). Set at the top level, it is the default for every server. Defaults are the command line's: a local server is served over SSE, aurlone on stdio. pathserves a server somewhere other than/<name>. A name that can't be part of a URL needs one. The gateway refuses to start if a server's URL would be answered by another server or by the gateway's ownhealthEndpoint.port,host,logLevel,logFormat,exitWithProcessand the top-levelhealthEndpointare the gateway's own. A top-levelhealthEndpointanswers for the whole gateway; one on a server is under that server's path. With"healthCheck": "server", a server's own health endpoints check that server; the gateway's stay"ok"while the gateway is up, so one failing server doesn't fail the whole gateway.apiKeyon a server locks that server only. Keys from--apiKey,--apiKeyFileorSUPERGATEWAY_API_KEYlock every server."disabled": trueskips a server. Keys only clients use (autoApprove,timeout,disabledTools, ...) are warned about and ignored. Any other unknown key is an error that suggests the closest known one.${VAR},${VAR:-default}and${env:VAR}are replaced from the environment in every value except astdiocommand line, which the shell expands itself. A variable that isn't set is an error.$$is a literal$.- Flags beside
--configmay be the gateway's own (--port,--host,--logLevel,--logFormat,--exitWithProcess,--healthEndpoint,--apiKey,--apiKeyFile). They override the file, and the startup log says so. A server flag such as--statefulis refused, because it would be unclear which server it means. - With more than one server, each log line about a server starts with its name (
[git]), and JSON logs give it aserverfield. - On Windows,
"command": "npx"needsnpx.cmd, as it does in Claude Desktop, sincecommandruns without a shell.stdioruns through the shell. - Comments and trailing commas are allowed (JSONC). Run
--checkConfigafter editing.
A url server is served like a local one when it has an output other than stdio (see Remote server → SSE, WS or Streamable HTTP): "outputTransport": "streamableHttp", for example.
One url server may use stdio output beside servers on the port. This is for a client that launches Supergateway with a config file, such as Claude Desktop: it talks to that server over stdin and stdout, and other clients reach the rest over HTTP. All logs then go to stderr. The process belongs to the client that started it. When stdin closes, a signal arrives, or the stdio server stops (its remote server refused the first connection, for example), every server stops, and the exit code is the stdio server's.
An entry with its own mcpServers serves them as one MCP server, at the entry's URL:
{
"port": 8000,
"outputTransport": "streamableHttp",
"mcpServers": {
"dev": {
"mcpServers": {
"git": { "command": "uvx", "args": ["mcp-server-git"] },
"files": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
},
"docs": {
"url": "https://docs.example.com/mcp",
"type": "http",
"toolPrefix": "docs_",
},
},
},
},
}A client of http://localhost:8000/dev/mcp sees the tools, prompts and resources of all three.
- Names are not changed. A request goes to the server that has the tool, prompt or resource it names. When two servers offer the same name, the first one listed wins and the other's is hidden; the log says so once. Set
toolPrefixon a server to keep both, andtoolsto choose which of a server's tools are shown. Many tools behind one URL make a model's choice harder, so combine what belongs together. - Each session has its own servers, started when the client initializes. A server that can't start is left out, with a line in the log; the session fails only if none starts. A server that stops later fails the calls it had, the client is told the lists changed, and the rest go on.
- Requests from a server to the client (sampling, roots, elicitation) work as they do for one server, on outputs that carry them (SSE, WebSocket, stateful Streamable HTTP).
- The protocol version is the lowest any of the servers answers; the capabilities are everything any of them has; their
instructionsare joined, each under its server's name. - Lists come as one page, every server's in the order listed.
- Settings for the URL (
outputTransport,apiKey,cors,healthEndpoint, ...) go on the entry. A combined server hascommand/args/env/cwd,stdio, orurl/type/headers/oauth2Bearer, andtoolPrefix/tools. Combining goes one level deep. - On stdio (
"outputTransport": "stdio"), a combined entry is what a desktop client launches to reach several servers through one entry of its own config. It can run beside servers on the port, as a single remote server can. - 2026-07-28 is answered when every combined server speaks it (the gateway asks them once a minute). Each request then starts only the server it is for;
server/discoverand the lists go to every server. If one server doesn't speak it, the entry answers the earlier versions, and clients that negotiate fall back. - Not yet: tasks. Combined servers share one model context, so combine only servers you trust with each other's results.
Each release also ships Supergateway as a single executable with Node built in, so it runs where Node is not installed. It takes the same flags.
Homebrew (macOS and Linux):
brew install supercorp-ai/tap/supergatewaymacOS (Apple Silicon) and Linux: download and unpack. Use linux-x64, linux-arm64, linux-musl-x64 or linux-musl-arm64 in place of darwin-arm64 as needed.
curl -fsSL https://github.com/supercorp-ai/supergateway/releases/latest/download/supergateway-darwin-arm64.tar.gz | tar -xz./supergateway --stdio "uvx mcp-server-time" --port 8000Windows (PowerShell):
Invoke-WebRequest https://github.com/supercorp-ai/supergateway/releases/latest/download/supergateway-win-x64.zip -OutFile supergateway.zip; Expand-Archive supergateway.zip ..\supergateway.exe --stdio "uvx mcp-server-time" --port 8000Good to know:
- The MCP server you wrap still needs its own runtime:
--stdio "npx -y ..."needs Node,--stdio "uvx ..."needs uv. - Linux needs glibc 2.28 or later. On Alpine use the
linux-muslbuild and runapk add libstdc++first. - Intel Macs have no standalone build;
brew installgives them the npm package on Homebrew's Node instead. - The executables are not code-signed by a paid certificate. Downloaded from a browser, macOS Gatekeeper and Windows SmartScreen ask before the first run;
curl, PowerShell and Homebrew installs are not affected. - Every archive is listed in the release's
SHA256SUMS, and you can check it was built by this repository's CI withgh attestation verify supergateway-darwin-arm64.tar.gz -R supercorp-ai/supergateway.
Allow more than five seconds for graceful shutdown. Child servers should handle SIGTERM when you stop the gateway with Ctrl-C.
For network-output gateways, closing a pipe connected to stdin also stops the gateway. Starting with stdin ignored or redirected from /dev/null keeps it running.
- Run Supergateway:
npx -y supergateway --port 8000 \
--stdio "npx -y @modelcontextprotocol/server-filesystem /Users/MyName/Desktop"- Use MCP Inspector:
npx @modelcontextprotocol/inspectorYou can now list tools, resources, or perform MCP actions via Supergateway.
Use ngrok to share your local MCP server publicly:
npx -y supergateway --port 8000 --stdio "npx -y @modelcontextprotocol/server-filesystem ."
# In another terminal:
ngrok http 8000ngrok provides a public URL for remote access.
MCP server will be available at URL similar to: https://1234-567-890-12-456.ngrok-free.app/sse
A Docker-based workflow avoids local Node.js setup. A ready-to-run Docker image is available here: supercorp/supergateway. Also on GHCR: ghcr.io/supercorp-ai/supergateway
docker run -it --rm -p 8000:8000 supercorp/supergateway \
--stdio "npx -y @modelcontextprotocol/server-filesystem /" \
--port 8000Docker pulls the image automatically. The MCP server runs in the container’s root directory (/). You can mount host directories if needed.
Pull any of these pre-built Supergateway images for various dependencies you might need.
-
uvx Supergateway + uv/uvx, so you can call
uvxdirectly:docker run -it --rm -p 8000:8000 supercorp/supergateway:uvx \ --stdio "uvx mcp-server-fetch" -
deno Supergateway + Deno, ready to run Deno-based MCP servers:
docker run -it --rm -p 8000:8000 supercorp/supergateway:deno \ --stdio "deno run -A jsr:@omedia/mcp-server-drupal --drupal-url https://your-drupal-server.com"
Build from this checkout:
npm ci
npm run pack:release
docker build -f docker/base.Dockerfile -t supergateway \
--build-arg VERSION="$(node -p "require('./.release/manifest.json').version")" \
--build-arg PACKAGE_SHA256="$(node -p "require('./.release/manifest.json').sha256")" .
docker run -it --rm -p 8000:8000 supergateway --stdio "npx -y @modelcontextprotocol/server-filesystem ."Claude Desktop can use Supergateway’s SSE→stdio mode.
{
"mcpServers": {
"supermachineExampleNpx": {
"command": "npx",
"args": [
"-y",
"supergateway",
"--sse",
"https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app"
]
}
}
}{
"mcpServers": {
"supermachineExampleDocker": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"supercorp/supergateway",
"--sse",
"https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app"
]
}
}
}Cursor can also integrate with Supergateway in SSE→stdio mode. The configuration is similar to Claude Desktop.
{
"mcpServers": {
"cursorExampleNpx": {
"command": "npx",
"args": [
"-y",
"supergateway",
"--sse",
"https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app"
]
}
}
}{
"mcpServers": {
"cursorExampleDocker": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"supercorp/supergateway",
"--sse",
"https://mcp-server-ab71a6b2-cd55-49d0-adba-562bc85956e3.supermachine.app"
]
}
}
}Note: Although the setup supports sending headers via the --header flag, if you need to pass an Authorization header (which typically includes a space, e.g. "Bearer 123"), you must use the --oauth2Bearer flag due to a known Cursor bug with spaces in command-line arguments.
In stdio→SSE mode only the path of --baseUrl reaches clients: --baseUrl https://mcp.example.com/gateway makes the message endpoint /gateway/message, and clients post to the host they connected through. Clients that need an absolute endpoint URL, such as Copilot Studio, should use --outputTransport streamableHttp.
Model Context Protocol standardizes AI tool interactions. Supergateway converts MCP stdio servers into SSE or WS services, simplifying integration and debugging with web-based or remote clients.
- Superargs - provide arguments to MCP servers during runtime.
- @Sodawyx
- @brrock
- @jwalitptl
- @jakajancar
- @thedadams
- @iutx
- @hxy91819
- @gamedevsam
- @davidferlay
- @bossanyit
- @bbracha-evinced
- @homer6
- @aleleba
- @bimax
- @essentialols
- @gcoinstash-cmd
- @nullStack65
- @srijan
- @TheItschi
- @wowsofine
- @yanziwei
- @tttcoding666
- @aneasystone
- @luyunfeng-bytedance
- @kvick-games
- @nowireless4u
- @paul-maas
- @ArnaudBger
- @alvaroalon2
- @oshaban
- @springbrookconsultingllc-byte
- @jstar0
- @v8eta
- @zaggash
- @0xt3ch
- @werebear73
- @move-hoon
- @dustindoan
- @swarthyplacebo
- @ildunari
- @tamermina
- @frankstupak
- @brainoir
- @JuliaF1988
- @terjefl
- @yangzinan
- @ckhsponge
- @AxelFooley
- @Growdy
- @sulivanti
- @EvanSchalton
- @suneetagarwalre-boop
- @edmcman
- @noyoa
- @JamesSlocumIH
- @mike12806
- @oscar-izval
- @haissamtariqzaman
- @rubenmajor2
- @quigles1977
- @jmcgurk2
- @maxx3250
- @julioccorderoc
- @logan-crosby
- @BishopMartin
- @gkinter
- @JoeLuker
- @agerit-programator2
- @sfasching
- @RussellZager
- @Farzy
- @body-cmd
- @cosmic-fire-eng
- @0xbrainkid
- @dangdinhquan
- @iandol
- @micci184
- @manmao
- @sibelius
- @NathanNeves
- @Avi-Robusta
- @yakovyarmo
- @GhimBoon
- @akirilyuk
- @ongeluk
- @dparkmit24
- @glani
- @enxilium
- @scalabreseGD
- @davidjitca
- @sbatista-uc
- @terafin
- @ecdesigns2007
- @brendandebeasi
- @waldman
- @O7Furkan17
- @HalmSascha
- @Paul-Kyle
- @janosborst
- @MartinZvelebil
- @longfin
- @griffinqiu
- @folkvir
- @wizizm
- @dtinth
- @rajivml
- @NicoBonaminio
- @sibbl
- @podarok
- @jmn8718
- @TraceIvan
- @zhoufei0622
- @ezyang
- @aleksadvaisly
- @wuzhuoquan
- @mantrakp04
- @mheubi
- @mjmendo
- @CyanMystery
- @earonesty
- @StefanBurscher
- @tarasyarema
- @pcnfernando
- @Areo-Joe
- @Joffref
- @michaeljguarino
- @qdrddr
- @Shellishack
- @anyuan95
Issues and PRs welcome. Please open one if you encounter problems or have feature suggestions. For questions and discussion, join us on Discord.
Supergateway is tested with the Node Test Runner.
To run the suite locally you need Node 24+. Using nvm you can install and activate it with:
nvm install 24
nvm use 24
npm install
npm run build
npm testThe tests/helpers/mock-mcp-server.js script provides a local MCP server so all
tests run without network access.
Two more checks compare this build with the last release, and need the network to fetch it. CI runs both on every pull request:
npm run test:versions # the same client against the release and this build, side by side
npm run test:released # the release's own end-to-end tests, run against this buildA difference that is meant is written down with its reason in tests/crossVersion.intended.ts or tests/releasedTests.intended.json; any other fails.

{ "port": 8000, "mcpServers": { "git": { "command": "uvx", "args": ["mcp-server-git"] }, "files": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "./my-folder"], "outputTransport": "streamableHttp", "apiKey": "${FILES_KEY}", }, }, }