You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
v0.2.0: split into mcp-core library + mcp-gateway CLI
Monorepo restructure (pnpm workspace) and feature expansion:
- Extract pure primitives into @swarmclawai/mcp-core: config schema,
DownstreamManager (transport-injectable), McpRequestRouter, McpMultiClient,
SessionToolPromoter, token estimator. Embedders can use these in-process
without pulling in the CLI or stdio server binding.
- @swarmclawai/mcp-gateway becomes the CLI + stdio/HTTP server wrapping on
top of mcp-core. Re-exports core for back-compat.
- Add mcp_tool_search meta-tool: always-exposed built-in that fuzzy-matches
downstream tools by name/description, promotes matches for the rest of the
session. Agents use it to discover lazy (alwaysExpose: false) tools on
demand instead of eagerly binding every tool schema.
- Add streamable-HTTP transport on both sides. Upstream:
`start --http --port 3477` runs the gateway as a streamable-http server
at POST /mcp. Downstream: config servers can now be remote HTTP endpoints
(url + headers) alongside the existing stdio command spec. Auto-selects
transport per spec via defaultClientTransportFactory.
- Add `npx mcp-gateway add <agent>` installer for claude-code, cursor,
cline, windsurf. --dry-run, --force, .bak backups.
- Add weekly token leaderboard: bench/servers.json + scripts/benchmark.ts
+ .github/workflows/leaderboard.yml publishes per-server token costs to
bench/leaderboard.md.
- README overhaul leading with the token problem, launch post drafts for
HN/r/ClaudeAI/r/LocalLLaMA/r/mcp, CHANGELOGs for both packages, RELEASE.md
with publish process, sync-docs script so tarball docs can't drift,
.github/workflows/release.yml for tag-triggered publish.
37 tests passing across both packages.
Copy file name to clipboardExpand all lines: README.md
+97-58Lines changed: 97 additions & 58 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,41 +1,61 @@
1
1
# mcp-gateway
2
2
3
-
> Local MCP gateway that fans out to N downstream MCP servers, namespaces their tools, and lazy-loads their schemas — so your coding agent's context isn't eaten by MCP boilerplate.
3
+
> **Your coding agent is spending 47,000 tokens on MCP boilerplate before you type a word.**`mcp-gateway`fans out to your downstream MCP servers, namespaces their tools, and only exposes what you ask for. Agents discover the rest on demand via `mcp_tool_search`.
<!-- TODO: replace with recorded before/after GIF of /context in Claude Code -->
11
+
<imgsrc="./docs/assets/before-after.gif"alt="Claude Code /context dropping from 47K to 3K tokens after installing mcp-gateway"width="720" />
12
+
</p>
10
13
11
-
Install more than a handful of MCP servers and something ugly happens: every one of them dumps its full tool schema into your coding agent's context at startup. People routinely report **30,000 – 60,000 tokens** of MCP boilerplate consumed before they've typed a single message. The 1M-context upgrade makes this worse, not better — people just install more servers.
- Docker's own gateway is great at **multi-tenancy** and **auth**.
17
-
- Nobody owns the **local runtime** problem: "I have 15 MCP servers installed. I want 3 of them exposed by default and the other 12 to only show up when I actually ask for them."
`mcp-gateway` is that tool. You point your upstream client (Claude Code, Cursor, Cline, Aider, Windsurf, etc.) at one MCP endpoint — the gateway. It fans out to all your downstream servers, prefixes their tool names to prevent collisions, and only exposes the tools you've explicitly chosen to pre-load.
20
+
# Or Cursor, Cline, Windsurf
21
+
npx @swarmclawai/mcp-gateway@latest add cursor
22
+
npx @swarmclawai/mcp-gateway@latest add cline
23
+
npx @swarmclawai/mcp-gateway@latest add windsurf
24
+
```
20
25
21
-
## 30-second demo
26
+
That edits the agent's MCP config (with a `.bak` backup) so it talks to one gateway endpoint instead of N individual servers. Then:
22
27
23
28
```bash
24
-
# Generate a starter config
29
+
# Generate a starter config and edit to taste
25
30
npx @swarmclawai/mcp-gateway@latest init --write
26
31
27
-
# Edit mcp-gateway.config.json — set alwaysExpose per server
32
+
# See how many tokens each downstream server is spending on tool schemas
33
+
npx @swarmclawai/mcp-gateway@latest token-report
34
+
```
28
35
29
-
# See how many tokens each server is spending
30
-
npx @swarmclawai/mcp-gateway token-report
36
+
## How it works
31
37
32
-
# Point Claude Code at the gateway instead of at each server individually
33
-
claude mcp add gateway -- npx -y @swarmclawai/mcp-gateway@latest start
-**Namespaces downstream tools.** Two servers can both expose `read_file` — you see `fs__read_file` and `github__read_file`.
52
+
-**Lazy-loads by default.** Tools from `alwaysExpose: false` servers stay hidden until an agent calls `mcp_tool_search({query: "..."})`, which promotes them for the rest of the session.
53
+
-**Speaks stdio upstream.** Or streamable-HTTP — see [HTTP mode](#http-mode).
54
+
-**Stdio or HTTP downstream.** Set `command` for local processes, `url` for remote MCP servers.
35
55
36
56
## Config
37
57
38
-
A single `mcp-gateway.config.json` at your project root (or `--config <path>`):
58
+
`mcp-gateway.config.json` at your project root (or `--config <path>`):
39
59
40
60
```json
41
61
{
@@ -59,77 +79,97 @@ A single `mcp-gateway.config.json` at your project root (or `--config <path>`):
-`alwaysExpose: true` — every tool from this server is in your agent's context on startup.
68
-
-`alwaysExpose: false` — tools aren't exposed at first; the gateway connects to the server when the agent calls a tool whose name begins with `<prefix>__`.
94
+
-`alwaysExpose: false` — tools aren't exposed at first; `mcp_tool_search` surfaces them on demand.
69
95
-`alwaysExpose: ["tool_a", "tool_b"]` — only the listed tools are pre-exposed.
96
+
- Each server is **stdio** (`command`) or **HTTP** (`url`), not both.
97
+
98
+
## The `mcp_tool_search` meta-tool
99
+
100
+
The gateway always exposes one built-in tool: `mcp_tool_search({query, limit?})`. Agents call it to discover lazy tools by name or description keywords. Matched tools are promoted for the rest of the session and start showing up in subsequent `list_tools` responses.
70
101
71
-
The gateway prefixes every downstream tool with its server name and the namespace separator (`__` by default) so two servers can both expose a tool called `read_file` without collision — your agent sees `fs__read_file` and `github__read_file`.
102
+
This is the feature that makes `alwaysExpose: false` actually usable — an agent doesn't need to know a tool exists up-front, it searches when it needs one.
72
103
73
-
## Install
104
+
## HTTP mode
74
105
75
106
```bash
76
-
pnpm add -g @swarmclawai/mcp-gateway
77
-
# or
78
-
npm i -g @swarmclawai/mcp-gateway
79
-
# or run on demand
80
-
npx @swarmclawai/mcp-gateway@latest --help
107
+
# Listen on http://127.0.0.1:3477/mcp instead of stdio
- Your agent runs on a server (e.g. SwarmClaw on a VPS) and wants to talk to a local gateway over the network.
113
+
- You want to run one persistent gateway and point multiple agents at it.
114
+
- Your client prefers streamable-HTTP to spawning stdio child processes.
115
+
83
116
## Commands
84
117
85
118
| Command | Purpose |
86
119
|---|---|
87
-
|`init`| Create a starter config |
88
-
|`validate`| Validate the config file without connecting to any downstream |
89
-
|`status`| Connect to every enabled downstream and report status + tool counts |
90
-
|`token-report`| Estimate how many tokens each downstream's schemas cost |
91
-
|`add-server <name> <command> [args...]`| Append a server to the config |
92
-
|`start`| Start the gateway (stdio MCP server for an upstream client) |
120
+
|`add <agent>`| Install the gateway into an agent's MCP config (claude-code, cursor, cline, windsurf) |
121
+
|`init`| Create a starter `mcp-gateway.config.json`|
122
+
|`validate`| Validate the config without connecting |
123
+
|`status`| Connect to each downstream and report status + tool counts |
124
+
|`token-report`| Estimate token cost per downstream |
125
+
|`add-server`| Append a downstream server to the config |
126
+
|`start`| Start the gateway (stdio by default, `--http --port` for streamable-HTTP) |
93
127
|`help-agents`| Print the machine-readable command catalog |
94
128
95
129
Every command accepts `--json` and returns a one-line JSON envelope. Exit codes: `0` success, `1` user error, `2` internal error.
96
130
97
-
## How token-report works
131
+
## Token leaderboard
98
132
99
-
The report walks every downstream server, connects to it over stdio, calls `tools/list`, and estimates the token cost of each tool's name + description + input schema. We don't call a real tokenizer — that'd introduce a heavy dep for a directional number. Chars / 3.5 is close enough to tell you which server is blowing up your window.
133
+
Every week, `mcp-gateway` benchmarks a curated set of popular MCP servers and publishes the results: which are the leanest, which are the heaviest, how much you're spending.
100
134
101
-
## Wiring it into your agent
135
+
See [`bench/leaderboard.md`](./bench/leaderboard.md) — or open a PR against [`bench/servers.json`](./bench/servers.json) to add your server.
102
136
103
-
### Claude Code
137
+
##Used by
104
138
105
-
```bash
106
-
claude mcp add gateway -- npx -y @swarmclawai/mcp-gateway@latest start
107
-
```
139
+
-**[SwarmClaw](https://github.com/swarmclawai/swarmclaw)** — self-hosted autonomous-agent runtime. Embeds `@swarmclawai/mcp-core` (the library half of this repo) so every SwarmClaw agent gets lazy tool exposure + `mcp_tool_search` without running a separate gateway process.
108
140
109
-
Remove your individual server entries — the gateway replaces them.
141
+
Ship something on top of it? Open a PR and we'll add you.
110
142
111
-
### Cursor
143
+
##Library use (`@swarmclawai/mcp-core`)
112
144
113
-
In `~/.cursor/mcp.json`:
145
+
The pure primitives — config parsing, downstream multiplexing, token estimation, `McpRequestRouter`, `McpMultiClient`, `mcp_tool_search` — live in a separate package so embedders can use them in-process:
Same pattern: one `mcp-gateway start` entry in place of N individual server entries. See [`awesome-mcp-for-coding-agents`](https://github.com/swarmclawai/awesome-mcp-for-coding-agents#how-to-install) for the exact config syntax per agent.
168
+
See the [package README](./packages/mcp-core/README.md) for the full API surface.
129
169
130
170
## Built for coding agents
131
171
132
-
Every swarmclawai CLI follows the same agent conventions so Claude Code, Cursor, Cline, Aider, Codex et al can drive them without guessing:
172
+
Every `@swarmclawai/*` CLI follows the same agent conventions so Claude Code, Cursor, Cline, Codex, Factory Droid, Cursor Agent et al can drive them without guessing:
133
173
134
174
-`--json` everywhere, one-line envelope on stdout
135
175
- Stderr for logs, stdout for data
@@ -141,11 +181,10 @@ See [`AGENTS.md`](./AGENTS.md) for the full machine-readable reference.
141
181
142
182
## Roadmap
143
183
144
-
- Session-scoped explicit exposure: an agent can ask the gateway "expose github__* for the rest of this session" without restarting
145
-
- Schema compression: strip optional descriptions on lazy-exposed tools to shrink `tools/list` replies further
146
-
- Observability endpoint: count tool calls per server to justify what should actually be alwaysExpose
147
-
- Remote (HTTP/SSE) transport for upstream clients, not just stdio
148
-
- Per-tool deny/allow list beyond the name prefix
184
+
- Tool **profiles / groups**: `"profile": "coding"` exposes a curated subset across servers
185
+
- Schema compression for lazy-exposed tools
186
+
- Per-agent session state so gateways serving multiple clients don't cross-promote tools
187
+
- Per-tool deny/allow list beyond the namespace prefix
-`@swarmclawai/mcp-gateway` — the CLI + stdio/HTTP server that wraps them
7
+
8
+
`mcp-gateway` depends on `mcp-core` via `workspace:^`, so `pnpm publish` rewrites that to the published version automatically. **Always use `pnpm publish`** — `npm publish` will leave the `workspace:^` literal in the published package.json and break installs.
9
+
10
+
## Manual release
11
+
12
+
From a clean working tree on `main`:
13
+
14
+
```bash
15
+
pnpm install --frozen-lockfile
16
+
pnpm -r run build
17
+
pnpm -r run typecheck
18
+
pnpm -r run test
19
+
20
+
# Bump versions — bump mcp-core first so the gateway's workspace:^ resolves
21
+
# to the new version.
22
+
pnpm --filter @swarmclawai/mcp-core version patch # or minor / major
23
+
pnpm --filter @swarmclawai/mcp-gateway version patch
# Publish mcp-core first (gateway depends on it being resolvable).
31
+
pnpm --filter @swarmclawai/mcp-core publish --access public
32
+
pnpm --filter @swarmclawai/mcp-gateway publish --access public
33
+
```
34
+
35
+
## Automated release
36
+
37
+
Tagging a commit matching `v*` triggers `.github/workflows/release.yml`, which runs the full build + test + publish for both packages using an `NPM_TOKEN` secret.
0 commit comments