One Endpoint, Every Client: Running 10 MCP Servers Without Losing Your Mind

Algis Dumbris • 2026/04/28

TL;DR

I use five MCP clients daily — Claude Desktop, Claude Code, Cursor, Codex CLI, Gemini CLI — and ten upstream servers across them. Keeping five config files in sync was eating more of my morning than the servers were saving me. I now run one mcpproxy serve on localhost:8080/mcp, point every client at it, and let mcpproxy upstream import read my existing per-client configs into a single registry. Four imports, three minutes, one place to edit forever. This is the N×M config problem the MCP spec quietly handed us, and the fix is a single endpoint.

The N×M Config Problem

MCP is a wire protocol between an agent and its tools, but nobody owns the wiring. Every client — Claude Desktop, Claude Code, Cursor, Codex CLI, Gemini CLI — ships its own config format, its own config location, and its own idea of what a server entry looks like. Add a server and you are editing up to five files:

ClientConfig pathFormat
Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.jsonJSON
Claude Code.claude/settings.json or ~/.claude.jsonJSON
Cursor IDE~/.cursor/mcp.jsonJSON
Codex CLI~/.codex/config.tomlTOML
Gemini CLI~/.gemini/settings.jsonJSON

The formats overlap but are not interchangeable. Claude Desktop wants mcpServers; Codex uses [mcp_servers.name] TOML tables; Gemini nests the same data under tools.mcp. Rotate an API key in one place and you have either rotated it once and broken four clients, or you are now opening five editors. This is the N×M problem in the most literal sense — N clients times M servers, with the product growing every time you install a new IDE.

I hit the wall at ten servers. GitHub, GitLab, Slack, filesystem, git, sqlite, fetch, Brave search, Linear, Sentry — all perfectly reasonable to run, all a nightmare to keep consistent when the same GITHUB_TOKEN has to live in five files. The first time I paged through my dotfiles looking for “where did I put the Slack bot token” I knew the per-client approach was not going to scale.

The Fix: One Endpoint

The structural fix is to stop thinking about it as “configure server X on client Y” and start thinking about it as “configure server X once, expose one endpoint, point every client at it.” That is what a gateway does. For MCP, that endpoint is http://localhost:8080/mcp, served by a local proxy process that holds the authoritative list of upstream servers and brokers every client’s tool calls.

Once that is running, every client gets the same one-line config:

{
  "mcpServers": {
    "mcpproxy": {
      "type": "http",
      "url": "http://localhost:8080/mcp"
    }
  }
}

Codex and Gemini get the analogous TOML / JSON snippet. The rest of the config work — tokens, command paths, environment variables, per-server flags — lives in one place: the proxy’s registry.

The Three-Minute Walkthrough

The trick that makes this painless is that you do not have to rewrite anything. mcpproxy upstream import auto-detects the format of any of the five client configs and ingests its servers into the proxy’s registry. If you have been editing those files for months, that history is your starting data.

1. Install the proxy

# macOS
brew install smart-mcp-proxy/tap/mcpproxy

# cross-platform (Go toolchain)
go install github.com/smart-mcp-proxy/mcpproxy-go/cmd/mcpproxy@latest

The tray app on macOS auto-starts mcpproxy serve; on other platforms run it once and leave it in a terminal or a launchd/systemd unit. Default listen is http://localhost:8080.

2. Import every client config you already have

# dry-run first — shows what will be registered without changing anything
mcpproxy upstream import --dry-run \
  "$HOME/Library/Application Support/Claude/claude_desktop_config.json"

# then run for real
mcpproxy upstream import \
  "$HOME/Library/Application Support/Claude/claude_desktop_config.json"
mcpproxy upstream import ~/.cursor/mcp.json
mcpproxy upstream import ~/.codex/config.toml
mcpproxy upstream import ~/.gemini/settings.json

Run them in sequence. Duplicates across files — the same GitHub server entry in both Cursor and Codex, for example — merge cleanly; the proxy keeps one canonical record and preserves environment variables and command paths. Imported servers are quarantined by default, which means the proxy knows about them but will not expose their tools until you explicitly approve each one. That is the right default when you are bulk-importing configs assembled over months — you can review the list before anything goes live.

List what landed:

mcpproxy upstream list

Approve the ones you trust. Delete the ones you forgot you had installed.

3. Point every client at the proxy

Replace each client’s per-server mcpServers section with a single mcpproxy entry pointing at http://localhost:8080/mcp. One line per client, five clients, done. Claude Code has a one-liner CLI:

claude mcp add --transport http mcpproxy http://localhost:8080/mcp

Cursor, Claude Desktop, Codex, and Gemini each take the JSON/TOML snippet above. Restart the client. Ask the agent “what tools do you have?” and watch it discover the whole upstream catalog through the proxy.

4. Verify

mcpproxy doctor
mcpproxy upstream list
mcpproxy tools list

If doctor is clean and your client sees tools, the migration is complete. The five files you used to edit are now one registry, and every future server you add lands in one place.

What This Buys You On Day Two

The import is the first ten minutes; the payoff is every day after. Four concrete things change:

Adding a server is one command. mcpproxy upstream add <name> --command <cmd> or the Web UI at http://localhost:8080/ui/. Every client picks it up on the next tools/list. No restart of Claude Desktop, no new entry in Cursor settings.

Rotating a credential is one edit. The GitHub token lives in the proxy’s server record, referenced as ${keyring:github_token} via the system keyring. Rotate it once; the next tool call uses the new value.

Per-client tool budgets stop mattering. Cursor caps tool count at 40; GitHub Copilot at 128. With the proxy, each client sees one tool — retrieve_tools — and fetches the relevant schemas on demand. Ten servers, 150 tools, still one slot on the client side. (That context math is the subject of the previous post.)

Security posture travels with the gateway. Quarantine, activity logs, tool-description inspection — these are proxy-level features, and they apply uniformly regardless of which client is calling. You stop auditing five tool surfaces and start auditing one.

What I Am Not Claiming

Try It

If you already run MCP servers in more than one client, the import flow is a lossless way to find out what your actual footprint looks like:

mcpproxy upstream import --dry-run ~/.cursor/mcp.json

No changes. Just a diff of what the proxy would register. Most people who run this on their own machine discover they have been maintaining two or three copies of the same server record across clients. Deduplicating is a good enough reason to start.

And if you run the full import, open a GitHub issue with your before/after count — how many config files you used to touch, how many you touch now. The scaling problem is quiet until it isn’t, and the honest readings are what get us to a spec-level fix.


Further reading