mcplex
π MCPlex β The MCP Smart Gateway
Semantic tool routing β’ Security guardrails β’ Real-time observability
Stop dumping 50k tokens of tool definitions into your LLM's context window. MCPlex intelligently routes only the tools your agent actually needs.
The Problem
Every developer building multi-agent AI systems with MCP hits the same wall:
Pain Point | Impact |
π§ Context Bloat | 20+ MCP servers = 50k+ tokens of tool definitions consuming your context window |
π No Security | No RBAC, no audit trails, tool poisoning vulnerabilities |
ποΈ Blind Operations | Can't track costs, latency, or debug wrong tool selection |
π Restart Required | Config changes require full restart in production |
πΈοΈ NΓM Complexity | Orchestrating dozens of servers is an integration nightmare |
The Solution
MCPlex is a single-binary Rust gateway that sits between your AI agent and MCP servers:
Your Agent βββ MCPlex Gateway βββ GitHub MCP (stdio β persistent)
β βββ Slack MCP (stdio β persistent)
β βββ Database MCP (HTTP)
β βββ Filesystem MCP (stdio β persistent)
βΌ
π§ Smart Routing (70-90% token savings)
π RBAC + Audit Logs + API Key Auth
π Real-time Dashboard + Prometheus
π¦ Response Caching (auto-detect read-only)
π Multi-Tenant (API key β role mapping)
π₯ Hot-reload ConfigTransport Support
MCPlex supports both MCP transport types as a first-class citizen:
Transport | Discovery | Runtime Calls | Connection Model |
Stdio | β Full MCP handshake | β Multiplexed JSON-RPC | Persistent child process (long-lived) |
Streamable HTTP | β Full MCP handshake | β Standard HTTP POST | Stateless (connection pooling) |
Stdio servers are spawned at startup and kept alive for the gateway's lifetime. The MCP handshake (initialize β notifications/initialized) runs once, then all subsequent tools/call, resources/read, and prompts/get requests are multiplexed over the same stdin/stdout pipe using JSON-RPC ID correlation.
β‘ Quick Start
1. Install (Pre-built Binary)
Download the latest release from GitHub Releases:
# Linux / macOS
curl -LO https://github.com/ModernOps888/mcplex/releases/latest/download/mcplex-linux-x86_64
chmod +x mcplex-linux-x86_64
sudo mv mcplex-linux-x86_64 /usr/local/bin/mcplex2. Build from Source
git clone https://github.com/modernops888/mcplex.git
cd mcplex
cargo build --release3. Configure
cp mcplex.toml my-config.toml
# Edit my-config.toml with your MCP serversMinimal config for stdio servers:
[gateway]
listen = "127.0.0.1:3100"
dashboard = "127.0.0.1:9090"
[router]
strategy = "semantic"
[[servers]]
name = "filesystem"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
[[servers]]
name = "memory"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-memory"]4. Run
./target/release/mcplex --config my-config.toml
# Expected output:
# π Spawning stdio server 'filesystem': npx ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
# π€ MCP handshake complete for 'filesystem'
# π‘ Server 'filesystem': 11 tools, 0 resources, 0 prompts
# π Spawning stdio server 'memory': npx ["-y", "@modelcontextprotocol/server-memory"]
# π€ MCP handshake complete for 'memory'
# π‘ Server 'memory': 3 tools, 0 resources, 0 prompts
# β‘ MCPlex gateway listening on 127.0.0.1:31005. Connect Your Agent
Point your MCP client to http://127.0.0.1:3100/mcp and open the dashboard at http://127.0.0.1:9090.
π Running as a Service (Deployment)
For persistent environments, run MCPlex as a background service to ensure it starts automatically on boot and restarts if it crashes.
Linux (systemd)
Create a systemd unit file at /etc/systemd/system/mcplex.service:
[Unit]
Description=MCPlex Gateway
After=network.target
[Service]
Type=simple
User=your_user
WorkingDirectory=/path/to/mcplex
ExecStart=/usr/local/bin/mcplex --config /path/to/mcplex/mcplex.toml
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.targetEnable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable mcplex
sudo systemctl start mcplex
sudo systemctl status mcplexmacOS (launchd)
Create a launchd plist file at ~/Library/LaunchAgents/com.modernops.mcplex.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.modernops.mcplex</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/mcplex</string>
<string>--config</string>
<string>/path/to/mcplex.toml</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/mcplex.log</string>
<key>StandardErrorPath</key>
<string>/tmp/mcplex-error.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
</dict>
</plist>Load and start the service:
launchctl load ~/Library/LaunchAgents/com.modernops.mcplex.plist
launchctl start com.modernops.mcplexLog Rotation
When running as a service, ensure you implement log rotation to prevent infinite log growth. For macOS, add an entry to /etc/newsyslog.conf; for Linux, use logrotate. MCPlex also has built-in audit log rotation configured via max_log_size_mb.
Service Troubleshooting
Issue | Resolution |
Service fails to start immediately | Check your configuration file syntax by running |
Port already in use | Verify no other service is bound to the |
| Ensure the |
Server respawn loop | Check the |
π How to Connect Your Agent
MCPlex is a transparent MCP proxy β any MCP client that supports Streamable HTTP can connect to it. Your agent talks to MCPlex as if it were a single MCP server, and MCPlex handles multiplexing, routing, and security behind the scenes.
Claude Code / Claude Desktop (Recommended β stdio bridge)
Claude Code and Claude Desktop use stdio transport. MCPlex ships a cross-platform bridge (bridge.mjs) that translates stdio β HTTP. Works on macOS, Windows, and Linux.
Add a .mcp.json to your project root (Claude Code auto-discovers it):
{
"mcpServers": {
"mcplex": {
"command": "node",
"args": ["/path/to/mcplex/bridge.mjs"],
"env": {
"MCPLEX_GATEWAY": "http://127.0.0.1:3100/mcp"
}
}
}
}For Claude Desktop, add the same config to claude_desktop_config.json.
VS Code / GitHub Copilot (agent mode)
VS Code supports MCP servers natively. Add a .vscode/mcp.json to your workspace (or run MCP: Add Server from the Command Palette) pointing at the MCPlex bridge:
{
"servers": {
"mcplex": {
"type": "stdio",
"command": "node",
"args": ["/path/to/mcplex/bridge.mjs"],
"env": {
"MCPLEX_GATEWAY": "http://127.0.0.1:3100/mcp"
}
}
}
}A ready-to-copy template lives at examples/vscode-mcp.json.
With the default meta-tool mode, Copilot's agent sees just 3 gateway tools (~200 tokens) instead of every tool definition from every connected server β the same 70β90% context savings apply inside your IDE session. Tool discovery happens on demand via mcplex_find_tools, and every call is still routed through RBAC, allowlists, audit logging, and the response cache.
Cursor / Windsurf / HTTP-capable MCP Clients
Clients that support streamable HTTP can connect directly:
{
"mcpServers": {
"mcplex-gateway": {
"url": "http://127.0.0.1:3100/mcp"
}
}
}Custom Python Agent
import requests
GATEWAY = "http://127.0.0.1:3100/mcp"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"} # Optional
# Initialize
resp = requests.post(GATEWAY, json={
"jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": {"protocolVersion": "2025-11-25", "capabilities": {},
"clientInfo": {"name": "my-agent", "version": "1.0"}}
}, headers=HEADERS)
# List all tools (MCPlex aggregates from all servers)
resp = requests.post(GATEWAY, json={
"jsonrpc": "2.0", "id": 2, "method": "tools/list"
}, headers=HEADERS)
tools = resp.json()["result"]["tools"]
# Call a tool (MCPlex routes to the right server automatically)
resp = requests.post(GATEWAY, json={
"jsonrpc": "2.0", "id": 3, "method": "tools/call",
"params": {"name": "create_issue", "arguments": {"repo": "my-repo", "title": "Bug fix"}}
}, headers=HEADERS)How It Catches Your Agent's Calls
MCPlex acts as a man-in-the-middle proxy for all MCP traffic:
Your Agent ββPOST /mcpβββ MCPlex Gateway βββ Upstream MCP Server
β (persistent stdio or HTTP)
ββ β
Auth check (constant-time API key compare)
ββ π¦ Rate limit check
ββ π§ͺ Input validation (name charset, 64KB / depth-16 args cap)
ββ π RBAC + allowlist/blocklist (role bound to API key)
ββ π Audit log (every call)
ββ π Metrics (latency, tokens, security events)Every tools/call goes through the security engine and is logged. Every tools/list goes through the semantic router. There's no way to bypass it β if your agent uses MCPlex as its MCP endpoint, all calls are intercepted, checked, and logged.
π€ Compatible AI Models
MCPlex is model-agnostic β it routes any MCP-compliant client traffic regardless of which LLM is driving it. These are the frontier models actively tested with MCPlex as of July 2026:
Model | Provider | MCP Client | Best For |
GPT-5.6 Sol | OpenAI | ChatGPT Work, custom | Flagship reasoning + agentic tasks |
GPT-5.6 Terra | OpenAI | ChatGPT Work, custom | Balanced performance / cost |
GPT-5.6 Luna | OpenAI | ChatGPT Work, custom | Cost-efficient everyday tasks |
Claude Fable 5 | Anthropic | Claude Code, Claude Desktop | Extended reasoning + code |
Claude Mythos 5 | Anthropic | Claude Code, Claude Desktop | Complex multi-step agent workflows |
Claude Sonnet 5 | Anthropic | Claude Code, Claude Desktop | High-capability, broad availability |
Gemini 3.5 Flash | Gemini Spark, custom | High-throughput, cost-efficient | |
Gemini 3.1 Pro | Gemini Spark, custom | Multimodal + Workspace integration | |
Grok 4.5 | xAI | Custom / open-weight stacks | Competitive reasoning, open ecosystem |
The meta-tool pattern (3 gateway tools, ~200 tokens) is particularly effective with models that have smaller default context budgets β MCPlex's token savings become more impactful as model costs rise.
Context savings scale with model pricing. With GPT-5.6 Sol at frontier pricing, eliminating 40k tokens of tool definitions per request translates to measurable cost reduction at scale.
π§ Semantic Tool Routing
The killer feature. Instead of dumping all tool definitions into your LLM's context, MCPlex uses a meta-tool pattern that works with every standard MCP client β no custom extensions needed:
Scenario | Without MCPlex | With MCPlex | Savings |
5 servers, 50 tools | ~10,000 tokens | ~200 tokens | 98% |
10 servers, 100 tools | ~20,000 tokens | ~200 tokens | 99% |
20 servers, 200 tools | ~40,000 tokens | ~200 tokens | 99.5% |
How It Works
When your agent calls tools/list, MCPlex returns 3 lightweight meta-tools (~200 tokens) instead of all real tools:
Agent MCPlex Gateway
β β
βββtools/listβββββββββββββββββββΊβ Returns: mcplex_find_tools, mcplex_call_tool,
β β mcplex_list_categories (~200 tokens)
β β
βββmcplex_find_toolsβββββββββββββΊβ "store a memory"
βββββββββββββββββββββββββββββββββ€ β [{name: "create_memory", desc: "...", inputSchema: {...}},
β β {name: "save_note", desc: "...", inputSchema: {...}}]
β β
βββmcplex_call_toolββββββββββββββΊβ {name: "create_memory", arguments: {...}}
βββββββββββββββββββββββββββββββββ€ β tool result (routed through security + cache + audit)mcplex_find_tools(query)β Search for tools by natural language intent. Returns matching tools with full schemas.mcplex_call_tool(name, arguments)β Execute a discovered tool. Routes through the full security/audit/cache pipeline.mcplex_list_categories()β Browse available tool categories (server groups) with tool counts.
This works with Claude Code, Claude Desktop, Cursor, Windsurf, and any other MCP client β no custom extensions or client-side plugins required.
Routing Mode
MCPlex supports three routing modes via router.mode:
Mode | Behavior | Client Compatibility |
| Returns 3 gateway meta-tools; agent discovers real tools via | β All standard MCP clients |
| Returns all real tools directly (no routing indirection) | β All standard MCP clients |
| Uses | β Custom clients only |
Routing Strategy
Within metatool and legacy modes, MCPlex uses a routing strategy to rank tools:
semanticβ Character n-gram embeddings with cosine similarity (recommended)keywordβ TF-IDF keyword matching (zero ML dependency)passthroughβ No filtering (baseline)
[router]
mode = "metatool" # "metatool", "passthrough", or "legacy"
strategy = "semantic" # "semantic", "keyword", or "passthrough"
top_k = 5 # Return top 5 most relevant tools
similarity_threshold = 0.3 # Minimum relevance score
cache_embeddings = true # Cache for faster repeated queriesπ Security Engine
Role-Based Access Control (RBAC)
[security]
enable_rbac = true
[roles.developer]
allowed_tools = ["github/*", "database/query_*"]
[roles.admin]
allowed_tools = ["*"]
[roles.readonly]
allowed_tools = ["*/list_*", "*/get_*"]
blocked_tools = ["*/delete_*", "*/drop_*"]Role trust boundary: a caller's role is only ever established server-side, from a verified
api_key/api_keysmatch β never from anything a client sends in the request body. Whenenable_rbac = trueand noapi_key/api_keysare configured, every tool call is denied by default (no role can be established), and the gateway logs a startup warning so this isn't mistaken for a bug.
Per-Server Tool Blocklists
[[servers]]
name = "database"
url = "http://localhost:8080/mcp"
blocked_tools = ["drop_table", "delete_*", "truncate_*"]Structured Audit Logging
Every tool invocation is logged as JSON Lines:
{"timestamp":"2026-04-10T10:00:00Z","event":"tool_call","tool_name":"github/create_issue","server_name":"github","duration_ms":342,"trace_id":"a1b2c3d4"}
{"timestamp":"2026-04-10T10:00:01Z","event":"tool_blocked","tool_name":"database/drop_table","reason":"security_policy","trace_id":"e5f6g7h8"}π Real-time Dashboard
Built-in observability dashboard at http://localhost:9090:

