loading…
Search for a command to run...
loading…
Standalone MCP server that multiplexes multiple MCP servers into one interface
Standalone MCP server that multiplexes multiple MCP servers into one interface
CI npm version License: MIT Status: active
Your AI, Connected to Everything. Multiplex multiple MCP servers into one interface. One config, one connection, all your tools.
🌐 aiwerkmcp.com — Learn more about the AIWerk MCP Platform
Works with Claude Code, Codex (OpenAI), Claude Desktop, Cursor, Windsurf, Cline, OpenClaw, or any MCP client.
@aiwerk/mcp-bridge is actively developed as of 2026-05-03.
The primary use case is the local install path for the AIWerk catalog at
aiwerkmcp.com. Some recipes (those marked
localOnly: true, e.g. chrome-devtools) need a browser, display, USB
device or user-specific local path that the hosted bridge cannot reach;
this package is the recommended way to run them.
It also serves:
McpRouter, transports and the OAuth2
token manager directly.Active development tracks the Universal Recipe Spec v2 alongside the
hosted bridge — new fields like localOnly, multiInstance,
auth.options[] and envBinding are being ported, and the bundled
servers/ directory is being kept in sync with the catalog. The install
helper does not fetch recipes over HTTP — recipes come from the bundled
servers/ directory only.
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:
mcp meta-tool (~99% token reduction)action=batchservers/ — copy one as a starting point for your own config, or bring your own MCP server entirely${ENV_VAR} resolution from .envnpm install -g @aiwerk/mcp-bridge
# 1. Initialize config and register with Claude Code
mcp-bridge init --register claude-code
# 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. Restart Claude Code — bridge is ready
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"bridge": {
"command": "mcp-bridge",
"args": []
}
}
}
Bundled servers now ship with recipe.json using Universal Recipe Spec v2.1 (15 servers with rich metadata: category, subcategory, origin, countries, audience, sideEffects).
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:
recipe.json per the spec above.npx @aiwerk/mcp-bridge validate-recipe ./recipe.json
config.json (v1) remains supported, but recipe.json (v2) is the recommended format going forward.
mcp-bridge now fetches recipes from catalog.aiwerk.ch instead of relying on bundled recipe files.
import { CatalogClient, bootstrapCatalog, mergeRecipesIntoConfig } from '@aiwerk/mcp-bridge';
// Bootstrap: download top recipes
await bootstrapCatalog();
// Or use the client directly
const client = new CatalogClient();
const recipe = await client.resolve('todoist');
const results = await client.search('email');
Two config options control catalog behavior:
| Option | Type | Default | Description |
|---|---|---|---|
catalog |
boolean |
true |
Whether bootstrapCatalog() fetches recipes from the remote catalog |
autoMerge |
boolean |
false |
Whether mergeRecipesIntoConfig() auto-merges cached recipes into your config |
{
"catalog": true,
"autoMerge": true,
"servers": { ... }
}
autoMerge defaults to false (opt-in) — cached recipes are not automatically added to your server list unless you explicitly enable it. This prevents servers without required credentials from being silently activated.catalog defaults to true — recipe discovery from catalog.aiwerk.ch is enabled by default. Set to false to skip all remote fetching.Breaking change (v2.9.0): Previously, all cached recipes whose env vars were present were auto-merged. Now you must set
"autoMerge": trueto restore that behavior.
Auto-discovery uses the recipe name as the config key (e.g., gohighlevel). If you need multiple instances of the same server with different credentials (e.g., two GoHighLevel subaccounts), configure them manually:
// config.json or openclaw.json
{
"ghl-client-a": {
"transport": "streamable-http",
"url": "https://services.leadconnectorhq.com/mcp/",
"headers": {
"Authorization": "Bearer ${GHL_TOKEN_A}",
"locationId": "${GHL_LOCATION_A}"
}
},
"ghl-client-b": {
"transport": "streamable-http",
"url": "https://services.leadconnectorhq.com/mcp/",
"headers": {
"Authorization": "Bearer ${GHL_TOKEN_B}",
"locationId": "${GHL_LOCATION_B}"
}
}
}
Use unique env var names (e.g., GHL_TOKEN_A instead of GHL_PIT_TOKEN) to prevent auto-discovery from adding a duplicate third entry. Manual config always takes priority over auto-discovered recipes.
Note: The bundled
servers/directory is deprecated and will be removed in v3.0.0.
Add to your MCP config:
{
"mcpServers": {
"bridge": {
"command": "mcp-bridge",
"args": ["--config", "/path/to/config.json"]
}
}
}
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 unscopedopenclaw-mcp-bridgeon npm is a different, unrelated package.
See @aiwerk/openclaw-mcp-bridge for details.
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
}
}
In router mode, tool descriptions from upstream servers can be verbose (100-300+ chars each). Schema compression truncates them to save tokens:
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.
Router mode can cache successful action=call tool results in memory using an LRU policy.
resultCache.enabled: false)maxEntries: 100, defaultTtlMs: 300000 (5 minutes)server:tool:stableJson(params)resultCache.cacheTtl (for example "todoist:find-tasks": 60000)action=refresh clears the result cache"resultCache": {
"enabled": true,
"maxEntries": 100,
"defaultTtlMs": 300000,
"cacheTtl": { "todoist:find-tasks": 60000 }
}
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 availabilityminScore: minimum confidence to return a match (0-1, default: 0.3)action=intent callRun 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.
Three layers of protection for tool results:
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, known prompt injection patterns removed (best-effort — see note below) |
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 toolsallow: whitelist mode - only these tools are visiblePrevent oversized responses from consuming your context:
{
"maxResultChars": 50000,
"servers": {
"verbose-server": {
"maxResultChars": 10000
}
}
}
_truncated: true and _originalLengthFrequently 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.
| Mode | Tools exposed | Best for |
|---|---|---|
router (default) |
Single mcp meta-tool |
3+ servers, token-conscious agents |
direct |
All tools individually | Clients with deferred/lazy tool loading (Claude Code), few servers |
Switch modes via CLI or config:
mcp-bridge init --mode direct # all tools exposed individually
mcp-bridge init --mode router # single mcp meta-tool (default)
Or set in ~/.mcp-bridge/config.json:
{ "mode": "direct" }
Router mode — the agent calls mcp(server="todoist", action="list") to discover, then mcp(server="todoist", tool="find-tasks", params={...}) to execute. Best when you have many servers and want minimal token usage.
Direct mode — all tools from all servers are registered individually as todoist_find_tasks, github_list_repos, etc. The bridge still provides unified config, catalog install, OAuth2, security, retries, and reconnection. Ideal for clients that support deferred/lazy tool loading, where tools are registered but not loaded into context until needed.
When action="call" is used without server=, mcp-bridge can resolve collisions automatically.
server= → explicit target wins.server= → score each candidate:last=1.0, then 0.9, 0.8, floor 0.1)+0.3 if server used in last 5 successful calls+0.2 based on parameter-name overlap with input schema>= 0.15 → auto-dispatch to the winner.< 0.15 → return normal { ambiguous: true, candidates: [...] } response.Direct mode — tools are registered as todoist_find_tasks, github_list_repos, etc.
| 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 |
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.
OAuth2 Authorization Code + PKCE (interactive browser login):
For MCP servers behind enterprise SSO or user-level OAuth2 that require browser-based login (desktop/laptop):
{
"auth": {
"type": "oauth2",
"grantType": "authorization_code",
"authorizationUrl": "https://auth.example.com/authorize",
"tokenUrl": "https://auth.example.com/oauth/token",
"clientId": "optional-public-client-id",
"scopes": ["read", "write"]
}
}
Then authenticate via CLI:
mcp-bridge auth login my-server # Opens browser, completes OAuth2 flow
mcp-bridge auth status # Check token status for all servers
mcp-bridge auth logout my-server # Remove stored token
Features:
clientSecret needed for public clients~/.mcp-bridge/tokens/ (chmod 600), survive bridge restartsrefresh_token grantOAuth2 Device Code (headless environments — VPS, Docker, SSH, CI):
For environments without a browser. You authenticate on a separate device using a short code:
{
"auth": {
"type": "oauth2",
"grantType": "device_code",
"deviceAuthorizationUrl": "https://github.com/login/device/code",
"tokenUrl": "https://github.com/login/oauth/access_token",
"clientId": "your-app-id",
"scopes": ["repo", "read:org"]
}
}
mcp-bridge auth login my-server
# ──────────────────────────────────────────
# Device authentication for "my-server"
#
# 1. Open: https://github.com/login/device
# 2. Enter code: ABCD-1234
# ──────────────────────────────────────────
# Waiting for authorization...
Features:
interval and slow_down responses~/.mcp-bridge/tokens/ with auto-refreshSecrets 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.
You can also set env vars via CLI or at runtime:
mcp-bridge set-env TODOIST_API_TOKEN your-token-here
Or via the agent: mcp(action="set-env", params={key: "TODOIST_API_TOKEN", value: "..."})
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 debug metadata in tool responses
mcp-bridge --config ./my.json # Custom config file
mcp-bridge init # Create ~/.mcp-bridge/ with template config
mcp-bridge init --register claude-code # Init + register with Claude Code
mcp-bridge init --register codex # Init + register with Codex
mcp-bridge init --register cursor # Init + register with Cursor
mcp-bridge init --register windsurf # Init + register with Windsurf
mcp-bridge install <server> # Install from online catalog
mcp-bridge set-env <KEY> <value> # Set an API key in ~/.mcp-bridge/.env
mcp-bridge catalog # Browse 100+ available servers
mcp-bridge servers # List configured servers and current mode
mcp-bridge search <query> # Search catalog by keyword
mcp-bridge update [--check] # Check for / install updates
mcp-bridge --version # Print version
mcp-bridge auth login <server> # OAuth2 browser login (Authorization Code + PKCE)
mcp-bridge auth logout <server> # Remove stored token
mcp-bridge auth status # Show auth status for all servers
When connected to an MCP client (Claude Code, Codex, Cursor, etc.), the bridge exposes a single mcp meta-tool. Agents can discover and install servers at runtime:
mcp(action="search", params={query: "task management"}) # Search catalog
mcp(action="install", params={name: "todoist"}) # Install server (persisted to config)
mcp(action="set-env", params={key: "TODOIST_API_TOKEN", value: "your-key"}) # Set API key
mcp(action="catalog") # Browse all servers
mcp(action="list", server="todoist") # Discover tools on a server
mcp(action="call", server="todoist", tool="find-tasks", params={query: "today"})
The tool description automatically includes all connected servers with their descriptions, so agents know which server to use for what. New servers installed via the bridge are persisted to ~/.mcp-bridge/config.json and survive restarts.
Browse and install from the AIWerk MCP Catalog with 100+ verified, signed recipes:
mcp-bridge catalog # Browse all 100+ servers
mcp-bridge search payments # Search by keyword
mcp-bridge install todoist # Install from catalog
Popular servers include: todoist, github, notion, stripe, linear, google-maps, slack, supabase, mongodb, playwright, docker, and many more.
All catalog recipes are Ed25519 signed and security-audited. The bridge verifies signatures before installation.
Note: The bundled
servers/directory is deprecated. All servers now come from the online catalog.
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" });
┌─────────────────┐ ┌──────────────────────────────────────────────┐
│ 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 │ │
│ └────────────┘ │
└──────────────────────────────────────────────┘
The built-in security layer (trust levels, tool filters, result sanitization) provides best-effort baseline protection for common threats:
⚠️
trust: "sanitize"is NOT a security boundary. It catches common/known injection patterns but is trivially bypassable via Unicode homoglyphs, zero-width characters, base64 encoding, or multi-step injection chains. Treat it as defense-in-depth, not a sole protection layer.
What it does NOT cover:
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.
| 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 |
| ✅ | OAuth2 Authorization Code + PKCE | 2.5.0 |
| ✅ | OAuth2 Device Code flow (headless) | 2.6.0 |
| ✅ | Agent-driven discovery (search/install at runtime) | 2.8.6 |
| 🔜 | Hosted bridge (bridge.aiwerk.ch) | planned |
| ✅ | Remote catalog integration | 2.8.0 |
| ✅ | CLI online catalog | 2.8.23 |
| ✅ | Debug mode (_debug metadata) | 2.8.4 |
| 🔜 | OpenTelemetry / Prometheus metrics | planned |
| 🔜 | PII redaction | planned |
| 🔜 | Skill system (recipe.json skills for agents) | planned |
See docs/hosted-bridge-spec.md for the hosted bridge architecture.
MIT — AIWerk
Run in your terminal:
claude mcp add bridge --env GITHUB_PERSONAL_ACCESS_TOKEN="" --env GITHUB_TOKEN="" --env NOTION_API_KEY="" --env NOTION_TOKEN="" --env TODOIST_API_KEY="" --env TODOIST_API_TOKEN="" -- npx -y @aiwerk/mcp-bridgeYes, Bridge MCP is free — one-click install via Unyly at no cost.
Yes, it requires environment variables: GITHUB_PERSONAL_ACCESS_TOKEN, GITHUB_TOKEN, NOTION_API_KEY, NOTION_TOKEN, TODOIST_API_KEY, TODOIST_API_TOKEN. Unyly injects them into the config during install.
Self-hosted: the server runs locally on your machine via the install command above.
Open Bridge on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
pro tip
Just installed Bridge? Say to Claude: "remember why I installed Bridgeand what I want to try" — it'll save into your Vault.
how this works →CSA PROJECT - FZCO © 2026 IFZA Business Park, DDP, Premises Number 31174 - 001
Security
Review before useWill ask for:
GITHUB_PERSONAL_ACCESS_TOKENGITHUB_TOKENNOTION_API_KEYNOTION_TOKENTODOIST_API_KEYTODOIST_API_TOKENAutomated heuristic from public metadata — not a security guarantee.