npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aiwerk/mcp-bridge

v2.1.4

Published

Standalone MCP server that multiplexes multiple MCP servers into one interface

Readme

@aiwerk/mcp-bridge

CI npm version License: MIT

Multiplex multiple MCP servers into one interface. One config, one connection, all your tools.

Works with Claude Desktop, Cursor, Windsurf, Cline, OpenClaw, or any MCP client.

Why?

Most AI agents connect to MCP servers one-by-one. With 10+ servers, that's 10+ connections, 200+ tools in context, and thousands of wasted tokens.

MCP Bridge solves this:

  • Router mode: all servers behind one mcp meta-tool (~99% token reduction)
  • Intent routing: say what you need in plain language, the bridge finds the right tool
  • Schema compression: tool descriptions compressed ~57%, full schema on demand
  • Security layer: trust levels, tool deny/allow lists, result size limits
  • HTTP auth: bearer token, custom headers, and OAuth2 Client Credentials with automatic token management
  • Result caching: LRU cache with per-tool TTL overrides
  • Batch calls: parallel multi-tool execution via action=batch
  • Multi-server resolution: automatic tool disambiguation when multiple servers provide the same tool
  • Configurable retries: exponential backoff for transient errors
  • Graceful shutdown: clean process termination and connection cleanup
  • Direct mode: all tools registered individually with automatic prefixing
  • 3 transports: stdio, SSE, streamable-http
  • Built-in catalog: 14 pre-configured servers, install with one command
  • Zero config secrets in files: ${ENV_VAR} resolution from .env

Install

npm install -g @aiwerk/mcp-bridge

Quick Start

# 1. Initialize config
mcp-bridge init

# 2. Install a server from the catalog
mcp-bridge install todoist

# 3. Add your API key
echo "TODOIST_API_TOKEN=your-token" >> ~/.mcp-bridge/.env

# 4. Start (stdio mode — connects to any MCP client)
mcp-bridge

Use with Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "bridge": {
      "command": "mcp-bridge",
      "args": []
    }
  }
}

Recipe Spec v2

Bundled servers now ship with recipe.json using Universal Recipe Spec v2.0. During install, MCP Bridge prefers recipe.json when present and falls back to legacy config.json (v1) for backwards compatibility.

For third-party recipe authors:

  1. Author recipe.json per the spec above.
  2. Validate your recipe before publishing:
npx @aiwerk/mcp-bridge validate-recipe ./recipe.json

config.json (v1) remains supported, but recipe.json (v2) is the recommended format going forward.

Use with Cursor / Windsurf

Add to your MCP config:

{
  "mcpServers": {
    "bridge": {
      "command": "mcp-bridge",
      "args": ["--config", "/path/to/config.json"]
    }
  }
}

Use with OpenClaw

Install as a plugin (handles everything automatically):

openclaw plugins install @aiwerk/openclaw-mcp-bridge

⚠️ Important: Always use the full scoped name @aiwerk/openclaw-mcp-bridge. The unscoped openclaw-mcp-bridge on npm is a different, unrelated package.

See @aiwerk/openclaw-mcp-bridge for details.

Configuration

Config: ~/.mcp-bridge/config.json | Secrets: ~/.mcp-bridge/.env

{
  "mode": "router",
  "servers": {
    "todoist": {
      "transport": "stdio",
      "command": "npx",
      "args": ["-y", "@doist/todoist-ai"],
      "env": { "TODOIST_API_KEY": "${TODOIST_API_TOKEN}" },
      "description": "Task management"
    },
    "github": {
      "transport": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" },
      "description": "GitHub repos, issues, PRs"
    },
    "notion": {
      "transport": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-notion"],
      "env": { "NOTION_API_KEY": "${NOTION_TOKEN}" },
      "description": "Notion pages and databases"
    }
  },
  "toolPrefix": true,
  "connectionTimeoutMs": 5000,
  "requestTimeoutMs": 60000,
  "maxBatchSize": 10,
  "schemaCompression": {
    "enabled": true,
    "maxDescriptionLength": 80
  }
}

