This project has been archived by its maintainers, and is no longer receiving any updates.
MCP Nexus
The discovery and routing layer that keeps MCP servers out of your context window until you actually need them.
The problem
The Model Context Protocol lets an LLM talk to any number of servers — GitHub, Slack, Postgres, your internal tools. The catch: most clients load every tool definition from every configured server at startup. Six servers can mean 70+ tool schemas and thousands of tokens spent before the conversation even starts, most of which the model never touches in a given session.
What MCP Nexus does
MCP Nexus sits in front of your MCP servers as a thin discovery layer. Instead of loading everything up front, the LLM asks for what it needs — by server or by tool — and MCP Nexus resolves the request and connects on demand. You keep your existing MCP servers unmodified; MCP Nexus only changes how (and when) their tools reach the model's context.
It works at the protocol level, so it doesn't care what's actually behind a server — GitHub, Slack, a database, or a fully custom MCP server you built yourself over a proprietary application (CAD software, a game engine editor, an internal build system). If it speaks MCP, MCP Nexus can discover it and route to it. The bigger and more varied your server catalog gets, the more it pays off — see At large scale in Benchmarks for real numbers at 300 tools.
Two modes cover the two ways teams actually want this to work:
| Discovery Mode | Dynamic Mode | |
|---|---|---|
| Granularity | Whole server | Individual tool |
| Tools always in context | 4 (mcpd_find, mcpd_list, mcpd_connect, mcpd_get_schema) |
2 (find_tools, get_tool_schema) |
| Best for | "Connect me to GitHub" style workflows | Cherry-picking one tool from many servers |
| After resolution | LLM talks to the server directly — MCP Nexus exits the data path | MCP Nexus lazy-connects and stays in the loop per tool call |
v1.0.0 adds Lazy Schema Loading: Discovery Mode can hand back a stub tool list (names only, no schemas) and fetch a single tool's full schema only when it's about to be called. Real, reproducible numbers (not rough estimates) are in Benchmarks below.
How it works
Discovery Mode — server-level selection
Step 1 LLM -> mcpd_find("github issues")
MCP Nexus searches the registry
returns: { id: "github", tools: ["create_issue", "search_repos", ...] }
Step 2 LLM -> mcpd_connect("github")
MCP Nexus starts the GitHub MCP server
returns: 20 tools now available as github__create_issue, etc.
Step 3 LLM -> github__create_issue({ title: "...", body: "..." })
MCP Nexus proxies to the GitHub MCP server, returns the result
Real token counts for this flow are in Benchmarks — see "Discovery Mode (before connect)" and "(after connect)".
Lazy Schema Loading
No proxy, no changes to the target MCP server required:
mcpd_find("github")→ choose a servermcpd_connect("github", lazy_mode=true)→ get a stub list (tool names only, no schemas)mcpd_get_schema("github", "create_issue")→ fetch one full schemagithub__create_issue(...)→ direct call, as always
Pass --sync-on-start so the registry has schemas cached ahead of time via nexus-sync.
Dynamic Mode — tool-level selection
Step 1 LLM -> find_tools("create issue, post slack message")
MCP Nexus searches the tool index across all servers
returns: create_issue (github), post_message (slack)
both tools added to tools/list
Step 2 LLM -> create_issue({ title: "Bug #42" })
MCP Nexus lazy-connects to the GitHub MCP server
executes create_issue, returns the result
Step 3 LLM -> post_message({ channel: "#eng", text: "Done" })
MCP Nexus lazy-connects to the Slack MCP server
executes post_message, returns the result
Real token counts for this flow are in Benchmarks — see "Dynamic Mode (before find)" and "(after find_tools)".
Installation
# Core — keyword search, stdio transport
pip install mcpnexus
# With HTTP and SSE transport (remote MCP servers)
pip install mcpnexus[http]
# With semantic search (sentence-transformers)
pip install mcpnexus[embeddings]
# With exact token counting for nexus-benchmark (tiktoken)
pip install mcpnexus[benchmark]
# Full installation
pip install mcpnexus[all]
# Development
pip install mcpnexus[dev]
Quick start
1. Build your registry
The registry is a lightweight JSON catalog of your MCP servers and their tool summaries. Build it from your MCP client's config (e.g. Cursor: ~/.cursor/mcp.json, Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json):
nexus-sync --config /path/to/your/mcp-config.json --output registry/mcpd-registry.json
2. Point your MCP client at MCP Nexus
Discovery Mode (4 tools, connect to one server at a time):
{
"mcpServers": {
"nexus-server": {
"command": "nexus-server",
"args": ["--registry", "/path/to/mcpd-registry.json", "--sync-on-start"]
}
}
}
Dynamic Mode (2 tools, cherry-pick tools across all servers):
{
"mcpServers": {
"nexus-gateway": {
"command": "nexus-gateway",
"args": ["--registry", "/path/to/mcpd-registry.json"]
}
}
}
Or invoke the Python module directly (avoids PATH issues):
{
"mcpServers": {
"nexus-gateway": {
"command": "python",
"args": ["-m", "mcpnexus.dynamic.server", "--registry", "/path/to/mcpd-registry.json"]
}
}
}
3. Measure the token savings
nexus-benchmark --registry registry/mcpd-registry.json
(Generate your registry first with nexus-sync.) See Benchmarks for real, reproducible numbers and what they mean.
Architecture
┌─────────────────────────────────────────────────────────────┐
│ LLM / AI Client │
└──────────────────────┬──────────────────────────────────────┘
│ MCP (stdio / JSON-RPC 2.0)
┌────────────┴─────────────┐
│ │
┌──────▼──────┐ ┌───────▼──────┐
│ Discovery │ │ Dynamic │
│ Mode │ │ Mode │
│ │ │ │
│ mcpd_find │ │ find_tools │
│ mcpd_list │ │ │
│ mcpd_connect│ │ LazyPool │
│ mcpd_get_schema│ │ │
└──────┬──────┘ └───────┬──────┘
│ │
└────────────┬─────────────┘
│
┌────────────▼─────────────┐
│ Shared Core │
│ │
│ Registry (mcpd-registry) │
│ KeywordSearchEngine │
│ ToolSearchEngine │
│ HybridSearch (TF-IDF + │
│ sentence-transformers) │
│ NexusConnector │
│ ├─ stdio transport │
│ ├─ streamable-http │
│ └─ SSE transport │
└───────────────────────────┘
Design principles
A discovery layer, not a permanent proxy. In Discovery Mode, once mcpd_connect resolves, the LLM gets direct tool access to the connected server. In Dynamic Mode, server connections stay lazy — a server process starts only when one of its tools is actually called.
Offline-first registry. Tool summaries (name, description, tags) are captured at sync time. Searches run against the cached registry with zero network traffic; full tool schemas load only on connection.
Search degrades gracefully. Keyword search (TF-IDF with synonyms) is the default — always available, no extra dependencies. Semantic search is optional (pip install mcpnexus[embeddings]): when installed, sentence-transformers embeddings blend with keyword results, which helps for loosely-phrased natural-language queries like "a tool for reading web pages" → Playwright. The keyword synonym table also understands multilingual input (e.g. Polish query terms resolve to the right English tool concepts). Keyword-first keeps installs frictionless when you don't need semantic search.
Benchmarks
Methodology. Every number below comes from nexus-benchmark, which instantiates the real DiscoveryServer/DynamicServer classes and measures their actual tools/list JSON-RPC output — not hardcoded stand-ins that can drift out of sync with the real code. Tokens are counted with tiktoken's o200k_base encoding (the GPT-4o tokenizer) when installed; without it, the CLI clearly labels its output as a rougher char/4 estimate rather than presenting both with false equal precision. Reproduce any number here yourself:
pip install mcpnexus[benchmark]
nexus-benchmark --registry registry/benchmark-registry.json --query "create github issue" --quality
At realistic scale
6 servers, 61 tools, every tool has a complete real schema — registry/benchmark-registry.json, not a partial catalog:
| Scenario | Tools | Tokens | vs. direct load |
|---|---|---|---|
| Direct (all servers, no MCP Nexus) | 61 | 3,786 | — |
| Discovery Mode (before connect) | 4 | 480 | 87% fewer |
| Discovery Mode (after connect: github) | 18 | 955 | 75% fewer |
| Dynamic Mode (before find) | 2 | 201 | 95% fewer |
Dynamic Mode (after find_tools("create github issue")) |
7 | 390 | 90% fewer |
recall@k on this registry: 100% (8/8) — these savings aren't bought with worse search accuracy (quality.py verifies it).
At large scale — where MCP Nexus really pays off
This is the case MCP Nexus is actually built for: an organization with a large, growing catalog of MCP servers — internal tools, SaaS integrations, custom in-house APIs — where any one session only ever touches a handful. The meta-tool interface (mcpd_find/mcpd_list/mcpd_connect/mcpd_get_schema, or find_tools/get_tool_schema) costs a fixed number of tokens no matter how big the registry gets, while a direct load grows linearly with every tool you add. The gap only widens as you scale up. Measured on a fully-specified synthetic registry of 20 servers / 300 tools (registry/benchmark-registry-large.json):
| Scenario | Tools | Tokens | vs. direct load |
|---|---|---|---|
| Direct (all servers, no MCP Nexus) | 300 | 20,757 | — |
| Discovery Mode (before connect) | 4 | 480 | 98% fewer |
| Discovery Mode (after connect: one server) | 19 | 997 | 95% fewer |
| Dynamic Mode (before find) | 2 | 201 | 99% fewer |
Dynamic Mode (after find_tools(...)) |
7 | 394 | 98% fewer |
The static meta-tool cost (480 / 201 tokens) is identical to the 61-tool benchmark above — that's the whole mechanism. Add a 21st server, a 500th tool, it doesn't move. Only the "Direct" column keeps growing. This is the regime — many configured MCP servers, a handful used per session — where MCP Nexus is the strongest option in this document.
Small, fixed setups: skip the discovery layer
The same benchmark against a deliberately tiny registry (3 servers, 7 tools — the fixture in tests/conftest.py) shows the other end of the curve, and we're showing it because it's true, not because it's flattering:
| Scenario | Tools | Tokens | vs. direct load |
|---|---|---|---|
| Direct (all servers, no MCP Nexus) | 7 | 252 | — |
| Discovery Mode (before connect) | 4 | 480 | 90% more |
| Discovery Mode (after connect) | 7 | 576 | 129% more |
| Dynamic Mode (before find) | 2 | 201 | 20% fewer |
| Dynamic Mode (after find_tools) | 3 | 288 | 14% more |
Rule of thumb: if you have a small, fixed set of 2-3 MCP servers you always use, configure them directly — a discovery layer (this one or any competitor's) adds overhead you don't need. MCP Nexus's value curve turns sharply positive once your registry grows past a handful of servers, and keeps improving from there — see the 300-tool numbers above.
Versus other tools on the market
One real side-by-side, run ourselves: NCP Orchestrator v2.3.1, installed fresh (npx -y @portel/ncp@latest), zero backend servers configured — its own static meta-tool interface, tokenized the exact same way:
| Tool | Meta-tools exposed | Tokens |
|---|---|---|
NCP v2.3.1 (find + code) |
2 | 903 |
| MCP Nexus Discovery Mode (before connect) | 4 | 480 |
| MCP Nexus Dynamic Mode (before find) | 2 | 201 |
Measured 2026-07-29, tiktoken o200k_base. This is the one comparison in this section we actually ran ourselves — same tokenizer, same "before connecting anything" scenario, reproducible by anyone with Node.js installed.
For everyone else below, we're citing published numbers, not our own measurements — different registries, different tokenizers, different baselines. Treat these as directional, not as line-by-line comparable to the numbers above:
| Tool | Claimed reduction | Source |
|---|---|---|
| Anthropic native Tool Search (Claude Code) | ~85–96% (reported 134k→5k tokens internally) | community writeup |
| Speakeasy Dynamic Toolsets | ~99% ("100x") | speakeasy.com |
| NCP Orchestrator (vendor-claimed) | 83–97%, varies by source | arul.sg/ncp, mcp.directory |
The most important line in this table isn't a percentage: Anthropic shipped this exact pattern natively into Claude Code. If you're specifically on Claude Code, check whether you need any third-party discovery layer — this one included — before reaching for one.
Verdict
MCP Nexus's savings scale with the size of your MCP ecosystem: at 300 tools across 20 servers, the measured numbers above hit 98-99%, and that curve keeps climbing the more servers you add — the meta-tool cost never grows. That's the regime this is built for: large, growing MCP deployments where dozens of servers are configured and only a handful get used per session. Against a live-tested competitor (NCP Orchestrator) it wins outright at the same task; against vendor-published numbers from Anthropic and Speakeasy it's in the same range, without an apples-to-apples test to say more than that.
Best for: teams with many MCP servers — SaaS integrations, internal tools, and fully custom MCP servers you build yourself. Because MCP Nexus works at the protocol level, it doesn't care what's behind a server: if you wrap a proprietary application in an MCP server (CAD tools, a game engine editor, an internal build system — anything you can script), MCP Nexus discovers and routes to it exactly like it does GitHub or Slack. The bigger and more varied that catalog gets, the more this pays off.
Less useful for: a handful of MCP servers you always use directly, or Claude Code users who already get equivalent behavior natively — see Small, fixed setups above.
Registry format
The registry file (mcpd-registry.json) is a JSON catalog of MCP servers:
{
"mcpd_version": "1.0",
"metadata": {
"name": "My MCP Registry",
"description": "Personal registry of MCP servers"
},
"servers": [
{
"id": "github",
"name": "GitHub MCP Server",
"description": "Official GitHub MCP server (remote). Repositories, issues, pull requests, and code search",
"version": "remote-2025-11",
"transport": {
"type": "streamable-http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": { "Authorization": "Bearer ${GITHUB_MCP_PAT}" }
},
"tags": ["github", "git", "code", "issues"],
"tools_summary": [
{
"name": "issue_write",
"description": "Create or update an issue or pull request",
"tags": ["issues", "create"]
}
],
"estimated_tools_count": 90,
"enabled": true,
"last_synced": "2026-06-11T00:00:00Z"
}
]
}
Full schema: registry/schemas/mcpd-schema.json
Project structure
mcpnexus/
├── mcpnexus/ # Python package
│ ├── __init__.py # Public API and version
│ ├── models.py # Shared dataclasses
│ ├── registry.py # Registry loader (mcpd-registry.json)
│ ├── connector.py # MCP connector — stdio, HTTP, SSE transports
│ ├── sync.py # Registry builder (sync from mcp.json)
│ ├── benchmark.py # Token savings measurement
│ ├── search/
│ │ ├── keyword_search.py # Server-level TF-IDF search
│ │ ├── tool_search.py # Tool-level TF-IDF search
│ │ ├── embeddings.py # Sentence-transformer embedding engine
│ │ └── hybrid.py # Hybrid keyword + semantic search
│ ├── discovery/
│ │ └── server.py # Discovery Mode MCP server
│ └── dynamic/
│ ├── server.py # Dynamic Mode MCP server
│ ├── tool_index.py # O(1) tool lookup index
│ └── lazy_pool.py # On-demand connection pool
├── registry/
│ ├── mcpd-registry.example.json # Example registry
│ ├── benchmark-registry.json # Fully-specified registry used by the Benchmarks section
│ ├── benchmark-registry-large.json # 20-server/300-tool registry for at-scale benchmarks
│ └── schemas/
│ └── mcpd-schema.json # JSON Schema for registry validation
├── docs/
│ ├── specification.md # Protocol specification
│ ├── architecture.md # Architecture deep-dive
│ ├── registry-format.md # Registry format reference
│ └── dynamic-mcp.md # Dynamic Mode guide
├── examples/
│ ├── cursor-config-discovery.json
│ ├── cursor-config-dynamic.json
│ └── README.md
├── tests/ # 509 tests, 100% coverage
└── pyproject.toml
Development
git clone https://github.com/KrzysztofAugiewicz/MCPNexus.git
cd MCPNexus
pip install -e ".[dev]"
# Run tests
pytest
# Run tests with coverage
pytest --cov=mcpnexus --cov-report=term-missing
# Run end-to-end integration test
python test_e2e.py
# Benchmark token savings (generate registry first with nexus-sync)
nexus-benchmark --registry registry/mcpd-registry.json
CLI reference
| Command | Description |
|---|---|
nexus-server |
Start the Discovery Mode MCP server |
nexus-gateway |
Start the Dynamic Mode MCP server |
nexus-sync |
Build or update the registry from an mcp.json config |
nexus-benchmark |
Measure token savings for a given registry |
All commands accept --help for the full option reference.
Transport support
| Transport | Install extra | Use case |
|---|---|---|
| stdio | (core) | Local process-based MCP servers |
| Streamable HTTP | mcpnexus[http] |
Remote HTTP MCP servers |
| SSE | mcpnexus[http] |
Legacy remote servers (Server-Sent Events) |
Transport type is resolved automatically from the registry entry's transport.type field.
Documentation
- Protocol Specification — Tool schemas, message formats, handshake sequence
- Architecture — Data flow, shared core, design decisions
- Registry Format — Full registry schema reference
- Dynamic Mode Guide — Dynamic Mode deep-dive
Publishing to PyPI
Releases are published automatically when a GitHub Release is created. Prerequisites:
- Add
PYPI_API_TOKENto repository secrets (create at pypi.org/manage/account/token) - Create a release with a tag (e.g.
v1.0.1)
The publish workflow builds and uploads to PyPI.
Contributing
Contributions are welcome. Please read CONTRIBUTING.md before opening a pull request. For bug reports and feature requests, use GitHub Issues.
Authors
- Krzysztof Augiewicz — Lead Architect & Creator — LinkedIn · GitHub
- Kacper Pisarczyk — Core Contributor, Discovery & Registry Systems — LinkedIn
- Sebastian Pawłowski — Advisory & QA Support (testing, hardware/software provisioning) — LinkedIn
- Mateusz Wiszniowski — Core Contributor
Full details in AUTHORS.md.
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcpnexus-1.0.6.tar.gz.
File metadata
- Download URL: mcpnexus-1.0.6.tar.gz
- Upload date:
- Size: 128.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d454e95c7d471dc429625048dff3e20be3180be175c160b5474c49167874d4d
|
|
| MD5 |
b1abb8e24eb8cccd1bd1c71a3ac33eba
|
|
| BLAKE2b-256 |
682465b0c88967f981cadb28a8d74af074e04cabcfa21b2884b097099f158922
|
File details
Details for the file mcpnexus-1.0.6-py3-none-any.whl.
File metadata
- Download URL: mcpnexus-1.0.6-py3-none-any.whl
- Upload date:
- Size: 68.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce35e1d3e071c7c7b1976ee133fe34b4b8c16c302b7c1459916c9da730125855
|
|
| MD5 |
a21611a83fca92ab712b9585e3c82ffc
|
|
| BLAKE2b-256 |
7524d8bc25c6548734e7bea625ff61e5b4a69e23a13d859e7862f9c3ca194dd3
|