Install
$ agentstack add mcp-aiwerk-mcp-bridge ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
@aiwerk/mcp-bridge
[](https://github.com/AIWerk/mcp-bridge/actions/workflows/ci.yml) [](https://www.npmjs.com/package/@aiwerk/mcp-bridge) [](https://opensource.org/licenses/MIT) [](#status)
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.
Status
@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:
- OpenClaw plugin users via
@aiwerk/openclaw-mcp-bridge,
which embeds this library and ships its own recipe-install tooling.
- Self-hosted / offline deployments where the hosted service is not an
option.
- Library consumers that import
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.
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
mcpmeta-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, OAuth2 Client Credentials, and OAuth2 Authorization Code + PKCE (interactive browser login)
- 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
- Bundled recipe examples: 14+ reference server recipes in
servers/— copy one as a starting point for your own config, or bring your own MCP server entirely - Zero config secrets in files:
${ENV_VAR}resolution from.env
Install
npm install -g @aiwerk/mcp-bridge
Quick Start
# 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
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.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.
- Spec: [
docs/universal-recipe-spec.md](docs/universal-recipe-spec.md) - Runtime compatibility: v1 and v2 are both supported
- Existing v1-only servers continue to work unchanged
For third-party recipe authors:
- Author
recipe.jsonper the spec above. - 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.
Catalog Integration (v2.8.0+)
mcp-bridge now fetches recipes from catalog.aiwerk.ch instead of relying on bundled recipe files.
How it works
- First run: Automatically downloads the top 15 most popular recipes
- On-demand: When you install a server, it checks the catalog first
- Offline: Falls back to local cache if catalog is unreachable
API
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');
Catalog & Auto-Merge Options
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": { ... }
}
autoMergedefaults tofalse(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.catalogdefaults totrue— recipe discovery from catalog.aiwerk.ch is enabled by default. Set tofalseto 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": true to restore that behavior.
Multiple instances of the same server
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.
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=schemato 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=refreshclears 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 availabilityminScore: minimum confidence to return a match (0-1, default: 0.3)- Index is built lazily on first
action=intentcall
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, known prompt injection patterns removed (best-effort — see note below) |
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 toolsallow: 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: trueand_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 | 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.
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, then0.9,0.8, floor0.1)
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: AIWerk
- Source: AIWerk/mcp-bridge
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.