Global Metrics β Total requests, tool calls, errors, tokens saved
Per-Tool Stats β Invocation count, avg/p50/p95/p99 latency
Server Fleet β Connected servers, transport type, tool/resource/prompt counts
Live Event Stream β Real-time feed of all gateway activity with latency tags
Glassmorphism design with animated gradients. Auto-refreshes every 3 seconds. Zero configuration.
By default the dashboard's /api/* routes are unauthenticated β fine for 127.0.0.1-only binds, but if you expose the dashboard beyond localhost, set gateway.dashboard_api_key so requests need an Authorization: Bearer <key> (or X-API-Key) header:
[gateway]
dashboard_api_key = "${MCPLEX_DASHBOARD_API_KEY}"π¦ Response Caching
Avoid redundant upstream calls for read-only tools:
[cache]
enabled = true
ttl_seconds = 300 # 5 minute TTL
max_entries = 1000 # Max cached responsesCached responses are partitioned by caller role, so a cache entry from one role/tenant is never served to another. MCPlex auto-detects read-only tools by prefix (list_*, get_*, search_*, query_*, describe_*, show_*). You can override with custom patterns:
[cache]
patterns = ["my_custom_tool", "expensive_*"]Write operations (create_*, update_*, delete_*) are never cached by default.
π Multi-Tenant API Keys
Map API keys to RBAC roles for team-based access:
[api_keys."sk-dev-team-abc123"]
role = "developer"
description = "Dev team key"
[api_keys."sk-admin-xyz789"]
role = "admin"
description = "Admin key"When a request comes in with Authorization: Bearer sk-dev-team-abc123, MCPlex automatically applies the developer role's RBAC policies.
π₯ Hot-Reload Configuration
Config changes apply instantly β no restart needed:
# Edit config while MCPlex is running
vim mcplex.toml
# MCPlex automatically detects changes:
# π Config file changed, reloading...
# β
Configuration reloaded successfullyπ Full MCP Capability Support
MCPlex aggregates and forwards all three MCP capability types from upstream servers:
Capability | List | Execute/Read | Routing |
Tools |
|
| β Semantic / Keyword |
Resources |
|
| Direct routing |
Prompts |
|
| Direct routing |
Configuration Reference
[gateway]
Key | Type | Default | Description |
| string |
| MCP client connection address |
| string | β | Dashboard address (disabled if not set) |
| bool |
| Auto-reload config on file change |
| string |
| Gateway instance name |
| string | β | API key for client auth (supports |
| string | β | API key for the dashboard |
| int |
| Max requests/sec per client (0 = unlimited) |
[router]
Key | Type | Default | Description |
| string |
|
|
| string |
|
|
| int |
| Maximum tools returned per query |
| float |
| Minimum relevance score (0.0-1.0) |
| bool |
| Cache tool embeddings |
[security]
Key | Type | Default | Description |
| bool |
| Enable role-based access control |
| bool |
| Enable structured audit logging |
| string |
| Audit log file path |
| int |
| Max log file size before rotation (keeps 5 backups) |
| int |
| Max crashes within the window before respawns are blocked |
| int |
| Sliding window (seconds) for crash counting |
| int |
| Steady-state per-request timeout for HTTP and stdio upstream calls, once connected (0 = no timeout) |
| int |
| Default timeout for the initial stdio MCP handshake ( |
[[servers]]
Key | Type | Required | Description |
| string | β | Unique server name |
| string | β‘ | Executable path for stdio transport |
| list | β | Command arguments |
| string | β‘ | URL for HTTP transport |
| map | β | Environment variables (supports |
| list | β | Roles allowed to access this server |
| list | β | Tool blocklist patterns (glob) |
| list | β | Tool allowlist patterns (glob) |
| bool |
| Enable/disable this server |
| int | β | Per-server override for the initial stdio handshake timeout. Falls back to |
β‘ = One of command or url is required
Note: For stdio servers,
commandshould be the executable path (e.g.npx,/usr/bin/python3). Additional arguments go in theargsarray.
Handshake timeout vs. request timeout: the initial stdio
initializehandshake and steady-statetools/callrequests use two independent timeouts. A slow-starting server (e.g.npxfetching a package on first run) can be given a longerhandshake_timeout_secswithout loosening the timeout applied to every later tool call:[[servers]] name = "slow-npx-server" command = "npx" args = ["-y", "@some/mcp-server"] handshake_timeout_secs = 90 # default 30s, 0 = no timeout
[roles.<name>]
Key | Type | Description |
| list | Tool patterns this role can access (glob) |
| list | Tool patterns this role cannot access (glob) |
Glob Patterns: * matches any characters, ? matches a single character.
Examples: github/*, */query_*, database/get_?ser
Environment Variables
MCPlex supports ${ENV_VAR} syntax in configuration:
[[servers]]
name = "github"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
env = { GITHUB_TOKEN = "${GITHUB_TOKEN}" }Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MCPlex Gateway β
β β
β βββββββββββββββ ββββββββββββββββ ββββββββββββββββββββ β
β β Semantic β β Security β β Observability β β
β β Router β β Engine β β Collector β β
β β β β β β β β
β β β’ Embeddings β β β’ RBAC β β β’ Token Savings β β
β β β’ TopK Match β β β’ Allowlist β β β’ Latency (p99) β β
β β β’ Caching β β β’ Audit Log β β β’ Dashboard β β
β ββββββββ¬βββββββ ββββββββ¬ββββββββ ββββββββββ¬ββββββββββ β
β ββββββββββββββ¬ββββ β β
β βΌ β β
β βββββββββββββββββββββββββββββββββ β β
β β MCP Protocol Multiplexer ββββββββββ β
β β + Response Cache β β
β ββββββββββββ¬βββββββββββββββββββββ β
βββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββΌββββββββββββββββββββββ
βΌ βΌ βΌ
MCP Server MCP Server MCP Server
(stdio β (stdio β (HTTP β
persistent) persistent) stateless)CLI Reference
mcplex [OPTIONS]
Options:
-c, --config <FILE> Config file path [default: mcplex.toml]
-v, --verbose Enable verbose logging
--listen <ADDR> Override gateway listen address
--dashboard <ADDR> Override dashboard listen address
--check Validate config and exit
-h, --help Print help
-V, --version Print versionπ‘οΈ Production Hardening
API Key Authentication
Secure your gateway so only authorized agents can connect:
[gateway]
api_key = "${MCPLEX_API_KEY}" # Set via environment variableClients authenticate via header:
Authorization: Bearer your-secret-key
# or
X-API-Key: your-secret-keyHealth checks (/health) are always unauthenticated for load balancer probes.
Rate Limiting
Prevent runaway agents from overwhelming your servers:
[gateway]
rate_limit_rps = 50 # Max 50 requests/sec per client (burst: 100)Uses a per-client token bucket with 2x burst allowance. Returns 429 Too Many Requests when exceeded.
Log Rotation
Audit logs rotate automatically β they won't fill your disk:
[security]
enable_audit_log = true
audit_log_path = "./logs/audit.jsonl"
max_log_size_mb = 100 # Rotates at 100MB, keeps 5 backupsFiles rotate as: audit.jsonl β audit.jsonl.1 β ... β audit.jsonl.5 (oldest deleted).
Network Security
Bind to localhost only (default) for local agents:
[gateway]
listen = "127.0.0.1:3100" # Localhost only
dashboard = "127.0.0.1:9090" # Dashboard also localhostFor network access, use a reverse proxy (nginx/caddy) with TLS.
Prometheus Monitoring
MCPlex exposes Prometheus-compatible metrics on the dashboard port for external monitoring:
/api/metricsβ JSON metrics endpoint/api/prometheusβ Prometheus text exposition format (v0.4.0)
mcplex_requests_total 1234
mcplex_tool_calls_total 567
mcplex_errors_total 3
mcplex_tokens_saved_total 45000
mcplex_tool_duration_ms{tool="create_issue",quantile="0.95"} 142π AgentLens Integration
MCPlex pairs with AgentLens for full-stack agent observability. MCPlex handles execution, AgentLens handles visualization β together they cover 100% of the agent lifecycle. As of v0.4.0, the AgentLens bridge is fully wired and active when configured via the [agentlens] config section.
Enable the Bridge (opt-in)
Add to your mcplex.toml:
[agentlens]
enabled = true
url = "http://127.0.0.1:3000/api/ingest"
session_name = "MCPlex Gateway"Every tool call, security event, and routing decision is forwarded to AgentLens's timeline replay UI. The bridge is non-blocking (fire-and-forget) β it never slows down the gateway, even if AgentLens is offline.
Note: MCPlex works 100% independently without AgentLens. The bridge is entirely opt-in.
π₯ The Forge Integration
MCPlex pairs naturally with The Forge β a multi-agent code evolution platform that pits frontier models head-to-head on coding challenges using evolutionary algorithms.
The Forge exposes its own MCP server, which means you can add it to MCPlex just like any other server:
[[servers]]
name = "forge"
url = "http://localhost:8400/mcp" # The Forge MCP endpoint
transport = "streamable-http"
allowed_roles = ["developer", "admin"]With this setup, your agents can invoke Forge tools (multi-model arena runs, evolution sessions, research synthesis) through the same gateway β with full RBAC, audit logging, and token-saving routing applied automatically.
Forge Tool | What It Does |
| Pit GPT-5.6 / Claude Fable 5 / Gemini / Grok head-to-head on a coding challenge |
| Apply mutation operators to code and run fitness-gated selection |
| Deep-research a topic across multiple models, synthesise consensus |
| Score and rank model outputs on custom evaluation criteria |
MCPlex works 100% independently without The Forge. See github.com/ModernOps888/the-forge for setup.
Contributions are welcome! Please:
Fork the repository
Create a feature branch (
git checkout -b feature/amazing-feature)Run
cargo fmt && cargo clippy -- -D warnings && cargo testCommit your changes (
git commit -m 'Add amazing feature')Push to the branch (
git push origin feature/amazing-feature)Open a Pull Request
License
MIT License β see LICENSE for details.
π§ Recent Changes (v0.7.1 β Security Hardening & Configurable Handshake Timeout)
RBAC Self-Assertion Bypass Fixed (HIGH) β
dispatch_real_toolpreviously fell back to a client-supplied_mcplex_rolerequest param whenever no server-verified role was established (e.g. noapi_key/api_keysconfigured). Any caller could self-assertrole: "admin"and bypass RBAC entirely. The role now comes exclusively from the auth middleware's server-verifiedtrusted_role.Dashboard Authentication β
/api/*dashboard routes were previously unauthenticated. Added optionalgateway.dashboard_api_key; when set, dashboard routes require a matchingAuthorization: Bearer/X-API-Keyheader. Unset by default (backward compatible), with a startup warning to bind localhost-only in that case.Role-Partitioned Cache β The tool response cache is now partitioned by caller role, closing a cross-tenant leakage gap in multi-key deployments.
Empty-Secret Rejection β
validate_confignow rejectsgateway.api_key/dashboard_api_key/api_keysthat resolve to an empty string (e.g. from an unset${ENV_VAR}), instead of silently turning "auth required" into a no-op.Configurable Stdio Handshake Timeout (fixes #20) β the initial
initializehandshake and steady-state tool calls now use independent timeouts:security.default_handshake_timeout_secs(global, default 30s) andservers[].handshake_timeout_secs(per-server override) for the handshake, vs.security.request_timeout_secsfor steady-state calls. Fixes spurious failures on slow-cold-start servers (e.g.npxdownloading a package on first run).0 = no timeoutis now actually implemented for both.Audit Redaction List Expanded β added
pwd,auth,cookie,session,bearerto the sensitive-key redaction list.38 tests passing (up from 32) β new regression tests for the RBAC fix, cache partitioning, and handshake-timeout config.
Compatible Models Dashboard Panel β Built-in dashboard now shows a
π€ Compatible Modelspanel with colour-coded provider badges for all 9 active frontier models: GPT-5.6 Sol/Terra/Luna, Claude Fable 5/Mythos 5/Sonnet 5, Gemini 3.5 Flash/3.1 Pro, Grok 4.5.Ecosystem Footer β Dashboard footer now links to AgentLens, The Forge, and GitHub with the current MCP protocol version displayed.
Expanded Server Examples β
mcplex.tomlandexamples/updated withmemory,fetch,brave-search,postgres,sequential-thinking, andforge(The Forge MCP) server blocks.The Forge Integration β New README section with example config and tool table for connecting The Forge multi-model arena as an MCP server.
Compatible AI Models Table β Full July 2026 model matrix in the README with MCP client and use-case guidance.
Fixed Python example protocol version
2025-03-26β2025-11-25.Fixed missing CHANGELOG comparison links for v0.4.0βv0.6.0.
Audit Log Secret Redaction β Values under sensitive keys (
password,token,api_key,secret,credential, etc.) are recursively replaced with[REDACTED]before hitting disk; credentials never persist inaudit.jsonl.Spoof-Proof Rate Limiting β Client identity now comes from the real socket address (
ConnectInfo), falling back to the firstX-Forwarded-Forhop only behind proxies; header spoofing no longer evades limits.Auth/Rate-Limit Denial Telemetry β
auth_deniedandrate_limitedsecurity events now recorded in metrics and visible in the dashboard event stream.Resource/Prompt Audit Coverage β
resources/readandprompts/getare now written to the audit trail (resource_read/prompt_getevents); resource URIs capped at 2048 chars.Panic Fix β
tools/listin meta-tool mode no longer panics when fewer real tools than meta-tools are connected (saturating arithmetic).2 new audit redaction tests (31 total); live smoke test verified end-to-end.
Constant-Time API Key Verification β Key comparison is no longer vulnerable to timing side-channels.
Trusted Role Binding β Multi-tenant API keys now bind their RBAC role at the middleware layer (
X-MCPlex-Roleinjected server-side). Clients can no longer self-assert a role via_mcplex_rolewhen authenticated.Tool Call Input Validation β Tool names are restricted to a safe charset (max 128 chars); arguments are capped at 64KB with a max JSON nesting depth of 16. Malformed calls are rejected before touching upstream servers.
Request Body Limit β Gateway rejects request bodies over 1MB.
Security Telemetry β New
security_events,blocked_tool_calls, andrejected_tool_callscounters, surfaced as dashboard cards plus a live security-posture pill (RBAC/Audit status) in the header.VS Code / GitHub Copilot Integration β Documented
.vscode/mcp.jsonsetup with a ready-to-copy template (examples/vscode-mcp.json) for token-saving meta-tool routing inside IDE sessions.Dynamic Version Banner β CLI banner and dashboard header now display the crate version automatically.
Shared HTTP Client β Replaced per-call
reqwest::Client::new()with a pooled static client. Enables HTTP/2 connection reuse, TLS session resumption, and 30s request timeouts.Circuit Breaker Config β
circuit_breaker_max_crashes,circuit_breaker_window_secs, andrequest_timeout_secsare now configurable in[security](previously hardcoded).Env-Var Expansion Fix β
${ENV_VAR}expansion now uses a single-pass cursor instead of awhile-loop, preventing infinite loops if a resolved value itself contains${.Rate Limiter Memory Fix β Stale client IP entries are now pruned every 100 checks (entries idle >5 min), preventing unbounded HashMap growth.
Built with π¦ Rust for the AI agent community
If MCPlex saves your context window, give it a β
This server cannot be installed
Maintenance
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ModernOps888/mcplex'
If you have feedback or need assistance with the MCP directory API, please join our Discord server