mcp-shared-gateway
mcp-shared-gateway
One local MCP gateway for all your AI agents. Every Claude Code / Codex / Cursor session and every sub-agent normally starts its own copy of every MCP server. With this gateway, each server runs once and is shared by everyone; idle servers are stopped automatically.
Claude Code ──┐ ┌─ filesystem shared: 1 process for everybody
Codex ────────┼─ shim (tiny stdio) ── gateway ───┼─ memory shared: 1 process for everybody
sub-agents ───┘ one per session localhost └─ chrome-devtools owner: 1 per session, own windowWhy
Memory. N sessions × M servers = N×M processes. Launched with
npx, each server is really a small process tree (npm wrapper + node + console host), so it adds up quickly.Context. Clients only see 6 small meta-tools (
list,describe,call,resource,status,reset). The agent looks up tools when it needs them, so hundreds of tool schemas no longer sit in every context window.One config. Add or change a server in one file instead of in every client.
Related MCP server: MCP Multi-Agent Gateway Studio
Measured
Public servers @modelcontextprotocol/server-filesystem, server-memory and server-sequential-thinking, started with npx -y. Total working set of all processes involved, gateway mode including every shim and the gateway itself (node bench/memory.mjs <sessions>, Windows 11, Node 25):
Sessions | Direct (each session starts its own) | Through the gateway | Saved |
5 | 75 processes, 3055 MB | 26 processes, 1138 MB | 63% |
10 | 150 processes, 6001 MB | 36 processes, 1494 MB | 75% |
Savings grow with the number of sessions and servers: direct costs grow with N×M, the gateway with N small shims + M servers.
Quick start
Requires Node.js 22+.
git clone https://github.com/2836603852/mcp-shared-gateway.git
cd mcp-shared-gateway
pnpm install # or npm install
cp config/servers.example.json config/servers.json # then edit itMount the shim in each client as an ordinary stdio MCP server named gateway. The first call starts the gateway; you never start it by hand.
Claude Code:
claude mcp add --scope user gateway -- node /path/to/mcp-shared-gateway/src/shim.mjsCodex (~/.codex/config.toml):
[mcp_servers.gateway]
command = "node"
args = ["/path/to/mcp-shared-gateway/src/shim.mjs"]Anything that takes mcpServers JSON (Cursor, Claude Desktop, …):
{ "mcpServers": { "gateway": { "command": "node", "args": ["/path/to/mcp-shared-gateway/src/shim.mjs"] } } }Configuration
config/servers.json (see servers.example.json):
Key | Meaning |
| Where the gateway listens (default |
| Stop a backend after this many idle minutes (default 15). |
| Compatibility heartbeat interval when connecting to an older gateway (default 60 s), and an optional limit on how long a running session's idle |
| Proxy for backends. Set it here: on Windows the gateway does not inherit the client's environment. |
| Call timeouts (120 / 900 s) and the size above which long text results are written to a file (40 000 chars). |
| Where long results are written and the default working directory of backends (default |
| Replace the built-in instructions shown to agents (a string, an array of lines, or a file relative to the config). Server |
| Extra environment variable names that backends must not inherit (e.g. |
|
|
| Optional shared Chrome ( |
|
|
| A stdio server. |
| Env values filled in per instance: |
| A remote Streamable HTTP server. |
|
|
| Shown to the agent in |
| Attach to the shared Chrome; each session gets its own window, selected for it, and |
| Shown in |
${NAME} anywhere in the config is filled from .local/secrets.json, then from the environment, so keys stay out of the config.
Which scope? Use shared for stateless servers (docs, search, filesystem, SSH, databases, remote APIs) — that is where the memory goes away. Use owner when a server keeps per-user state such as a selected browser page, a connected device or an open project.
Environment overrides: MCP_GATEWAY_CONFIG (config file), MCP_GATEWAY_STATE (state directory, default .local/), GW_IDLE_MINUTES.
CLI
node src/cli.mjs status # gateway, instances, recent errors
node src/cli.mjs list [server] [--query q]
node src/cli.mjs describe <server> <tool>
node src/cli.mjs call <server> <tool> '{"arg":1}' [--owner name]
node src/cli.mjs reset <server>
node src/cli.mjs browser [url] # open a window in the shared Chrome to log in by hand
node src/cli.mjs stop # restart picks up config changes on the next callOn Windows, mcpx.cmd is a shortcut for node src/cli.mjs.
Running it day to day
Run the gateway from a clone you only update when CI is green, and keep everything machine-specific in files git ignores:
config/servers.json: your servers, proxy, paths. Relative paths in thegatewaysection are relative to this file, e.g."instructionsFile": "../.local/instructions.md"for instructions in your own language,"outputDir": "../.tmp/outputs"..local/: token,secrets.json, logs, tool catalog cache, your instructions file..tmp/: long results and the backends' working directory (safe to delete).
To update: git pull --ff-only, then node src/cli.mjs stop when no session is in the middle of a call; the next call starts the new version. If you also work on the gateway, use a second clone for development so unfinished changes never run as your live gateway.
How it works
shim (
src/shim.mjs): the stdio server each client mounts. It has no MCP SDK dependency, answersinitializeandtools/listitself, and forwards tool calls to the gateway over localhost HTTP. If the gateway is not running, the shim starts it. On Windows it uses WMI, so the gateway is outside the client's process tree and outlives it.gateway (
src/gateway.mjs): starts backends on first use. An instance is identified by server name + owner (or*when shared) + directory (when keyed bycwd). It caches tool lists, solistanddescribedon't start anything, and it stops idle backends.Per-session instances live as long as the session. The shim holds a local HTTP connection so the gateway keeps its
ownerinstances (browser window and tabs, selected device, …) while the agent works on something else. Clock jumps after sleep do not expire this connection. When the shim exits or is killed, the socket closes and the gateway reclaims that session's instances and the pages it opened. If the client exits while a leaked stdin handle stays open, the shim also watches its parent. It reconnects after a gateway restart, falls back to heartbeats with older gateways, and accepts older shims' heartbeats. Shared instances, andownerinstances of clients without a session connection or heartbeats (CLI, HTTP clients), keep theidleMinutestimeout. Restart terminals opened before the upgrade to use the new shim.Only unused start tabs are reused. The first
new_pageuses the window's blank start tab. A script, navigation or emulation change claims that page even when its URL staysabout:blank. If opening a tab in the session window fails, the call returns an error instead of falling back to Chrome's default window and crossing session boundaries.Stuck backends recover. A timed-out call usually keeps running in the backend, and calls on an
ownerinstance run in order, so one stuck call makes the next ones time out too. After two timeouts in a row the gateway replaces that session's instance. The error text tells the agent what happened.Read-only meta-tools are marked as such (MCP tool annotations), so clients with approvals turned off, such as Codex, can run
list,describe,statusandresourcewithout asking.describealso shows the backend tool's own annotations.Requests need a random Bearer token stored in
.local/token, and the gateway only listens on127.0.0.1.
Limits
Used daily on Windows 11. CI runs the tests on Windows, Linux and macOS, but day-to-day use on Linux and macOS is less tested.
All sessions using a shared server see the same backend state. Switch such servers to
ownerif that matters.Shared instances are never restarted automatically after timeouts (other sessions may be using them); use
reset.
Development
pnpm test # starts a gateway on a free port with a fixture server
node bench/memory.mjs 5 # memory benchmark (downloads the public servers via npx)
node tests/manual/long-call.mjs # a call longer than 300 s through the shim (takes ~5 min)
# against the gateway of your config (they restart it / open browser windows):
node tests/manual/lifecycle.mjs # gateway outlives the client; shims reconnect after a restart
node tests/manual/browser-sessions.mjs # two sessions, two windows in the shared browser
node tests/manual/isolated-browser.mjs # disposable profile and ports, using the configured Chrome/backendIssues and PRs are welcome. Please keep the shim dependency-free and add a test for behavior changes.
License
MIT
Related MCP Connectors
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
- aXDeprecatedio.github.ax-platform
Agent-native collaboration network: orchestrate a team of long-running agents from any MCP client.
The OpenRouter for tools. One MCP connection gives any AI agent 254 hosted tools, pay per call.
Shared control plane for AI coding agents — tasks, memory, decisions, file locks. 12 tools.
Related MCP Servers
- AlicenseAqualityCmaintenanceProvides per-Subagent MCP controls to any coding agent or client across all your MCPs and prevents context window waste. Loads only 3 tools instead of all your MCP Server's tool definitions. Agents discover tools on-demand, only when needed and only the servers and tools they are allowed.4109 PyPI41MIT
- AlicenseNot gradedqualityBmaintenanceEnables agents to discover and securely invoke heterogeneous backend tools over unified SSE using JSON-RPC 2.0, with dynamic tool filtering, RBAC, quotas, and audit logging.MIT
- AlicenseNot gradedqualityCmaintenancePresents multiple stdio backend tool servers to an MCP client as one aggregated server, sharing those backend processes across concurrent agent sessions through a background broker while namespacing and routing each tool call to the right backend. Servers can be run shared, per-session, or lazily, with automatic restart and request ID remapping so many sessions coexist without collisions.MIT
- AlicenseNot gradedqualityAmaintenanceEnables MCP clients to list and call tools from multiple configured servers while each backend starts only on first use, is shared across sessions, and stops when idle. This reduces memory and process overhead without changing tool names or permissions.7 npmMIT