Schema Compression

In router mode, tool descriptions from upstream servers can be verbose (100-300+ chars each). Schema compression truncates them to save tokens:

  • Enabled by default — descriptions capped at 80 characters
  • Cuts at sentence boundary when possible, otherwise word boundary
  • Use action=schema to retrieve the full uncompressed schema for any tool on demand
"schemaCompression": {
  "enabled": true,
  "maxDescriptionLength": 80
}

Token savings example: 30 Todoist tools: ~2800 tokens uncompressed -> ~1200 compressed (~57% reduction).

To get full details for a specific tool:

mcp(server="todoist", action="schema", tool="find-tasks")

Set "enabled": false to disable compression and return full descriptions.

Result Caching

Router mode can cache successful action=call tool results in memory using an LRU policy.

  • Disabled by default (resultCache.enabled: false)
  • No external dependencies (Map-based implementation)
  • Defaults: maxEntries: 100, defaultTtlMs: 300000 (5 minutes)
  • Cache key: server:tool:stableJson(params)
  • Per-tool TTL override via resultCache.cacheTtl (for example "todoist:find-tasks": 60000)
  • action=refresh clears the result cache
  • Error responses are never cached
"resultCache": {
  "enabled": true,
  "maxEntries": 100,
  "defaultTtlMs": 300000,
  "cacheTtl": { "todoist:find-tasks": 60000 }
}

Intent Routing

Instead of specifying the exact server and tool, describe what you need:

mcp(action="intent", intent="find my tasks for today")

The bridge uses vector embeddings to match your intent to the right server and tool automatically. Returns the best match with a confidence score and alternatives.

Embedding providers (configured via intentRouting.embedding):

| Provider | Config | Requires | |----------|--------|----------| | gemini (default for auto) | GEMINI_API_KEY in .env | Free tier available | | openai | OPENAI_API_KEY in .env | Paid API | | ollama | Local Ollama running | No API key | | keyword | Nothing | Offline fallback, less accurate |

"intentRouting": {
  "embedding": "auto",
  "minScore": 0.3
}
  • auto (default): tries gemini, openai, ollama, then keyword - in order of availability
  • minScore: minimum confidence to return a match (0-1, default: 0.3)
  • Index is built lazily on first action=intent call

Batch Calls

Run multiple tool calls in one round-trip with action="batch" (parallel execution):

{"action":"batch","calls":[{"server":"todoist","tool":"find-tasks","params":{"query":"today"}},{"server":"github","tool":"list_repos","params":{}}]}
{"action":"batch","results":[{"server":"todoist","tool":"find-tasks","result":{"tasks":[]}}, {"server":"github","tool":"list_repos","error":{"error":"mcp_error","message":"..."}}]}

Use maxBatchSize in config to cap requests (default: 10). Failed calls return per-slot error while successful calls still return result.

Security

Three layers of protection for tool results:

Trust Levels

Per-server control over how results are passed to the agent:

"servers": {
  "my-trusted-server": {
    "trust": "trusted"
  },
  "unknown-server": {
    "trust": "untrusted"
  },
  "sketchy-server": {
    "trust": "sanitize"
  }
}

| Level | Behavior | |-------|----------| | trusted (default) | Results pass through as-is | | untrusted | Results tagged with _trust: "untrusted" metadata | | sanitize | HTML tags stripped, prompt injection patterns removed |

Tool Filter

Control which tools are visible and callable per server:

"servers": {
  "github": {
    "toolFilter": {
      "deny": ["delete_repository"],
      "allow": ["list_repos", "create_issue", "search_code"]
    }
  }
}
  • deny: block specific dangerous tools
  • allow: whitelist mode - only these tools are visible
  • If both: allowed tools minus denied ones
  • Applied in both tool listing and execution (defense in depth)

Max Result Size

Prevent oversized responses from consuming your context:

