Metadata-Version: 2.4
Name: mcp-switch
Version: 0.3.0
Summary: Context-saving MCP switchboard for AI agents - lazy-load tools, resources, and prompts only when needed
Project-URL: Homepage, https://github.com/Zer0Wav3s/mcp-switch
Project-URL: Repository, https://github.com/Zer0Wav3s/mcp-switch
Project-URL: Issues, https://github.com/Zer0Wav3s/mcp-switch/issues
Author: ZeroWaves
License-Expression: MIT
License-File: LICENSE
Keywords: agent,ai,claude-code,codex,cursor,hermes,lazy-loading,mcp,model-context-protocol,namespace,proxy,tools
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Requires-Dist: click>=8.0
Requires-Dist: mcp>=1.0.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

<div align="center">

# 🔀 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](https://img.shields.io/badge/Version-0.2.1-FF4B4B?style=for-the-badge&logo=github&logoColor=white)](#)
[![PyPI](https://img.shields.io/pypi/v/mcp-switch?style=for-the-badge&logo=pypi&logoColor=white&color=3775A9)](https://pypi.org/project/mcp-switch/)
[![License](https://img.shields.io/badge/License-MIT-22C55E?style=for-the-badge)](LICENSE)

[![Python](https://img.shields.io/badge/Python_3.10+-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/)
[![MCP](https://img.shields.io/badge/MCP_Protocol-000000?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+&logoColor=white)](https://modelcontextprotocol.io/)

[![Claude Code](https://img.shields.io/badge/Claude_Code-D4A574?style=for-the-badge&logo=anthropic&logoColor=black)](https://docs.anthropic.com/en/docs/claude-code)
[![Cursor](https://img.shields.io/badge/Cursor-000000?style=for-the-badge&logo=cursor&logoColor=white)](https://cursor.sh)
[![Codex](https://img.shields.io/badge/Codex-412991?style=for-the-badge&logo=openai&logoColor=white)](https://github.com/openai/codex)
[![Windsurf](https://img.shields.io/badge/Windsurf-09B6A2?style=for-the-badge&logo=codeium&logoColor=white)](https://codeium.com/windsurf)

</div>

---

## 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

```diff
- 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

```mermaid
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:

```yaml
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

```bash
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:

```yaml
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:

```yaml
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](https://docs.astral.sh/uv/getting-started/installation/) 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:

```bash
# 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](#step-2-connect-to-your-agent) and create a config manually - see [Configuration](#configuration).

### Step 2: Connect to Your Agent

Pick your agent and add one config block. That's the entire setup.

<details>
<summary><img src="https://img.shields.io/badge/Claude_Code-D4A574?style=flat-square&logo=anthropic&logoColor=black" alt="Claude Code" height="20"/> <strong>Claude Code</strong></summary>

#### Option A: One-liner

```bash
claude mcp add router -- uvx mcp-switch serve --config ~/.config/mcp-switch/config.yaml
```

#### Option B: Project config (`.mcp.json`)

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

</details>

<details>
<summary><img src="https://img.shields.io/badge/Cursor-000000?style=flat-square&logo=cursor&logoColor=white" alt="Cursor" height="20"/> <strong>Cursor</strong></summary>

Add to Cursor Settings > MCP Servers, or create `.cursor/mcp.json`:

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

</details>

<details>
<summary><img src="https://img.shields.io/badge/Codex-412991?style=flat-square&logo=openai&logoColor=white" alt="Codex" height="20"/> <strong>OpenAI Codex</strong></summary>

Add to your Codex MCP config:

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

</details>

<details>
<summary><img src="https://img.shields.io/badge/Claude_Desktop-D4A574?style=flat-square&logo=anthropic&logoColor=black" alt="Claude Desktop" height="20"/> <strong>Claude Desktop</strong></summary>

Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "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.

</details>

<details>
<summary><img src="https://img.shields.io/badge/Windsurf-09B6A2?style=flat-square&logo=codeium&logoColor=white" alt="Windsurf" height="20"/> <strong>Windsurf</strong></summary>

Add to your Windsurf MCP config:

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

</details>

<details>
<summary><img src="https://img.shields.io/badge/Cline-5A67D8?style=flat-square" alt="Cline" height="20"/> <strong>Cline</strong></summary>

Add to Cline MCP settings:

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

</details>

<details>
<summary><img src="https://img.shields.io/badge/Hermes-FF6B35?style=flat-square" alt="Hermes" height="20"/> <strong>Hermes</strong></summary>

Add to `~/.hermes/config.yaml`:

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

</details>

<details>
<summary><img src="https://img.shields.io/badge/OpenClaw-1a1a2e?style=flat-square" alt="OpenClaw" height="20"/> <strong>OpenClaw</strong></summary>

Add to your OpenClaw MCP config:

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

</details>

<details>
<summary><img src="https://img.shields.io/badge/VS_Code-007ACC?style=flat-square&logo=visual-studio-code&logoColor=white" alt="VS Code" height="20"/> <strong>VS Code Copilot</strong></summary>

Add to `.vscode/mcp.json`:

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

</details>

<details>
<summary><img src="https://img.shields.io/badge/Amazon_Q-FF9900?style=flat-square&logo=amazon&logoColor=white" alt="Amazon Q" height="20"/> <strong>Amazon Q CLI</strong></summary>

Add to `~/.aws/amazonq/mcp.json`:

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

</details>

<details>
<summary><strong>Any Other MCP Client</strong></summary>

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.

</details>

---

## Configuration

### Quick Start: Import Existing

```bash
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`:

```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

```bash
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.**

```yaml
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.

```yaml
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.

```yaml
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.

```yaml
# 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:

```yaml
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
```

---

## License

MIT © 2026 ZeroWaves — see [LICENSE](LICENSE).

<!-- mcp-name: io.github.Zer0Wav3s/mcp-switch -->
