Replies: 6 comments 2 replies
|
This idea is great! However, I want to know which model will be used for sentence encoding and whether it will be resource-intensive. Can we use a cloud API for embedding instead of local sentence encoding? Additionally, if the semantics of MCP change, how can we quickly re-encode? |
|
Great question — let me break it down: Embedding ModelThe current implementation uses
This runs entirely local — no API calls, no tokens burned, no latency. For our use case (encoding short tool descriptions like Why not a Cloud API for embeddings?You could use OpenAI's
The whole point of this project is to reduce complexity, not add more moving parts. Local encoding with MiniLM keeps it lightweight and reliable [1]. That said — if someone absolutely wants cloud embeddings (e.g., running on a very constrained device), the architecture supports it. You'd just swap out the Re-encoding when MCP semantics changeThis is the key question. Two approaches: Approach 1: Manual re-sync (simplest)# After adding/changing any MCP server:
python3 sync_tools.py --config /path/to/config.json
# Done. Takes ~5-10 seconds for 220 tools.Approach 2: Automated periodic re-sync (set and forget)This is what I'd recommend for production setups. A simple cron job or systemd timer that re-indexes every 6 hours: # Cron (inside the container or on host)
0 */6 * * * cd /path/to/smart-mcp-router && python3 sync_tools.py --config /path/to/config.json >> /var/log/mcp-sync.log 2>&1Or as a lightweight background service in the same Python project: # auto_sync.py (optional daemon)
import time, subprocess, os
INTERVAL = 6 * 60 * 60 # 6 hours
SCRIPT = os.path.join(os.path.dirname(__file__), "sync_tools.py")
CONFIG = os.path.expanduser("~/.nanobot/config.json")
while True:
print(f"🔄 Re-syncing tool index...")
subprocess.run(["python3", SCRIPT, "--config", CONFIG])
print(f"✅ Next sync in {INTERVAL // 3600}h")
time.sleep(INTERVAL)This covers all scenarios:
The re-encoding is idempotent — it rebuilds the entire index from scratch each time. With 220 tools that takes ~5 seconds on CPU, so there's zero reason to do incremental updates and add complexity. TL;DR
The design goal is maximum simplicity: two Python files, one numpy index, zero external services [1]. |
|
I am currently testing this model. MODEL_NAME = "BAAI/bge-small-en-v1.5" |
|
This is a very real bottleneck for high-MCP agent setups. Once every tool description is injected on every turn, the cost is no longer just model pricing; it becomes routing waste, slower tool selection, and lower accuracy from context pollution. A pattern I would test is a two-stage route: a cheap/local semantic pre-selector for tool candidates, then the stronger model only sees the 5-15 tools that are actually plausible for the task. Pairing that with per-run token budgets and provider fallback usually gives a much cleaner cost profile. I'm building a private-beta OpenAI-compatible API aggregation layer focused on Chinese models such as DeepSeek / Qwen / GLM-style providers, using official-provider keys. If you want to benchmark lower-cost models for this kind of MCP routing workload, I’d be happy to share some test credits. Curious: in your setup, is the bigger pain the raw token cost, latency, or tool selection quality? |
|
Running a similar problem here, but I solved it with a two-piece combo instead of the embedding-router approach: mcp-lazy-proxy: lazy-loads tool definitions instead of registering everything with the LLM upfront. Tools only get pulled into context when actually needed for a call, rather than dumping all 220+ definitions on every turn. Cuts the per-call token cost dramatically without needing a vector index at all. Together they get you most of the same win as your semantic router (LLM isn't drowning in tool defs), but the mechanism is "load lazily + normalize on the way through" rather than "embed everything and do cosine similarity search." No sentence-transformers, no vector index file to maintain — just proxying and shape-correction. Curious how it'd compare on your 220-tool setup — did you benchmark actual selection accuracy (right tool picked) vs just token savings? That's the part I'd want to see hold up as your tool count grows. |
Uh oh!
There was an error while loading. Please reload this page.
💡 Smart MCP Router – Semantic Tool Routing for Nanobot
The Problem
When running many MCP servers simultaneously (20, 30, 40+), every single tool definition gets injected into the LLM's context on every request. In a real-world setup with 38 MCP servers and 220+ registered tools, this means:
This is not a RAM or CPU problem — it's a context window pollution problem.
Real-World Numbers (from my setup)
~97% of the tool context is irrelevant on any given request.
The Idea: Smart MCP Router
Instead of exposing all 220+ tools directly to the LLM, introduce a lightweight Python-based MCP proxy that:
smart_routeandlist_tools)The LLM never sees 220 tools. It sees 2. The router handles the rest.
Architecture / Flow Diagram
Why File-Based Instead of Qdrant/ChromaDB?
For most setups (even with 500+ tools), a full vector database is overkill. The tool index is small:
A simple numpy
.npzfile with precomputed embeddings + cosine similarity search is:numpyandsentence-transformersIf you later scale to 1000+ tools across distributed systems, you can swap in Qdrant/ChromaDB with minimal code changes.
Implementation Outline
Project Structure
Step 1:
sync_tools.py– Automatic Tool Discovery & IndexingThis script reads your existing
config.json, starts each MCP server briefly, asks it for its tool list via the MCP protocol (tools/list), computes embeddings, and saves everything to local files.Step 2:
smart_router_mcp.py– The Router MCP ServerStep 3: Integration in
config.jsonReplace your 38 MCP server entries with a single one:
{ "mcpServers": { "smart-router": { "command": "python3", "args": ["/path/to/smart-mcp-router/smart_router_mcp.py"], "toolTimeout": 35 } } }All 38 original servers stay installed and runnable — the router calls them on demand. They just don't get registered with the LLM directly anymore.
requirements.txtBefore & After Comparison
python3 sync_tools.py→ restartsentence-transformers,numpy(pure Python).npz)Limitations & Future Work
sentence-transformersmodel load takes ~2-3 seconds on first request (stays cached after)list_toolshelps heresmart_routecalls (works fine, just slightly more verbose)batch_routetool for multi-step workflowsPrompt for Coding AI (Copy-Paste Ready)
If you want an AI assistant to implement or extend this, use this prompt:
How to Test
Looking for feedback on:
pip install smart-mcp-router)?Would love to hear from anyone running 10+ MCP servers — what's your experience with tool context bloat?
All reactions