{
  "maxResultChars": 50000,
  "servers": {
    "verbose-server": {
      "maxResultChars": 10000
    }
  }
}
  • Global default + per-server override
  • Truncated results include _truncated: true and _originalLength

Adaptive Promotion

Frequently used tools can be automatically "promoted" to standalone tools alongside the mcp meta-tool. The promotion system tracks usage and reports which tools qualify — the host environment (e.g., OpenClaw plugin) decides how to register them.

"adaptivePromotion": {
  "enabled": true,
  "maxPromoted": 10,
  "minCalls": 3,
  "windowMs": 86400000,
  "decayMs": 172800000
}

| Option | Default | Description | |--------|---------|-------------| | enabled | false | Opt-in: must be explicitly enabled | | maxPromoted | 10 | Maximum number of tools to promote | | minCalls | 3 | Minimum calls within window to qualify | | windowMs | 86400000 (24h) | Time window for counting calls | | decayMs | 172800000 (48h) | Demote tools with no calls in this period |

Use action="promotions" to check current promotion state:

mcp(action="promotions")

Returns promoted tools (sorted by frequency) and full usage stats. All tracking is in-memory — promotion rebuilds naturally from usage after restart.

Modes

| Mode | Tools exposed | Best for | |------|--------------|----------| | router (default) | Single mcp meta-tool | 3+ servers, token-conscious agents | | direct | All tools individually | Few servers, simple agents |

Router mode — the agent calls mcp(server="todoist", action="list") to discover, then mcp(server="todoist", tool="find-tasks", params={...}) to execute.

Multi-Server Tool Resolution

When action="call" is used without server=, mcp-bridge can resolve collisions automatically.

  • Tool exists on exactly one server → direct dispatch.
  • Tool exists on multiple servers + explicit server= → explicit target wins.
  • Tool exists on multiple servers + no server= → score each candidate:
    • base_priority: reverse config order (last=1.0, then 0.9, 0.8, floor 0.1)
    • recency_boost: +0.3 if server used in last 5 successful calls
    • param_match: up to +0.2 based on parameter-name overlap with input schema
  • If top score gap is >= 0.15 → auto-dispatch to the winner.
  • If top score gap is < 0.15 → return normal { ambiguous: true, candidates: [...] } response.

Direct mode — tools are registered as todoist_find_tasks, github_list_repos, etc.

Transports

| Transport | Config key | Use case | |-----------|-----------|----------| | stdio | command, args | Local CLI servers (most common) | | sse | url, headers | Remote SSE servers | | streamable-http | url, headers | Modern HTTP-based servers |

Authentication

SSE and streamable-HTTP transports support three auth methods:

Bearer token:

{ "auth": { "type": "bearer", "token": "${MY_API_TOKEN}" } }

Custom headers:

{ "auth": { "type": "header", "headers": { "X-API-Key": "${MY_KEY}" } } }

OAuth2 Client Credentials (automatic token management):

{
  "auth": {
    "type": "oauth2",
    "clientId": "${CLIENT_ID}",
    "clientSecret": "${CLIENT_SECRET}",
    "tokenUrl": "https://provider.com/oauth/token",
    "scopes": ["read", "write"]
  }
}

OAuth2 features: automatic token acquisition, caching with expiry-aware refresh, single-attempt 401 retry, env var substitution in credentials.

Environment variables

Secrets go in ~/.mcp-bridge/.env (chmod 600 on init):

TODOIST_API_TOKEN=your-token-here
GITHUB_TOKEN=ghp_xxxxx
NOTION_TOKEN=ntn_xxxxx

Use ${VAR_NAME} in config — resolved from .env + system env.

CLI Reference

mcp-bridge                        # Start in stdio mode (default)
mcp-bridge --sse --port 3000      # Start as SSE server
mcp-bridge --http --port 3000     # Start as HTTP server
mcp-bridge --verbose              # Info-level logs to stderr
mcp-bridge --debug                # Full protocol logs to stderr
mcp-bridge --config ./my.json     # Custom config file

