Skip to main content

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.

Version PyPI License

Python MCP

Claude Code Cursor Codex Windsurf


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 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 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"]
    }
  }
}
Codex OpenAI Codex

Add to your Codex MCP config:

{
  "mcpServers": {
    "router": {
      "command": "uvx",
      "args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
    }
  }
}
Claude Desktop 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 mcpServers entries into mcp-switch.yaml as namespaces, then replace them all with the single router entry above. You go from N server configs to 1.

Windsurf Windsurf

Add to your Windsurf MCP config:

{
  "mcpServers": {
    "router": {
      "command": "uvx",
      "args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
    }
  }
}
Cline Cline

Add to Cline MCP settings:

{
  "mcpServers": {
    "router": {
      "command": "uvx",
      "args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
    }
  }
}
Hermes Hermes

Add to ~/.hermes/config.yaml:

mcp_servers:
  router:
    command: uvx
    args: ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
OpenClaw OpenClaw

Add to your OpenClaw MCP config:

{
  "mcpServers": {
    "router": {
      "command": "uvx",
      "args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
    }
  }
}
VS Code VS Code Copilot

Add to .vscode/mcp.json:

{
  "servers": {
    "router": {
      "command": "uvx",
      "args": ["mcp-switch", "serve", "--config", "~/.config/mcp-switch/config.yaml"]
    }
  }
}
Amazon Q 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)

  1. ./mcp-switch.yaml
  2. ./mcp-switch.yml
  3. ~/.config/mcp-switch/config.yaml
  4. ~/.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

Test Your Backends

# Test all namespaces (connects, lists tools, reports health)
uvx mcp-switch test

# Test a single namespace
uvx mcp-switch test postgres

# JSON output for scripting/CI
uvx mcp-switch test --json-output

Example output:

Testing 4 namespace(s)...

  ✓ postgres (mcp) [1203ms]
    5 tools, 2 resources, 1 prompt
  ✓ github (mcp) [892ms]
    12 tools, 0 resources, 0 prompts
  ✓ supabase (http) [341ms]
    8 tools, 3 resources, 0 prompts
  ✓ git (cli) [0ms]
    3 tools, 0 resources, 0 prompts

Results: 4 passed, 0 failed

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: 0 or 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


Troubleshooting

Common Issues

"Command not found" for MCP backends

  • Make sure the MCP server package is installed: uvx mcp-server-postgres --help
  • Run mcp-switch doctor to catch missing executables before serving

HTTP backend returns 401/403

  • Your auth token might not be set. Use ${ENV_VAR} syntax in config and set the variable in your shell
  • Run mcp-switch test <namespace> to verify connectivity
  • Headers are passed exactly as configured - check for trailing spaces or wrong format

Env var expansion shows empty values

  • Use ${VAR_NAME} syntax in config (not $VAR_NAME)
  • mcp-switch doctor will catch empty env expansions
  • Make sure the env var is exported in the shell that starts mcp-switch

All tools disappear if mcp-switch crashes

  • This is inherent to the proxy architecture - mcp-switch is a single point of failure
  • Pin critical namespaces with pinned: true to prevent accidental eviction
  • Consider running mcp-switch test in CI or on startup to catch config issues early

Agent ignores or misuses mcp-switch tools

  • Make namespace description fields clear and specific - the agent uses these to decide what to load
  • Keep namespace names short and descriptive (postgres, github, not my-postgres-server-v2)

Diagnostic Commands

# Check config is valid
mcp-switch validate

# Check backends, env vars, executables
mcp-switch doctor

# Test actual connectivity
mcp-switch test

# See full config summary
mcp-switch status

License

MIT © 2026 ZeroWaves - see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_switch-0.3.1.tar.gz (36.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_switch-0.3.1-py3-none-any.whl (34.7 kB view details)

Uploaded Python 3

File details

Details for the file mcp_switch-0.3.1.tar.gz.

File metadata

  • Download URL: mcp_switch-0.3.1.tar.gz
  • Upload date:
  • Size: 36.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mcp_switch-0.3.1.tar.gz
Algorithm Hash digest
SHA256 d8956c21d060395b5512073864629e480ca2aaaa6e6417b6aa1a5b932daefec4
MD5 49f6c98edf18e131ed252e706066d8f0
BLAKE2b-256 afa152e018354ac1c36e3071e45460c20422038f0d499602dd587b9b119e83a5

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_switch-0.3.1.tar.gz:

Publisher: publish.yml on Zer0Wav3s/mcp-switch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mcp_switch-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: mcp_switch-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 34.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mcp_switch-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0c1e6e4fc5066e895545f6f7e92e2a5e1c2b4bdbbfa5925d79113fa25c7202dd
MD5 9a258f1b9f178efa1744a095b0b5548c
BLAKE2b-256 5f4cee5a879242f63e4a2792bbc067c04448b8c4e25d4131136f5c4a351a7e3d

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_switch-0.3.1-py3-none-any.whl:

Publisher: publish.yml on Zer0Wav3s/mcp-switch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page