Context-saving MCP switchboard for AI agents - lazy-load tools, resources, and prompts only when needed
Project description
🔀 mcp-switch
Give your AI agent only the MCP capabilities it needs, right when it needs them.
mcp-switch is a context-saving MCP switchboard for AI agents. It lazy-loads tools, resources, and prompts on demand so Claude Code, Cursor, Codex, Hermes, OpenClaw, and similar clients stay fast, focused, and less confused by giant tool lists.
Why?
When you connect MCP servers to your AI agent, every tool gets loaded at once - even the ones your agent doesn't need right now.
The more tools an agent sees, the slower and less accurate it gets. It's like giving someone a toolbox with 50 tools when they just need a screwdriver.
mcp-switch fixes this. Your agent starts with just 5 simple tools:
| Tool | What it does |
|---|---|
list_namespaces |
"What tool groups are available?" |
load_namespace |
"Give me the database tools" |
unload_namespace |
"I'm done, put them away" |
load_group |
"Give me all the dev tools at once" |
unload_group |
"Put all the dev tools away" |
When the agent needs database tools, it loads them. When it's done, it puts them back. Clean, fast, focused.
Without mcp-switch vs With mcp-switch
- You: "Fix the CSS on the navbar"
- Agent sees: 46 tools (database, GitHub, Slack, Docker, Kubernetes...)
- Agent: gets distracted, picks wrong tools, slower response
+ You: "Fix the CSS on the navbar"
+ Agent sees: 5 tools (list, load, unload, load_group, unload_group)
+ Agent: focuses entirely on your request, fast response
+
+ You: "Now check the database for user records"
+ Agent: loads database tools, runs query, done
How It Works
flowchart TB
Agent["🤖 AI Agent<br/><i>Claude Code / Cursor / Codex / Hermes / OpenClaw</i>"]
subgraph Router["mcp-switch"]
Meta["5 Meta-Tools<br/>list_namespaces · load_namespace · unload_namespace<br/>load_group · unload_group"]
Registry["Registry"]
Meta --> Registry
end
Agent -- "MCP (stdio)" --> Meta
subgraph Backends["Backends (lazy-started on demand)"]
MCP1["🔌 MCP Backend<br/>postgres"]
MCP2["🔌 MCP Backend<br/>github"]
CLI1["⚡ CLI Backend<br/>git, docker, system"]
end
Registry -. "load_namespace('postgres')" .-> MCP1
Registry -. "load_namespace('github')" .-> MCP2
Registry -. "load_namespace('git')" .-> CLI1
Registry -. "load_group('dev')" .-> CLI1
MCP1 -- "stdio" --> S1["mcp-server-postgres"]
MCP2 -- "stdio" --> S2["mcp-server-github"]
subgraph Features["What gets proxied"]
Tools["🛠️ Tools"]
Resources["📦 Resources"]
Prompts["💬 Prompts"]
end
Registry --> Features
The agent asks mcp-switch what's available, loads what it needs, and unloads when done:
Agent: What tool groups do you have?
-> list_namespaces()
-> ["postgres", "github", "docker", "git"]
Agent: I need database tools.
-> load_namespace("postgres")
-> 5 tools, 2 resources, 1 prompt now available
Agent: Done with the database.
-> unload_namespace("postgres")
-> Database tools/resources/prompts removed, back to just 5 meta-tools
Agent: I need all dev tools.
-> load_group("dev")
-> git, docker, system namespaces loaded at once
What Gets Proxied
mcp-switch doesn't just proxy tools — it also passes through resources and prompts from your backends:
| Capability | Description | How it works |
|---|---|---|
| Tools | Functions the agent can call | Loaded on load_namespace, routed on call_tool |
| Resources | Data the agent can read (files, schemas, etc.) | Listed via list_resources, read via read_resource |
| Prompts | Reusable prompt templates | Listed via list_prompts, fetched via get_prompt |
When a namespace is loaded, all three capabilities are registered with the router. When it's unloaded, they're all removed together.
New in v0.3.0
🌐 HTTP/StreamableHTTP Backend
Connect to remote MCP servers over HTTP - no local process needed:
namespaces:
supabase:
type: http
url: https://mcp.supabase.com/mcp
headers:
Authorization: "Bearer ${SUPABASE_TOKEN}"
description: "Supabase database tools"
idle_ttl: 600
Works with any hosted MCP server: Supabase, Neon, Cloudflare Workers, or your own. Uses StreamableHTTP with automatic SSE fallback.
🔧 Hermes Export
uvx mcp-switch export hermes
Outputs native YAML ready to paste into ~/.hermes/config.yaml (not JSON like other clients).
📥 Smarter Import
mcp-switch init now correctly imports URL-based MCP configs as type: http instead of leaving a comment about unsupported transport.
New in v0.2.0
🗂️ Namespace Groups
Define groups of related namespaces in your config and load/unload them in one call:
groups:
dev:
- git
- docker
- system
data:
- postgres
- github
Agent: I need all my dev tools.
-> load_group("dev")
-> git, docker, and system namespaces loaded at once
Agent: Done with dev work.
-> unload_group("dev")
-> All three namespaces unloaded
⏱️ Idle TTL Auto-Unload
Set a per-namespace idle_ttl (in seconds) and mcp-switch will automatically unload namespaces that haven't been used recently:
namespaces:
postgres:
type: mcp
command: "uvx mcp-server-postgres"
args: ["--connection-string", "postgresql://localhost/mydb"]
description: "Query and manage PostgreSQL databases"
idle_ttl: 300 # Unload after 5 minutes of inactivity
This keeps your agent's context clean without manual cleanup. Set idle_ttl: 0 (the default) to disable auto-unload for a namespace.
Setup
No Python install needed. Every setup below uses uvx which runs packages on-the-fly (like npx for Python).
Don't have
uvx? Install uv first:curl -LsSf https://astral.sh/uv/install.sh | sh
Step 1: Import Your Existing MCP Config
If you already have MCP servers configured in Claude Desktop, Cursor, or VS Code, mcp-switch can import them automatically:
# Auto-detect and import from Claude Desktop, Cursor, VS Code, or Windsurf
uvx mcp-switch init
# Or import from a specific config file
uvx mcp-switch init --from ~/.config/claude/claude_desktop_config.json
This creates ~/.config/mcp-switch/config.yaml from your existing setup. No rewriting configs by hand.
Don't have existing MCP servers? Skip to Step 2 and create a config manually - see Configuration.
Step 2: Connect to Your Agent
Pick your agent and add one config block. That's the entire setup.
Claude Code
Option A: One-liner
claude mcp add router -- uvx mcp-switch serve --config ~/.config/mcp-switch/config.yaml
Option B: Project config (.mcp.json)
{
"mcpServers": {
"router": {
"command": "uvx",
"args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
}
}
}
Cursor
Add to Cursor Settings > MCP Servers, or create .cursor/mcp.json:
{
"mcpServers": {
"router": {
"command": "uvx",
"args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
}
}
}
OpenAI Codex
Add to your Codex MCP config:
{
"mcpServers": {
"router": {
"command": "uvx",
"args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
}
}
}
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"router": {
"command": "uvx",
"args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
}
}
}
Pro tip: Move your existing
mcpServersentries intomcp-switch.yamlas namespaces, then replace them all with the singlerouterentry above. You go from N server configs to 1.
Windsurf
Add to your Windsurf MCP config:
{
"mcpServers": {
"router": {
"command": "uvx",
"args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
}
}
}
Cline
Add to Cline MCP settings:
{
"mcpServers": {
"router": {
"command": "uvx",
"args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
}
}
}
Hermes
Add to ~/.hermes/config.yaml:
mcp_servers:
router:
command: uvx
args: ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
OpenClaw
Add to your OpenClaw MCP config:
{
"mcpServers": {
"router": {
"command": "uvx",
"args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
}
}
}
VS Code Copilot
Add to .vscode/mcp.json:
{
"servers": {
"router": {
"command": "uvx",
"args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
}
}
}
Amazon Q CLI
Add to ~/.aws/amazonq/mcp.json:
{
"mcpServers": {
"router": {
"command": "uvx",
"args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
}
}
}
Any Other MCP Client
The pattern is always the same. Point your client at:
- Command:
uvx - Args:
["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
mcp-switch speaks stdio MCP, which every compliant client supports.
Configuration
Quick Start: Import Existing
uvx mcp-switch init
Auto-detects Claude Desktop, Cursor, VS Code, and Windsurf configs and converts them.
Manual Config
Create ~/.config/mcp-switch/config.yaml:
namespaces:
# Proxy to real MCP servers (lazy startup)
postgres:
type: mcp
command: "uvx mcp-server-postgres"
args: ["--connection-string", "postgresql://localhost/mydb"]
description: "Query and manage PostgreSQL databases"
idle_ttl: 300 # Auto-unload after 5 min of inactivity (0 = disabled)
github:
type: mcp
command: "uvx mcp-server-github"
args: ["--repo", "owner/repo"]
description: "Manage GitHub repos, issues, PRs"
env:
GITHUB_TOKEN: "${GITHUB_TOKEN}"
# Wrap CLI commands as tools (no server needed)
git:
type: cli
description: "Git version control"
tools:
git_status:
command: "git status --porcelain"
description: "Show working tree status"
git_log:
command: "git log --oneline -20"
description: "Show recent commits"
git_diff:
command: "git diff"
description: "Show unstaged changes"
docker:
type: cli
description: "Docker container management"
tools:
docker_ps:
command: "docker ps --format 'table {{.Names}}\t{{.Status}}'"
description: "List running containers"
docker_logs:
command: "docker logs {container}"
description: "Show container logs"
parameters:
container:
type: string
description: "Container name or ID"
required: true
# Groups: load/unload multiple namespaces at once
groups:
dev:
- git
- docker
data:
- postgres
- github
Config Locations (checked in order)
./mcp-switch.yaml./mcp-switch.yml~/.config/mcp-switch/config.yaml~/.mcp-switch.yaml
Or explicit: uvx mcp-switch serve --config /path/to/config.yaml
Validate Your Config
uvx mcp-switch validate
uvx mcp-switch list
Namespace Types
mcp - Proxy to MCP Servers
Connects to a real MCP server via stdio. The backend starts lazily when the namespace is first loaded and stops when unloaded. Tools, resources, and prompts are all proxied.
postgres:
type: mcp
command: "uvx mcp-server-postgres"
args: ["--connection-string", "postgresql://localhost/mydb"]
description: "Query and manage PostgreSQL databases"
env:
DB_PASSWORD: "${DB_PASSWORD}"
idle_ttl: 300 # Optional: auto-unload after 5 minutes of inactivity
cli - Wrap Shell Commands
Turns shell commands into MCP tools. No server process needed. Parameters use {name} template substitution.
git:
type: cli
description: "Git version control"
tools:
git_status:
command: "git status --porcelain"
description: "Show working tree status"
git_diff:
command: "git diff {file}"
description: "Show changes for a specific file"
parameters:
file:
type: string
description: "File path to diff"
required: true
http - Connect to Remote MCP Servers
Connects to hosted/remote MCP servers via HTTP. Uses StreamableHTTP transport with automatic SSE fallback. No local subprocess needed.
supabase:
type: http
url: "https://mcp.supabase.com/mcp"
headers:
Authorization: "Bearer ${SUPABASE_TOKEN}"
description: "Supabase database tools"
idle_ttl: 600 # Optional: auto-unload after 10 minutes
pinned: true # Optional: never auto-evict
Works with any MCP server that supports StreamableHTTP or SSE transport (Supabase, Neon, Cloudflare Workers, custom servers, etc.).
Namespace Groups
Groups let you load or unload several related namespaces in a single call — perfect when your agent needs a whole set of tools at once.
# In your config.yaml
groups:
dev:
- git
- docker
- system
data:
- postgres
- github
The agent uses load_group("dev") and unload_group("dev") instead of loading each namespace individually.
Idle TTL Auto-Unload
Add idle_ttl to any namespace to have it automatically unloaded after a period of inactivity:
namespaces:
postgres:
type: mcp
command: "uvx mcp-server-postgres"
description: "Query and manage PostgreSQL databases"
idle_ttl: 300 # 5 minutes
- The timer resets every time any tool, resource, or prompt in the namespace is used.
- A background check runs every 60 seconds.
- Set
idle_ttl: 0or omit it to keep the namespace loaded until manually unloaded.
See It in Action
You: "Fix the CSS padding on the navbar"
Agent: (only sees 5 tools - stays focused, fixes the CSS immediately)
You: "Now check the database for user records"
Agent: "Let me grab the database tools."
-> load_namespace("postgres") -- 5 database tools, 2 resources, 1 prompt appear
-> postgres__query("SELECT * FROM users LIMIT 10")
-> shows you the results
Agent: "All done, putting the database tools away."
-> unload_namespace("postgres") -- back to just 5 meta-tools
You: "Set up my dev environment"
Agent: "Loading all dev tools."
-> load_group("dev") -- git, docker, system all loaded at once
Agent: "Done with dev work, cleaning up."
-> unload_group("dev") -- all three namespaces unloaded
License
MIT © 2026 ZeroWaves — see LICENSE.
Project details
Release history Release notifications | RSS feed
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 mcp_switch-0.3.0.tar.gz.
File metadata
- Download URL: mcp_switch-0.3.0.tar.gz
- Upload date:
- Size: 33.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f27dbbee05b534beb49f7f9e0fa8f335354bdb4af151860d558d0c93e4bc459
|
|
| MD5 |
1bd4f09d3ca71183e18408d8d6417376
|
|
| BLAKE2b-256 |
f0689ad4751357fa62f038594f0cb22c342f6aa35b83a2a5f738e5e6d6aaaae6
|
Provenance
The following attestation bundles were made for mcp_switch-0.3.0.tar.gz:
Publisher:
publish.yml on Zer0Wav3s/mcp-switch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_switch-0.3.0.tar.gz -
Subject digest:
2f27dbbee05b534beb49f7f9e0fa8f335354bdb4af151860d558d0c93e4bc459 - Sigstore transparency entry: 1309658713
- Sigstore integration time:
-
Permalink:
Zer0Wav3s/mcp-switch@0bd10abde8343eebfa575935b2299abf341a7eb1 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Zer0Wav3s
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0bd10abde8343eebfa575935b2299abf341a7eb1 -
Trigger Event:
release
-
Statement type:
File details
Details for the file mcp_switch-0.3.0-py3-none-any.whl.
File metadata
- Download URL: mcp_switch-0.3.0-py3-none-any.whl
- Upload date:
- Size: 32.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5966b07493bed2631c01461481edb2870673ce6246353a233259506ecc5b5a30
|
|
| MD5 |
8f5f1db2f1bab47bdd366bae8794ddb5
|
|
| BLAKE2b-256 |
0390325f115bcc4d667c12fb463e62b1fafb215045f799c1f383345a7ca62276
|
Provenance
The following attestation bundles were made for mcp_switch-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on Zer0Wav3s/mcp-switch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_switch-0.3.0-py3-none-any.whl -
Subject digest:
5966b07493bed2631c01461481edb2870673ce6246353a233259506ecc5b5a30 - Sigstore transparency entry: 1309658984
- Sigstore integration time:
-
Permalink:
Zer0Wav3s/mcp-switch@0bd10abde8343eebfa575935b2299abf341a7eb1 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Zer0Wav3s
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0bd10abde8343eebfa575935b2299abf341a7eb1 -
Trigger Event:
release
-
Statement type: