{
  "markdown": "# mcp-anything\n\n[![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)\n[![npm](https://img.shields.io/npm/v/mcp-anything)](https://www.npmjs.com/package/mcp-anything)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![Node >= 20](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](package.json)\n\n**One MCP server that can discover and call (almost) any MCP server in the world.**\n\n![mcp-anything demo: searching the MCP ecosystem from the terminal](docs/demo.gif)\n\n`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.\n\n```\nHost / LLM\n    │  (5 meta-tools, constant context cost)\n    ▼\nmcp-anything ──── local BM25 index ◄──┬── official MCP registry   (moderated, richest metadata)\n    │        (cross-source dedupe,    ├── PulseMCP    (~22k servers, stars & downloads)\n    │         popularity-boosted      ├── npm         (~67k packages tagged mcp)\n    │         ranking)                └── Glama       (~75k indexed servers)\n    │\n    │  security policy: SSRF guard · stdio allowlist · secrets injection · timeouts\n    ▼\ndownstream MCP servers   (streamable-http / sse / stdio via npx·uvx)\n```\n\n## Why\n\n- **Discovery**: thousands of MCP servers exist; your host only knows the ones you hand-configured.\n- **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.\n- **One config**: a single entry in your MCP client config instead of one per server.\n\n## Quickstart\n\n```bash\n# Run directly (Node >= 20):\nnpx mcp-anything sync     # first-time index download (~few seconds)\nnpx mcp-anything serve    # start the meta-MCP server on stdio\n```\n\n### Claude Code\n\n```bash\nclaude mcp add anything -- npx -y mcp-anything serve\n```\n\n### Claude Desktop / other hosts\n\n```json\n{\n  \"mcpServers\": {\n    \"anything\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"mcp-anything\", \"serve\"]\n    }\n  }\n}\n```\n\nThen 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.\n\n### HTTP mode & hosted discovery\n\n```bash\nmcp-anything serve --http                    # streamable HTTP on :8080 (POST /mcp)\nmcp-anything serve --http --discovery-only   # safe for public hosting: search/describe only\n```\n\nA 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.\n\n### CLI\n\n```bash\nmcp-anything sync             # refresh the registry index\nmcp-anything search \"weather\" # search the index from your terminal\nmcp-anything serve            # stdio MCP server (default command)\n```\n\n## The meta-tools\n\n| Tool | What it does |\n|---|---|\n| `search_mcp_servers` | Keyword search (BM25 + fuzzy) over the indexed registry. Returns candidates with a connectability verdict. |\n| `describe_mcp_server` | Full registry metadata: transports, packages, required env vars / secrets, policy verdict. |\n| `list_mcp_tools` | Connects live (per policy) and lists the server's actual tools with JSON schemas. |\n| `call_mcp_tool` | Executes one tool on a downstream server. Sessions are pooled and reused. |\n| `sync_registry` | Forces an index refresh (otherwise auto-refreshed on TTL expiry). |\n\n## Security model\n\nConnecting an LLM to arbitrary servers from a public registry is dangerous by default. `mcp-anything` ships with conservative defaults and makes every relaxation explicit:\n\n- **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).\n- **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.\n- **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).\n- **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).\n- **Limits**: connect/call timeouts, response-size truncation, bounded session pool.\n\n## Configuration\n\n`~/.config/mcp-anything/config.json` (or `--config <path>`, or `MCP_ANYTHING_CONFIG`):\n\n```json\n{\n  \"registryUrl\": \"https://registry.modelcontextprotocol.io\",\n  \"sources\": [\"official\"],\n  \"qualityFilter\": true,\n  \"cacheTtlHours\": 24,\n  \"maxServers\": 10000,\n  \"policy\": {\n    \"remote\": {\n      \"enabled\": true,\n      \"allowPrivateNetwork\": false,\n      \"headers\": {\n        \"io.github.example/github\": { \"Authorization\": \"Bearer ghp_...\" }\n      }\n    },\n    \"stdio\": {\n      \"enabled\": false,\n      \"allowPackages\": [\"@modelcontextprotocol/server-filesystem\"],\n      \"env\": {\n        \"io.github.example/postgres\": { \"DATABASE_URL\": \"postgres://...\" }\n      }\n    },\n    \"limits\": {\n      \"callTimeoutMs\": 60000,\n      \"connectTimeoutMs\": 20000,\n      \"maxSessions\": 8,\n      \"maxResultChars\": 100000\n    }\n  }\n}\n```\n\nEvery 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.\n\n### Index sources — going wide\n\n`sources` controls how much of the ecosystem gets indexed:\n\n| Source | Scale | What it adds |\n|---|---|---|\n| `official` *(default)* | thousands | Moderated entries with the richest metadata (transports, env vars, versions) |\n| `pulsemcp` | ~22k | Broad catalog + GitHub stars & download counts (feeds ranking) |\n| `npm` | ~67k tagged packages | The largest raw pool of stdio servers, with monthly downloads |\n| `glama` | ~75k | The widest index (best-effort adapter) |\n\n```json\n{ \"sources\": [\"official\", \"pulsemcp\", \"npm\"] }\n```\n\nEntries 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.\n\n## Design notes\n\n- **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).\n- **Sessions, not stateless calls.** MCP is session-oriented (initialize handshake, capability negotiation). Downstream connections are pooled and reused across calls with LRU eviction.\n- **Graceful degradation.** If the registry is unreachable, the last-synced cache keeps working.\n\n## Prior art & positioning\n\nThis 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.\n\n## Roadmap\n\n- Live health checks and result-quality signals in ranking\n- Per-tool (not just per-server) search by indexing `tools/list` of popular servers\n- OAuth flow passthrough for remote servers that require it\n- Container/Wasm sandboxing for stdio servers as an alternative to allowlisting\n- Multiple registries with federation and dedupe\n- Optional streamable-http serving mode (for shared/team deployment)\n\n## Development\n\n```bash\nnpm install\nnpm test              # unit + end-to-end (mock registry + real downstream MCP server)\nnpm run typecheck\nnpm run build\nnode scripts/smoke.mjs  # spawns the built CLI as a real stdio MCP server\n```\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md). Licensed [MIT](LICENSE).\n",
  "bytes": 9373,
  "sha": "54486a8743f02b41046ed494b7c820127b9d308a2874d7b11980f8d9fedef83f",
  "repo_slug": "dror-bengal/mcp-anything",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_dror_bengal_mcp_anything_45fe4cb9/readme"
}