MetaMCP

MetaMCP is a meta-MCP server that sits in front of N child MCP servers, collapsing hundreds of tools into 4 meta-tools (~1,000 schema tokens). Built to be composable, lazy, isolated, and fast.
It connects to local servers over stdio and remote servers over HTTP or SSE, auto-discovers configurations from your installed editors, classifies errors intelligently, and caches schemas to disk for near-instant cold starts.
How It Works
┌─── playwright (52 tools)
│
LLM ──► MetaMCP ────────┼─── fetch (3 tools)
(4 tools) │
├─── sqlite (6 tools)
│
└─── ... N more servers
Your LLM sees 4 tools. MetaMCP handles discovery, routing, connection lifecycle, and sandboxed execution across all child servers.
Documentation
Full documentation at metamcp.org.
Installation
npx @mentu/metamcp
npm install -g @mentu/metamcp
Auto-configure your MCP client (Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, and more):
npx @mentu/metamcp init
Note: MetaMCP optionally uses better-sqlite3 for semantic search (vector embeddings). This requires a C++ compiler for native compilation. If compilation fails, MetaMCP still works fully with keyword-only search. On macOS, run xcode-select --install if you see build errors. On Linux, install build-essential.
Quick Start
1. Create a .mcp.json in your project root:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
},
"sqlite": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sqlite", "/path/to/db"]
}
}
}
2. Run MetaMCP:
npx @mentu/metamcp --config .mcp.json
3. Connect your LLM. MetaMCP speaks MCP over stdio — point Claude Desktop, Claude Code, or any MCP client at it.
Tools
mcp_discover — Search & list
Search tool catalogs across all child servers. Without a query, returns server status and tool counts.
query | string | no | Search query for tools |
server | string | no | Filter to a specific server |
{ "query": "screenshot" }
mcp_provision — Intent-based routing
Describe what you need and MetaMCP resolves the right server. Searches local catalogs first, then the npm registry for installable MCP servers.
intent | string | yes | What capability you need |
context | string | no | Additional context for resolution |
autoProvision | boolean | no | Auto-provision if trusted (default: false) |
{ "intent": "I need to crawl a website and extract links" }
mcp_call — Forward to child server
Forward a tool call to a specific child server. Retries once on crash for vital servers.
server | string | yes | Target server name |
tool | string | yes | Tool name to call |
args | object | no | Arguments to pass to the tool |
{ "server": "playwright", "tool": "browser_navigate", "args": { "url": "https://example.com" } }
mcp_execute — Sandboxed code execution
Execute code in a V8 sandbox with access to all provisioned servers. Supports async/await, sleep(ms), and console.log. No access to process, require, fs, or the network.
code | string | yes | Code to execute |
{ "code": "const result = await servers.sqlite.call('query', { sql: 'SELECT count(*) FROM users' }); return result;" }
Configuration
MetaMCP reads .mcp.json — the same format used by Claude Desktop and Claude Code.
npx package:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
}
}
}
Local binary:
{
"mcpServers": {
"my-server": {
"command": "/usr/local/bin/my-mcp-server",
"args": ["--port", "8080"]
}
}
}
With environment variables:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_..."
}
}
}
}
Remote server (SSE):
{
"mcpServers": {
"remote-tools": {
"url": "https://mcp.example.com/sse",
"transportType": "sse",
"headers": {
"Authorization": "Bearer your-token"
}
}
}
}
Remote server (Streamable HTTP) with OAuth:
{
"mcpServers": {
"cloud-server": {
"url": "https://mcp.example.com/api",
"oauth": true
}
}
}
Server lifecycle (keep-alive with idle timeout):
{
"mcpServers": {
"database": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sqlite", "/path/to/db"],
"lifecycle": { "mode": "keep-alive", "idleTimeoutMs": 600000 }
},
"one-shot": {
"command": "/usr/local/bin/converter",
"lifecycle": "ephemeral"
}
}
}
MetaMCP supports three transport types: stdio (local process, default), http (Streamable HTTP), and sse (Server-Sent Events). Servers with a url field use HTTP by default. Set transportType to sse for SSE servers. OAuth triggers a browser-based authorization flow on first connect, with tokens persisted at ~/.metamcp/oauth/.
Servers can declare a lifecycle to control idle behavior. keep-alive servers are never evicted by the global idle timeout (optionally with a per-server idleTimeoutMs). ephemeral servers are torn down as soon as they go idle. Without a lifecycle declaration, servers follow the default pool timeout.
CLI Options
--config <path> | .mcp.json | Path to config file |
--max-connections <n> | 20 | Connection pool max size |
--idle-timeout <ms> | 300000 | Idle connection timeout (ms) |
--failure-threshold <n> | 5 | Circuit breaker consecutive failures |
--cooldown <ms> | 30000 | Circuit breaker cooldown (ms) |
--import | off | Import server configs from installed editors (Cursor, Claude, VS Code, Windsurf, Codex, OpenCode) |
--help | | Show help |
--version | | Show version |
Architecture
MetaMCP manages child server lifecycles with:
- Connection pool — bounded pool with LIFO idle list and configurable upper/lower bounds
- Lazy spawning — child servers start on first use, not at boot
- Circuit breaker — per-server failure tracking with automatic cooldown; errors are classified (
auth, offline, http, stdio-exit) so only transient failures trip the breaker
- Error classification — connection errors are categorized automatically; auth failures (401/403) are logged but never count toward circuit breaker thresholds
- Lifecycle declarations — servers declare
keep-alive or ephemeral behavior; keep-alive servers survive idle sweeps, ephemeral servers are torn down immediately
- Schema caching — tool schemas are persisted to
~/.metamcp/cache/ for faster cold starts; stale caches are refreshed transparently on reconnect
- Config import —
--import discovers MCP server configs from Cursor, Claude Desktop, Claude Code, VS Code, Windsurf, Codex, and OpenCode
- LIFO eviction — when the pool is full, the oldest idle connection is evicted first
- V8 sandbox —
mcp_execute runs in a locked-down vm.Context with frozen prototypes, no eval, no require, no network access
- Multi-transport — stdio for local servers, Streamable HTTP and SSE for remote servers, with OAuth support
- Trust policy — registry packages are evaluated before auto-provisioning
Contributing
See CONTRIBUTING.md for development setup, commit conventions, and PR guidelines.
Links
License
Apache-2.0