Because too many MCP routers turned out to be quite poo.
Run multiple local MCP servers (stdio), aggregate, expose as HTTP. Done.
🚽 No request retries, backend supervision, restart policy, plugin system, policy engine, metrics endpoint, protocol models, authentication or custom JSON-RPC bullshit.
- Catalog projection is configuration.
- Call routing is a lookup.
- Everything else belongs to the SDK or outside the process.
Caution
mcpee provides no authentication or sandboxing.
Keep it on loopback, or put authentication in front of it if you expose it remotely.
Want sandboxing? Run your MCP binaries through codex sandbox …, Seatbelt, bubblewrap, or whatever else fits your platform.
mcpee doesn't care. 🤗
- frontend: stateless Streamable HTTP at
/mcp - default listen address:
127.0.0.1:8080 - backends:
stdioMCP servers owned for the process lifetime - backend commands may be wrappers, but must preserve stdin/stdout.
mcpeemanages only the configured direct child process - projected names:
<backend>__<tool> - startup fails if any backend cannot connect/list tools, or if projection is invalid
- descriptions, schemas, annotations, icons, results, errors, and request cancellation are passed through via the official SDK
- logs go
brrto stderr
Use mcpee -t to validate the configuration without starting backends. Failures return a non-zero exit code. Run mcpee -h for command-line options.
listen: 127.0.0.1:8080
backends:
- name: exec
command: fs-mcp-rs
- name: my-stdio-mcp
command: my-mcp-binary
args:
- foxtrot
- unicorn
env:
CHARLIE: KILO
cwd: .
inherit_env: true
max_frame_size: 32MiBcwddefaults to mcpee's current working directory, resolved to an absolute path.inherit_envdefaults totrue. Configuredenvvalues override inherited values.max_frame_sizeaccepts bytes or human-readable sizes like33554432,32MB,32MiB, or-3KiB. The resulting byte count is passed directly to the MCP SDK.0keeps the SDK default.- startup logs report effective runtime values.
Built on top of modelcontextprotocol/go-sdk. Whatever it needs, we it need too.
Use mise run to discover all tasks (build / test / etc..)
Tests stick to the what mcpee actually owns: projection and routing, pass-through behavior, backend lifecycle, and config semantics.