{
  "markdown": "# MCP Proxy Gateway\n\nA context-aware MCP proxy that reduces token usage by exposing only 3 tools (`mcp_search`, `mcp_call`, `mcp_schema`) to LLMs instead of the full catalog.\n\n## Why This Exists\n\nWhen you connect multiple MCP servers to an LLM, every tool from every server is listed in the LLM's context window. For a typical workspace with 50-100 tools across multiple MCP servers, that's thousands of tokens of schema documentation on every request.\n\nMCP Proxy Gateway sits between your LLM and your MCP servers, offering:\n\n- **JIT tool loading** — tools from upstream servers are discovered once at startup, then tools are called on-demand. Clients never see the full catalog.\n- **Intelligent search** — fast lexical (BM25) search to find the right tool for a query. Tool tokens are pre-computed at startup for zero-overhead per-query scoring.\n- **Token savings** — LLMs only see 3 tool schemas (search, call, schema) instead of 50+. Typical savings: 20-40% per turn for tool-heavy workflows.\n- **Zero native dependencies** — pure JavaScript, no native modules, no model downloads, no supply chain risk from ML packages.\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                         Your LLM                                │\n│   (sees only: mcp_search, mcp_call, mcp_schema)                │\n└────────────────────┬────────────────────────────────────────────┘\n                     │\n         ┌───────────▼──────────────┐\n         │   MCP Proxy Gateway      │\n         │ ┌──────────────────────┐ │\n         │ │ Tool Registry        │ │\n         │ │ (BM25 lexical)       │ │\n         │ └──────────────────────┘ │\n         │ ┌──────────────────────┐ │\n         │ │ Connector Manager    │ │\n         │ │ (Idle timeout reap)  │ │\n         │ └──────────────────────┘ │\n         └────────────┬─────────────┘\n                      │\n        ┌─────────────┼─────────────┐\n        │             │             │\n        ▼             ▼             ▼\n   ┌─────────┐  ┌─────────┐  ┌─────────┐\n   │Google   │  │MailerLite│ │Your Svc │\n   │Gmail    │  │ Campaigns│ │ Custom  │\n   │Calendar │  │          │ │ Tools   │\n   │Drive    │  │          │ │         │\n   └─────────┘  └─────────┘  └─────────┘\n```\n\n## Prerequisites\n\n- Node.js 18+ with npm or pnpm\n- One or more MCP servers to proxy (stdio or HTTP)\n\n## Installation\n\n### From Source\n\n```bash\ngit clone https://github.com/steveweltman/4q-tokenz.git\ncd 4q-tokenz\n\npnpm install\npnpm build\n\n# Install to ~/.local/bin and configure\n./install.sh\n```\n\n### As a Dependency\n\n```bash\nnpm install -g 4q-tokenz\n```\n\n## Getting Started: Google Workspace Example\n\nHere's a concrete walkthrough to connect Google Workspace (Gmail, Calendar, Drive) to your LLM through the proxy:\n\n### Step 1: Choose or Build an MCP Server for Google\n\nYou need an MCP server that wraps Google APIs. Options:\n\n- **@antidrift/mcp-google** (recommended) — A collection of MCP server implementations for Google Workspace (Gmail, Calendar, Drive, Docs, Sheets). Works out of the box with this proxy.\n  ```bash\n  npm install @antidrift/mcp-google\n  # or\n  npx @antidrift/mcp-google --help\n  ```\n\n- **@modelcontextprotocol/server-gmail** — Gmail-only, official MCP server\n- **Build your own** — See the [MCP spec](https://modelcontextprotocol.io/) to wrap your own APIs\n\n### Step 2: Set Up Google OAuth\n\n1. Go to [Google Cloud Console](https://console.cloud.google.com/)\n2. Create a new project or select an existing one\n3. Enable these APIs:\n   - Gmail API\n   - Google Calendar API\n   - Google Drive API\n4. Create an OAuth 2.0 credential (type: Desktop application)\n5. Download the credential JSON\n6. Run the Google MCP server once to generate `token.json`:\n   ```bash\n   GOOGLE_CREDENTIAL_FILE=~/Downloads/credentials.json \\\n   npx @antidrift/mcp-google\n   ```\n   This opens a browser for you to authorize. Once done, it saves `token.json` locally.\n\n### Step 3: Configure the Proxy\n\nCreate `~/.config/4q-tokens/config.json`:\n\n```json\n{\n  \"upstreams\": [\n    {\n      \"name\": \"google-workspace\",\n      \"transport\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"@antidrift/mcp-google\"],\n      \"env\": {\n        \"GOOGLE_TOKEN_FILE\": \"~/.local/share/google-mcp/token.json\",\n        \"GOOGLE_CONNECTORS\": \"gmail,calendar,drive\"\n      }\n    }\n  ],\n  \"searchLimit\": 5,\n  \"callItemLimit\": 30,\n  \"maxTextLength\": 800,\n  \"maxOutputTokens\": 10000,\n  \"idleTimeoutMs\": 600000\n}\n```\n\n### Step 4: Start the Proxy\n\n```bash\nmcp-proxy\n# Or via systemd if installed:\nsystemctl --user start mcp-proxy\n```\n\n### Step 5: Connect Your LLM\n\nConfigure your LLM to use `http://127.0.0.1:9200/mcp` as its MCP server. It will see:\n- `mcp_search` — find tools by natural language\n- `mcp_call` — invoke a tool\n- `mcp_schema` — see tool details\n\nExample query:\n```\nmcp_search(\"send an email\")\n# Returns: google_send_email (Gmail)\n\nmcp_call(ref=\"google_send_email\", args={\"to\": \"user@example.com\", \"subject\": \"Hello\", \"body\": \"Test\"})\n```\n\n## Configuration\n\n### Quick Start with Environment Variables\n\n```bash\nexport MCP_PROXY_UPSTREAMS='[\n  {\n    \"name\": \"google\",\n    \"transport\": \"stdio\",\n    \"command\": \"node\",\n    \"args\": [\"/path/to/google/server.mjs\"],\n    \"env\": {\n      \"GOOGLE_TOKEN_FILE\": \"token.json\"\n    }\n  }\n]'\n\nexport MCP_PROXY_SINGLETON_PORT=9200\nexport MCP_PROXY_DASHBOARD_PORT=9100\n\nnode dist/index.js\n```\n\n### Config File (Recommended)\n\nCreate `~/.config/4q-tokens/config.json`:\n\n```json\n{\n  \"upstreams\": [\n    {\n      \"name\": \"google-workspace\",\n      \"transport\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"@antidrift/mcp-google\"],\n      \"env\": {\n        \"GOOGLE_TOKEN_FILE\": \"token.json\",\n        \"GOOGLE_CONNECTORS\": \"gmail,calendar,drive\"\n      }\n    },\n    {\n      \"name\": \"external-api\",\n      \"transport\": \"http\",\n      \"url\": \"https://mcp.example.com/\",\n      \"auth\": {\n        \"apiKey\": \"MY_API_KEY_ENV_VAR\"\n      }\n    }\n  ],\n  \"searchLimit\": 3,\n  \"callItemLimit\": 20,\n  \"maxTextLength\": 500,\n  \"maxOutputTokens\": 8000,\n  \"idleTimeoutMs\": 300000\n}\n```\n\nThen run:\n\n```bash\nnode dist/index.js\n```\n\nThe proxy will load the config from `~/.config/4q-tokens/config.json` if it exists, otherwise fall back to the `MCP_PROXY_UPSTREAMS` environment variable.\n\n### Configuration Reference\n\n#### Upstream Server Config\n\n```json\n{\n  \"name\": \"unique-id\",\n  \"transport\": \"stdio\" | \"http\",\n  \n  // For stdio transport:\n  \"command\": \"node\",\n  \"args\": [\"path/to/server.mjs\"],\n  \"cwd\": \"/working/dir\",  // optional\n  \"env\": { \"KEY\": \"value\" },  // optional\n  \n  // For http transport:\n  \"url\": \"https://example.com/mcp\",\n  \"auth\": {\n    \"apiKey\": \"ENV_VAR_NAME\"  // reads from process.env[ENV_VAR_NAME]\n  }\n}\n```\n\n#### Proxy Options\n\n| Option | Default | Description |\n|--------|---------|-------------|\n| `searchLimit` | 3 | Max tools returned by mcp_search |\n| `callItemLimit` | 20 | Max items in mcp_call response |\n| `maxTextLength` | 500 | Truncate text fields to N chars (detail=false: 500, detail=true: 1500) |\n| `maxOutputTokens` | 8000 | Hard cap on response size |\n| `idleTimeoutMs` | 300000 | Disconnect upstream servers after N ms of inactivity (0 = disabled) |\n\nEnvironment variable overrides:\n\n```bash\nexport MCP_PROXY_SEARCH_LIMIT=5\nexport MCP_PROXY_CALL_ITEM_LIMIT=30\nexport MCP_PROXY_MAX_TEXT_LENGTH=800\nexport MCP_PROXY_MAX_OUTPUT_TOKENS=10000\nexport MCP_PROXY_IDLE_TIMEOUT_MS=600000\n```\n\n## Running\n\n### Standalone (Stdio Transport)\n\n```bash\nnode dist/index.js\n```\n\nThe proxy connects via stdio to your LLM. Use it with Claude or other MCP clients.\n\n### As a Systemd User Service\n\nThe install script can set this up for you (see below), or manually:\n\n1. Create `~/.config/systemd/user/mcp-proxy.service`:\n\n```ini\n[Unit]\nDescription=MCP Proxy Gateway\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=%h/.local/bin/mcp-proxy\nRestart=on-failure\nRestartSec=5s\nEnvironment=\"PATH=%h/.local/bin:/usr/local/bin:/usr/bin\"\n\n[Install]\nWantedBy=default.target\n```\n\n2. Enable and start:\n\n```bash\nsystemctl --user daemon-reload\nsystemctl --user enable mcp-proxy\nsystemctl --user start mcp-proxy\n```\n\n3. View logs:\n\n```bash\njournalctl --user -u mcp-proxy -f\n```\n\n### Prometheus Metrics (Port 9100)\n\nThe dashboard exposes a Prometheus-compatible `/metrics` endpoint:\n\n```bash\ncurl http://localhost:9100/metrics\n```\n\nMetrics exposed:\n| Metric | Type | Description |\n|--------|------|-------------|\n| `mcp_proxy_uptime_seconds` | gauge | Seconds since process started |\n| `mcp_proxy_registered_tools` | gauge | Tools in the registry |\n| `mcp_proxy_upstream_up` | gauge | 1 if upstream is connected/idle, 0 if error |\n| `mcp_proxy_upstream_tools` | gauge | Tools discovered per upstream |\n| `mcp_proxy_calls_total` | counter | Calls by tool, provider, status |\n| `mcp_proxy_call_duration_ms_total` | counter | Cumulative call duration (ms) |\n| `mcp_proxy_output_bytes_total` | counter | Cumulative output bytes |\n\nAdd a scrape job in your Prometheus/Alloy config:\n\n```yaml\nscrape_configs:\n  - job_name: mcp-proxy\n    static_configs:\n      - targets: ['localhost:9100']\n    metrics_path: /metrics\n```\n\n### HTTP Server (Port 9200)\n\nThe proxy always starts an HTTP transport on port 9200 by default. Set `MCP_PROXY_SINGLETON_PORT` to use a different port. This allows multiple clients to connect to a single proxy instance.\n\n```bash\nexport MCP_PROXY_SINGLETON_PORT=9200\nnode dist/index.js &\n\n# From another process:\ncurl -X POST http://127.0.0.1:9200/mcp -H \"Content-Type: application/json\" \\\n  -d '{\"jsonrpc\": \"2.0\", \"method\": \"tools/call\", \"params\": {...}}'\n```\n\n## Troubleshooting\n\n### Upstream MCP Server Won't Connect\n\nCheck the server logs in the dashboard (port 9100 by default) or daemon logs:\n\n```bash\njournalctl --user -u mcp-proxy -e\n```\n\nThe proxy logs:\n- Tool discovery on startup\n- Connection failures with error messages\n- Upstream stderr (piped from stdio servers)\n\n### Proxy Crashes or Freezes\n\nThe proxy has comprehensive error handling to gracefully degrade on upstream failures:\n\n- If an upstream tool call fails, the error is logged and returned to the client\n- If all upstreams fail at discovery, startup fails with `NO_UPSTREAMS`\n\nFor unhandled errors, check:\n\n```bash\njournalctl --user -u mcp-proxy -n 50  # Last 50 lines\n```\n\n### Tool Returns No Data\n\nWhen a tool returns `null` or malformed data, the output shaper handles it gracefully:\n\n- Null results return `[]`\n- Strings are wrapped as `{value: string}`\n- CSV is auto-parsed if it looks like tabular data\n- Raw binary content (images, files) is preserved via `_rawContent`\n\nIf a tool response looks truncated, retry with `detail=true` in mcp_call to disable output shaping:\n\n```\nmcp_call(ref=\"google_send_email\", args={...}, detail=true)\n```\n\n## Security & Networking\n\nThe proxy binds to **`127.0.0.1` only** for security — it's not accessible from the network by default. To access remotely:\n\n- **Same machine**: Connect locally on `127.0.0.1:9200`\n- **Remote access**: Use Tailscale, SSH forwarding, or a VPN tunnel\n  ```bash\n  ssh -L 9200:127.0.0.1:9200 user@remote-host\n  ```\n- **Systemd service**: Access is local by default; no firewall rule needed\n\n## Known Limitations\n\n- **No automated tests** — this is production-quality code used daily, but test suite is not included\n\n## Changelog\n\n### v1.21.2\n- Fix incomplete v1.21.1 patch: the `hono` pnpm override (`>=4.12.25`) still resolved to a vulnerable 4.12.x release; tightened to `>=4.12.34`\n- Add pnpm overrides for `@hono/node-server >=1.19.15`, `fast-uri >=3.1.5`, `ip-address >=10.3.1`, `body-parser >=2.3.0` — all transitive via `@modelcontextprotocol/sdk`, resolves 3 high and 6 medium/low CVEs (DoS, ReDoS, SSRF-adjacent parsing bugs)\n- Bump `@modelcontextprotocol/sdk` from `^1.26.0` to `^1.30.0`\n- Verified clean against `pnpm audit` and OSV.dev for all resolved dependency versions\n\n### v1.21.1\n- Attempted hono dependency patch (`pnpm override: hono >=4.12.25`) — override was too loose and did not actually clear the vulnerable range; fully resolved in v1.21.2 above\n\n### v1.21.0\n- `detail=true` no longer truncates text fields in tool output\n\n### v1.20.0\n- Per-agent lane isolation: URL routing at `/mcp/{agentId}`, filtered tool registry views, `MCP_PROXY_AGENTS` config\n\n### v1.19.0\n- Add Prometheus `/metrics` endpoint on the dashboard port (9100 by default)\n- Exposes: `mcp_proxy_uptime_seconds`, `mcp_proxy_registered_tools`, `mcp_proxy_upstream_up`, `mcp_proxy_upstream_tools`, `mcp_proxy_calls_total`, `mcp_proxy_call_duration_ms_total`, `mcp_proxy_output_bytes_total`\n- Counters persist for the lifetime of the process; scrape with Prometheus/Alloy and visualize in Grafana\n\n### v1.18.0\n- Drop `@xenova/transformers` entirely — eliminates the protobufjs/ONNX runtime supply chain\n- Switch to pure lexical (BM25) search; tool tokens pre-computed at startup, zero per-query overhead\n- No change to mcp_search quality for English-language tool catalogs\n- Removes ~90MB model download on first run and all native module requirements\n\n### v1.17.1\n- Pre-compute tool token sets at registry build time; lexical scoring now reads the cache instead of re-tokenizing on every query\n- Add LRU cache for query embeddings (embeddings removed entirely in v1.18.0)\n\n### v1.17.0\n- Switch to lighter English-optimized embedding model, reducing cold-start download by ~380MB (embeddings removed entirely in v1.18.0)\n\n### v1.16.0\n- Update `@modelcontextprotocol/sdk` from `~1.22.0` to `^1.26.0` — resolves 3 high-severity CVEs: ReDoS, cross-client data leak, DNS rebinding\n- Add pnpm override: `protobufjs >=7.5.8` — resolves critical arbitrary code execution and multiple high CVEs in `@xenova/transformers` transitive dependency chain\n- Add pnpm override: `qs >=6.15.2` — resolves moderate DoS vulnerability in `express` transitive dependency\n\n## Attribution\n\nMCP Proxy Gateway is a fork of [@arvoretech/mcp-proxy](https://github.com/arvoreeducacao/arvore-mcp-servers), originally created by **João Augusto** and **Árvore Educação**.\n\nForked and extended with:\n- Singleton mode for HTTP bridge\n- Idle server reaping\n- Comprehensive error handling\n- Config file support\n- Systemd integration\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n",
  "bytes": 14109,
  "sha": "0ca5d8e6e560d5133247373f73b7e4a8b01da7756f26e582d49a709ee5f416a2",
  "repo_slug": "steveweltman/4q-tokens",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_steveweltman_4q_tokenz_063d9f8b/readme"
}