mcp-bridge init                   # Create ~/.mcp-bridge/ with template
mcp-bridge install <server>       # Install from catalog
mcp-bridge catalog                # List available servers
mcp-bridge servers                # List configured servers
mcp-bridge search <query>         # Search catalog by keyword
mcp-bridge update [--check]       # Check for / install updates
mcp-bridge --version              # Print version

Server Catalog

Built-in catalog with pre-configured servers:

| Server | Transport | Description | |--------|-----------|-------------| | todoist | stdio | Task management | | github | stdio | Repos, issues, PRs | | notion | stdio | Pages and databases | | stripe | stdio | Payments and billing | | linear | stdio | Project management | | google-maps | stdio | Places, geocoding, directions | | hetzner | stdio | Cloud infrastructure | | miro | stdio | Collaborative whiteboard | | wise | stdio | International payments | | tavily | stdio | AI-optimized web search | | apify | streamable-http | Web scraping and automation | | atlassian | stdio | Confluence and Jira | | chrome-devtools | stdio | Chrome browser automation | | hostinger | sse | Web hosting management |

mcp-bridge install todoist    # Interactive setup with API key prompt
mcp-bridge catalog            # Full list
mcp-bridge search payments    # Search by keyword

Library Usage

Use as a dependency in your own MCP server or OpenClaw plugin:

import { McpRouter, StandaloneServer, loadConfig } from "@aiwerk/mcp-bridge";

// Quick start
const config = loadConfig({ configPath: "./config.json" });
const server = new StandaloneServer(config, console);
await server.startStdio();
// Use the router directly
import { McpRouter } from "@aiwerk/mcp-bridge";

const router = new McpRouter(servers, config, logger);
const result = await router.dispatch("todoist", "call", "find-tasks", { query: "today" });

Architecture

┌─────────────────┐     ┌──────────────────────────────────────────────┐
│  Claude Desktop  │     │  MCP Bridge                                  │
│  Cursor          │◄───►│                                              │
│  Windsurf        │stdio│  ┌──────────┐  ┌────────┐  ┌────────────┐  │
│  OpenClaw        │ SSE │  │  Router   │  │Security│  │  Backend   │  │
│  Any MCP client  │ HTTP│  │  Intent   │─►│ Trust  │─►│  servers:  │  │
└─────────────────┘     │  │  Schema   │  │ Filter │  │  • todoist │  │
                        │  │  Compress │  │ Limit  │  │  • github  │  │
                        │  └──────────┘  └────────┘  │  • notion  │  │
                        │                            │  • stripe  │  │
                        │                            └────────────┘  │
                        └──────────────────────────────────────────────┘

Security Limitations

The built-in security layer (trust levels, tool filters, result sanitization) provides baseline protection for common threats:

  • Prompt injection patterns (known strings)
  • Oversized responses
  • Unauthorized tool access

What it does NOT cover:

  • Unicode obfuscation / homoglyph attacks
  • Sophisticated multi-step injection chains
  • Content-level PII detection

For production deployments with high security requirements, consider adding an external content filtering layer (e.g., guardrails, PII redaction service) between the bridge and your application.

Roadmap

| Status | Feature | Version | |--------|---------|---------| | ✅ | Smart Router v2 (intent, cache, batch, resolution) | 1.9.0 | | ✅ | HTTP auth (bearer, headers) | 2.0.0 | | ✅ | Configurable retries + graceful shutdown | 2.0.0 | | ✅ | OAuth2 Client Credentials | 2.1.0 | | 🔜 | Hosted bridge (bridge.aiwerk.ch) | planned | | 🔜 | Remote catalog integration | planned | | 🔜 | OpenTelemetry / Prometheus metrics | planned | | 🔜 | PII redaction | planned |

See docs/hosted-bridge-spec.md for the hosted bridge architecture.

Related

License

MIT — AIWerk