Architecture
Overview
mcpzip is a dual client/server proxy. On the downstream side, it acts as an MCP server (stdio) that Claude connects to. On the upstream side, it acts as an MCP client connecting to your real MCP servers.
┌──────────────────────────────── ─────────┐
│ mcpzip proxy │
│ │
Claude ──stdio──> │ MCP Server ──> ProxyServer │
│ │ │
│ ┌─────────┼──────────┐ │
│ │ │ │ │
│ search_tools describe execute │
│ │ │ │ │
│ v v v │
│ Searcher Catalog Manager │
│ (kw+LLM) (cached) (pool) │
│ │ │
└──────────────────────────────┼──────────┘
│
┌────────────────────┼────────┐
│ │ │
stdio/http/sse stdio/http sse
│ │ │
Server A Server B Server C
Components
MCP Server (src/mcp/server.rs)
NDJSON-over-stdio server implementing the MCP protocol. Handles:
initialize/initializedhandshaketools/list— returns the 3 meta-tool definitionstools/call— dispatches to ProxyServer handlers
ProxyServer (src/proxy/)
The core logic that implements the 3 meta-tools:
handle_search_tools— delegates to Searcher, formats resultshandle_describe_tool— looks up tool in Catalog, returns full schemahandle_execute_tool— resolves server from prefixed name, calls via Manager
Also handles admin tools:
proxy_status— returns tool count and server namesproxy_refresh— triggers a catalog refresh
Catalog (src/catalog/)
Maintains a cached index of all tools from all upstream servers.
- Disk cache (
~/.config/compressed-mcp-proxy/cache/tools.json) — persisted between restarts - Background refresh — on startup, serves from cache immediately while connecting to upstream servers in the background
- Merge on refresh — if a server fails to connect, keeps its cached tools rather than dropping them
Tool names are prefixed with the server name: slack__send_message, todoist__create_task. The __ separator is used to route execute_tool calls to the correct upstream server.
Searcher (src/search/)
Two-tier search engine:
- KeywordSearcher — tokenize query, score against tool metadata
- GeminiSearcher (optional) — send compact tool catalog + query to Gemini, get ranked results
- OrchestratedSearcher — runs both, merges and deduplicates
All results go through a QueryCache that normalizes queries for cache hits.
Manager (src/transport/manager.rs)
Connection pool for upstream MCP servers:
- Lazy connections — servers connect on first use
- Idle timeout — disconnects servers after configurable idle period
- Concurrent list_tools — connects to all servers in parallel with per-server timeout (30s)
- Reconnection — automatic reconnect on connection failure
Transport (src/transport/)
Three transport implementations:
| Transport | File | Protocol |
|---|---|---|
| stdio | stdio.rs | Spawn process, NDJSON over stdin/stdout |
| HTTP | http.rs | Streamable HTTP with SSE responses, OAuth 2.1 |
| SSE | sse.rs | Legacy Server-Sent Events |
Auth (src/auth/)
- TokenStore — persists OAuth tokens to disk (
~/.config/compressed-mcp-proxy/auth/) - OAuthHandler — implements OAuth 2.1 Authorization Code flow with PKCE
- Discovers authorization/token endpoints from resource metadata
- Opens browser for authorization
- Runs local callback server
- Reuses tokens from mcp-remote if available
Startup Flow
- Load config from disk
- Load cached tool catalog (instant — no network)
- Create transport manager, searcher, proxy server
- Start MCP server on stdio
- Spawn background task to refresh catalog:
- Connect to all upstream servers concurrently (30s timeout per server)
- Merge new tools with cached tools
- Persist updated catalog to disk
- Begin serving requests immediately (from cache)
Tool Name Convention
Tools are namespaced by server: {server}__{tool}. The double underscore (__) separator is used because:
- Single underscore is common in tool names
- Easy to split on first occurrence
- Human-readable in search results
Examples:
slack__send_messagetodoist__create_taskgmail-personal__send_email