# mcp-anything [![CI](https://github.com/Dror-Bengal/mcp-anything/actions/workflows/ci.yml/badge.svg)](https://github.com/Dror-Bengal/mcp-anything/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/mcp-anything)](https://www.npmjs.com/package/mcp-anything) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Node >= 20](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](package.json) **One MCP server that can discover and call (almost) any MCP server in the world.** ![mcp-anything demo: searching the MCP ecosystem from the terminal](docs/demo.gif) `mcp-anything` is a *meta*-MCP server: instead of configuring dozens of MCP servers in your host (Claude Desktop, Claude Code, Cursor, ...), you configure exactly one. It indexes the [official MCP registry](https://registry.modelcontextprotocol.io) locally and exposes five small **meta-tools** that let the model search for servers, inspect them, and call their tools on the fly — without ever loading thousands of tool schemas into the context window. ``` Host / LLM │ (5 meta-tools, constant context cost) ▼ mcp-anything ──── local BM25 index ◄──┬── official MCP registry (moderated, richest metadata) │ (cross-source dedupe, ├── PulseMCP (~22k servers, stars & downloads) │ popularity-boosted ├── npm (~67k packages tagged mcp) │ ranking) └── Glama (~75k indexed servers) │ │ security policy: SSRF guard · stdio allowlist · secrets injection · timeouts ▼ downstream MCP servers (streamable-http / sse / stdio via npx·uvx) ``` ## Why - **Discovery**: thousands of MCP servers exist; your host only knows the ones you hand-configured. - **Context**: loading many servers burns your context window. Meta-tools keep the cost constant — the model searches for capabilities in two phases (search → inspect → call), the same pattern Anthropic's Tool Search uses. - **One config**: a single entry in your MCP client config instead of one per server. ## Quickstart ```bash # Run directly (Node >= 20): npx mcp-anything sync # first-time index download (~few seconds) npx mcp-anything serve # start the meta-MCP server on stdio ``` ### Claude Code ```bash claude mcp add anything -- npx -y mcp-anything serve ``` ### Claude Desktop / other hosts ```json { "mcpServers": { "anything": { "command": "npx", "args": ["-y", "mcp-anything", "serve"] } } } ``` Then just ask your model things like *"find an MCP server that can query Postgres and list its tools"* — it will use the meta-tools by itself. ### HTTP mode & hosted discovery ```bash mcp-anything serve --http # streamable HTTP on :8080 (POST /mcp) mcp-anything serve --http --discovery-only # safe for public hosting: search/describe only ``` A public instance with execution enabled would be an open proxy — **never host a full instance publicly**. The Dockerfile defaults to discovery-only for exactly this reason. A hosted discovery instance lets any MCP client search the ecosystem; actually connecting and calling tools is what the local install is for. ### CLI ```bash mcp-anything sync # refresh the registry index mcp-anything search "weather" # search the index from your terminal mcp-anything serve # stdio MCP server (default command) ``` ## The meta-tools | Tool | What it does | |---|---| | `search_mcp_servers` | Keyword search (BM25 + fuzzy) over the indexed registry. Returns candidates with a connectability verdict. | | `describe_mcp_server` | Full registry metadata: transports, packages, required env vars / secrets, policy verdict. | | `list_mcp_tools` | Connects live (per policy) and lists the server's actual tools with JSON schemas. | | `call_mcp_tool` | Executes one tool on a downstream server. Sessions are pooled and reused. | | `sync_registry` | Forces an index refresh (otherwise auto-refreshed on TTL expiry). | ## Security model Connecting an LLM to arbitrary servers from a public registry is dangerous by default. `mcp-anything` ships with conservative defaults and makes every relaxation explicit: - **Remote servers** (streamable-http / sse): allowed, **but** private/loopback/link-local addresses (including cloud metadata endpoints like `169.254.169.254`) and plain `http:` are blocked — an SSRF guard for registry entries that point into your network. Opt out with `remote.allowPrivateNetwork` (useful for local development only). - **Stdio servers** (spawning `npx` / `uvx` processes): **disabled by default.** Running a package from a public registry is arbitrary code execution on your machine. Enable it only with an explicit per-package allowlist. - **Secrets**: API keys are never indexed or exposed to the model. You map them per server in your config; they are injected at connect time (headers for remote, env for stdio). - **Untrusted output**: results and tool descriptions from downstream servers are labeled as third-party data so the model treats them as data, not instructions. This *reduces* prompt-injection risk; it does not eliminate it — see [SECURITY.md](SECURITY.md). - **Limits**: connect/call timeouts, response-size truncation, bounded session pool. ## Configuration `~/.config/mcp-anything/config.json` (or `--config `, or `MCP_ANYTHING_CONFIG`): ```json { "registryUrl": "https://registry.modelcontextprotocol.io", "sources": ["official"], "qualityFilter": true, "cacheTtlHours": 24, "maxServers": 10000, "policy": { "remote": { "enabled": true, "allowPrivateNetwork": false, "headers": { "io.github.example/github": { "Authorization": "Bearer ghp_..." } } }, "stdio": { "enabled": false, "allowPackages": ["@modelcontextprotocol/server-filesystem"], "env": { "io.github.example/postgres": { "DATABASE_URL": "postgres://..." } } }, "limits": { "callTimeoutMs": 60000, "connectTimeoutMs": 20000, "maxSessions": 8, "maxResultChars": 100000 } } } ``` Every field is optional; the defaults above (minus the example headers/env) are what you get with no config at all. `registryUrl` accepts any registry implementing the official REST API — including a private/self-hosted one. ### Index sources — going wide `sources` controls how much of the ecosystem gets indexed: | Source | Scale | What it adds | |---|---|---| | `official` *(default)* | thousands | Moderated entries with the richest metadata (transports, env vars, versions) | | `pulsemcp` | ~22k | Broad catalog + GitHub stars & download counts (feeds ranking) | | `npm` | ~67k tagged packages | The largest raw pool of stdio servers, with monthly downloads | | `glama` | ~75k | The widest index (best-effort adapter) | ```json { "sources": ["official", "pulsemcp", "npm"] } ``` Entries appearing in several catalogs are **deduplicated** by normalized repository URL and package identifier; the most-trusted source wins the identity, metadata is backfilled from the others, and stars/downloads accumulate. Ranking then combines BM25 relevance with a log-scaled popularity boost (and a bonus for official-registry entries), so `search_mcp_servers` surfaces the maintained implementation of a capability rather than the hundredth abandoned clone. `qualityFilter` (default on) drops entries with no way to connect and no usable description — with wide sources, *more* is only better if the junk stays out of the top-5. A failed source degrades gracefully: the sync keeps whatever the other sources returned and reports the failure. ## Design notes - **Lexical search, not embeddings.** Fully local, zero API cost, no index build step — and for tool discovery, keyword search with fuzzy matching performs comparably in practice (Anthropic's Tool Search made the same call with BM25/regex). - **Sessions, not stateless calls.** MCP is session-oriented (initialize handshake, capability negotiation). Downstream connections are pooled and reused across calls with LRU eviction. - **Graceful degradation.** If the registry is unreachable, the last-synced cache keeps working. ## Prior art & positioning This space is active: [MetaMCP](https://github.com/metatool-ai/metamcp) and other gateways aggregate *servers you configure*; Composio's Rube routes to *its own curated catalog*; hosts are growing native tool-search. `mcp-anything`'s niche is the open combination: **the public registry as the catalog, a local-first single binary, and an explicit security policy** — no cloud account, no curation lock-in, self-hostable against a private registry. ## Roadmap - Live health checks and result-quality signals in ranking - Per-tool (not just per-server) search by indexing `tools/list` of popular servers - OAuth flow passthrough for remote servers that require it - Container/Wasm sandboxing for stdio servers as an alternative to allowlisting - Multiple registries with federation and dedupe - Optional streamable-http serving mode (for shared/team deployment) ## Development ```bash npm install npm test # unit + end-to-end (mock registry + real downstream MCP server) npm run typecheck npm run build node scripts/smoke.mjs # spawns the built CLI as a real stdio MCP server ``` See [CONTRIBUTING.md](CONTRIBUTING.md). Licensed [MIT](LICENSE).