← writing

The Hidden Cost of a Loaded MCP Stack

I’m running 22 MCP servers through Claude Code right now. Bench, Binnacle, Keel, Berth, Mooring, Lathe, Sound, Homelab Infra, Portainer, Grafana, InfluxDB, Cloudflare, GitHub, Vault, Contacts, Calendar, Miniflux, Resend, Umami, LightRAG, a Home Assistant bridge, and a Stripe read-only server. Every session, all 22 start. That’s a lot of processes, a lot of tool descriptions loaded into context, and a lot of surface area the LLM has to reason about before it writes a line.

The process management problem is real even at five or six servers. Without anything managing them, Claude Code spawns each MCP server as a subprocess when it starts your session and kills them when you close it. You feel this as startup lag. You feel it more when one server is slow to initialize or crashes mid-session and takes everything down with it. At 22 servers, you feel it constantly.

The fix I use is tbxark/mcp-proxy, a lightweight proxy that runs persistently as a launchd service and manages all the child processes itself. The proxy starts when your Mac boots, spawns every configured MCP server, and keeps them running. Claude Code connects to each server through the proxy’s SSE endpoints instead of spawning processes directly.

The config lives at ~/.config/mcp-proxy/config.json and looks like this:

{
  "mcpProxy": {
    "addr": "127.0.0.1:3700",
    "type": "streamable-http"
  },
  "mcpServers": {
    "bench": {
      "command": "/Users/you/Projects/bench/.build/release/Bench",
      "args": []
    },
    "berth": {
      "command": "/Users/you/Projects/berth/.venv/bin/berth",
      "args": []
    },
    "binnacle": {
      "command": "/usr/local/bin/binnacle",
      "args": []
    }
  }
}

Each server then gets an entry in Claude Code’s MCP config pointing at the proxy:

{
  "mcpServers": {
    "bench": {
      "type": "sse",
      "url": "http://localhost:3700/bench/sse"
    },
    "berth": {
      "type": "sse",
      "url": "http://localhost:3700/berth/sse"
    }
  }
}

This is not magic. You still have one config entry per server in Claude Code — the proxy doesn’t aggregate them into a single connection. What it does do is move process management out of the client and into a persistent service. Processes stay alive between sessions. Centralized config means you’re editing one JSON file instead of hunting through wherever Claude Code stores its MCP settings. The launchd service restarts the proxy if it crashes, and the proxy can restart child servers independently. That last part matters — a crashing Grafana MCP server doesn’t take down Bench and Binnacle with it.

What this doesn’t solve is the tool surface problem. All 22 servers are running all the time, and their full tool lists are in context whether you’re using them or not. That’s the thing I actually want to fix, and mcp-proxy doesn’t touch it. Lazy loading — spawning a server only when you call one of its tools — isn’t a feature in mcp-proxy. Neither is aggregation into a single Claude Code entry.

Those are the things MCP Hub is being built to handle. One Claude Code config entry that proxies everything. Servers that don’t spawn until their tools are called. Eventually, dynamic tool visibility when notifications/tools/list_changed gets proper support in Claude Code. The proxy solves process management. The hub solves context efficiency.

For now: if you’re running more than a handful of MCP servers and you’re not using a proxy, you’re managing processes by hand and paying the startup cost every session. mcp-proxy is a fifteen-minute fix for that specific problem. The rest comes later.

← back to writing