Enterprise MCP Gateway
Proxies over the GitHub MCP server to expose GitHub tools (e.g. creating issues, and other GitHub operations) through a single unified MCP endpoint, with namespacing, timeouts, and auto-restart support.
Proxies over the SQLite MCP server to expose tools for querying SQLite databases (e.g. sqlite__query) through the unified gateway, alongside other downstream servers.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Baldr MCP Gateway (mcp-baldr)
A modular reverse proxy for Model Context Protocol (MCP) servers. The gateway spawns downstream stdio MCP server subprocesses (and/or connects to remote Streamable HTTP MCP servers), aggregates their full capability surface — tools, resources, prompts, completion, logging, subscriptions — under namespaced identifiers, and exposes a single unified MCP server interface to AI clients. It also relays server→client requests (sampling, elicitation, roots) to the upstream client.
Features
Full-protocol aggregation — tools, resources, prompts, completion, logging, and subscriptions combined behind one endpoint, with server→client requests relayed to the client
Honest capability negotiation — advertises only what healthy downstreams support and what it can actually proxy; never overclaims
Automatic namespacing — tool/prompt collisions resolved via
server__nameprefixing; resources via a reversible, collision-safebaldr+<server>://URI schemeNotifications & subscriptions —
list_changedrefresh+forward, resource-update subscriptions with per-URI dedup, log-message forwarding with secret redactionServer→client relaying — sampling / elicitation / roots forwarded to the client only if the client advertised the capability; never auto-approved
Opaque, stable pagination — aggregated
*/listresults paginated with a tamper-evident cursor that stays correct as downstreams come and goResilient — crash detection, optional auto-restart, configurable timeouts, clean SIGINT/SIGTERM shutdown
Pluggable downstream transports — aggregate local
stdiosubprocesses and/or remote Streamable HTTP servers (MCP 2026-07-28), selected per server by atypefieldYAML + Pydantic config and structured JSON logging
Educational inspection — optional stderr protocol traces (see docs/LEARNING.md)
Related MCP server: MCP Proxy Server
MCP capability support
"Relayed" means the gateway is a conduit, not a provider — it forwards the request to the upstream client and relays the answer back, gated on the client's advertised capability.
Capability | Methods | Status |
Initialization |
| Aggregated / negotiated |
Tools |
| Aggregated + namespaced + routed |
Resources |
| Aggregated with reversible namespaced URIs |
Subscriptions |
| Deduplicated, translated, cleaned up on crash |
Prompts |
| Aggregated + namespaced + routed |
Completion |
| Routed (prompt-ref and resource-ref) |
Logging |
| Fan-out to downstreams; forwarded logs are redacted |
List changes | `notifications/tools | resources |
Ping |
| Answered directly |
Sampling / Elicitation / Roots |
| Relayed to the client (capability-gated, never auto-approved) |
Progress / Cancellation |
| Forwarded verbatim |
Capabilities per transport: stdio downstreams support the full surface above. Streamable HTTP downstreams support the request/response capabilities (tools, resources, prompts, completion, logging, ping); HTTP-side server→client requests, live subscriptions, and legacy fallback are on the Roadmap.
Prerequisites
Python 3.12 or later
The downstream MCP servers you want to proxy (e.g. npx, uvx, any stdio MCP server, or a remote Streamable HTTP MCP server)
Quickstart
# 1. Clone and install
git clone https://github.com/atifksiddiqui/mcp-baldr.git && cd mcp-baldr
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]" # dev (pytest, ruff); use `pip install .` for runtime only
# 2. Verify it works with the built-in mock servers
python scripts/quick_e2e.py
# 3. Run the gateway (connects an AI client via stdin/stdout)
mcp-baldr gateway_config.yamlExpected output from step 2:
[OK] Started server 'mock' (pid=12345)
[OK] Initialized: {'name': 'mock-server', 'version': '0.1.0'}
[OK] tools/list returned 2 tool(s): echo, add
[OK] tools/call echo: 'hello world'
[OK] tools/call add(7, 35): 42
[ALL CHECKS PASSED]More verification scripts: scripts/verify_multi_server.py (namespace routing), scripts/example_client.py gateway_config.yaml (example client as subprocess), scripts/learn_traffic_init.py (educational traffic). The gateway exits cleanly on EOF, SIGINT, or SIGTERM.
Architecture
AI Client (e.g. Claude)
│ stdin/stdout (JSON-RPC 2.0)
▼
┌──────────────────────────────────────────────────┐
│ Baldr MCP Gateway │
│ Config Loader ── Pydantic + YAML validation │
│ CapabilityReg. ── honest upstream aggregate │
│ Registries ── tools / resources / prompts │
│ RequestRouter ── method → handler dispatch │
│ Routing ── resolve("srv_a__echo") │
│ → ("srv_a", "echo") │
│ Subscriptions ── resource-update tracking │
│ Client relay ── sampling/elicitation/roots │
│ Transports ── stdio (subprocess) / http │
│ Monitoring ── crash detection + auto-restart │
│ Server Loop ── MCP server on own stdin/stdout │
└──────┬──────────────────────┬──────────────────────┘
│ stdio │ Streamable HTTP
▼ ▼
┌───────────┐ ┌───────────┐
│ Server A │ │ Server B │
│ (local) │ │ (remote) │
└───────────┘ └───────────┘The gateway is itself an MCP server: AI clients connect to its stdin/stdout, and it routes each request to the owning downstream (a local subprocess or a remote HTTP endpoint).
Routing. A namespaced tool/prompt name like mock_a__echo resolves to ("mock_a", "echo"); the gateway forwards the request to that server's transport using the original name. Resources use a reversible URI scheme — baldr+mock_a://<encoded> decodes back to ("mock_a", "<original uri>"). Unknown items return a JSON-RPC error; a down server returns an internal error.
Server→client requests. When a downstream asks the client to act (sampling/createMessage, elicitation/create, roots/list), the gateway relays it only if the client advertised that capability in its initialize call. Otherwise it returns a method-not-found error — it never auto-approves or fabricates a result. The gateway is strictly a conduit: it does not generate samples, approve elicitations, or invent roots.
Configuration
Define your downstream servers in gateway_config.yaml:
servers:
- name: mock_a
command: python3
args: [examples/mock_server.py]
- name: mock_b
command: python3
args: [examples/mock_server.py]
env:
MOCK_TOOLS: "alt"
logging:
level: INFO
format: json
pagination:
page_size: 100Tools are namespaced per server (mock_a__echo, mock_b__echo), so identical local names never collide. Every server entry shares these fields:
Field | Default | Description |
|
| Transport: |
| required | Unique identifier + namespace prefix (lowercase letters, digits, underscores) |
|
| Set to |
|
| Seconds to wait for a response before timing out (per-request override available) |
Stdio servers (type: stdio) additionally take:
Field | Default | Description |
| required | The executable to run |
|
| Arguments passed to the command |
|
| Environment variables merged with the current env |
|
| Restart the server if it crashes unexpectedly |
|
| Maximum restart attempts before giving up |
Top-level pagination.page_size (default 100) bounds how many items each aggregated */list page returns.
A real-world config mixing filesystem, GitHub, and SQLite servers with resiliency enabled:
servers:
- name: fs
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/docs"]
auto_restart: true
- name: github
command: npx
args: ["-y", "@modelcontextprotocol/server-github"]
env: { GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_..." }
timeout: 60
- name: sqlite
command: uvx
args: ["mcp-server-sqlite", "--db-path", "/data/app.db"]
auto_restart: trueAn AI client then sees fs__read_file, github__create_issue, sqlite__query, etc. — all through one connection, no collisions.
Remote servers over Streamable HTTP
Set type: http to aggregate a remote MCP server speaking the 2026-07-28 Streamable HTTP binding, alongside (or instead of) local stdio servers:
servers:
- name: local # stdio (the default type)
command: python3
args: [examples/mock_server.py]
- type: http
name: remote
url: https://mcp.example.com/mcp # the server's single MCP endpoint
headers: # static headers, e.g. bearer auth
Authorization: "Bearer your-token"
timeout: 60HTTP servers (type: http) take url (required) and headers (static per-request headers — the supported form of auth) in place of the stdio process fields. Header values are redacted in --learn output.
The HTTP transport proxies the request/response capabilities (tools, resources, prompts, completion, logging, ping). Server→client requests (which 2026-07-28 carries via Multi Round-Trip Requests), live subscriptions/listen change streams, OAuth, and legacy HTTP+SSE fallback are not yet implemented over HTTP — see the Roadmap. The stdio transport supports the full protocol surface.
Usage with MCP Clients
The gateway is a standard MCP stdio server; any MCP-compatible client can connect.
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"baldr": {
"command": "/path/to/mcp-baldr/.venv/bin/mcp-baldr",
"args": ["/path/to/mcp-baldr/gateway_config.yaml"]
}
}
}Cursor / VS Code (MCP extension) — point the client at mcp-baldr /path/to/gateway_config.yaml.
Programmatic (Python):
import asyncio
from mcp_baldr.gateway.server import Gateway
async def main():
async with Gateway("gateway_config.yaml") as gw:
tools = await gw.list_tools()
print(f"{len(tools)} tools available")
result = await gw.call_tool("mock_a__echo", {"text": "hello"})
print(result["content"][0]["text"])
asyncio.run(main())Resiliency
Process monitoring — a background task watches each subprocess; an unexpected exit flags the server as crashed, rejects in-flight requests, and fires the crash callback.
Auto-restart (opt-in per stdio server) — on crash, the gateway restarts the subprocess, re-discovers its tools/resources/prompts, and drops stale subscriptions so the client can re-subscribe. After
max_restarts(or whenauto_restartis off) the server's items are removed and calls to it return errors.Timeouts — per-server
timeout(default 30s), overridable per request; a slow downstream fails with a JSON-RPC error rather than hanging.Clean shutdown — SIGINT/SIGTERM stops the stdin reader (via
asyncio.StreamReader), terminates downstreams (SIGTERM → 5s grace → SIGKILL), and exits 0.
Learning & Protocol Inspection
An educational mode prints tree-formatted JSON-RPC traces to stderr (stdout protocol stream untouched) so you can watch handshakes, discovery, namespace routing, and downstream communication in real time:
mcp-baldr gateway_config.yaml --learnSee the Learning Guide for inspection configuration, session tracing, the scripts/learn_traffic_init.py traffic utility, and the honest capability matrix.
Docker
docker build -t mcp-baldr .
docker run --rm -i mcp-baldr # default config (stdin)
docker run --rm -i -v /path/to/your_config.yaml:/app/gateway_config.yaml mcp-baldr # custom configTesting
python -m pytest tests/ -v # full suite (stdio + http)
ruff check mcp_baldr/ tests/ examples/ scripts/ # lintCoverage includes core transport/registry/resiliency, a suite per protocol capability (capability negotiation, resources, prompts, subscriptions, completion/logging, server→client requests, pagination), the transport abstraction and HTTP transport (tests/test_http_transport.py, driven against a real Streamable HTTP mock server), and a full end-to-end integration walk through two live downstreams (tests/test_integration.py).
Contributing
See docs/DEVELOPMENT.md for environment setup, the daily workflow, the project structure, and the release checklist. In short: fork, pip install -e ".[dev]", make changes with tests, run ruff check + pytest, and open a PR.
Roadmap
Project history by phase lives in CHANGELOG.md. Not yet built — tracked as independent future workstreams:
Streamable HTTP — full parity with stdio — the downstream HTTP transport lands the request/response surface; still to do: server→client requests over HTTP (sampling/elicitation/roots via Multi Round-Trip Requests, SEP-2322), live change notifications via
subscriptions/listen, authentication beyond static headers (OAuth), backward-compat with the legacy HTTP+SSE / pre-2026-07-28 Streamable HTTP revisions, proactive connection-health detection (a crashed/unreachable HTTP downstream is currently caught reactively on the next request, not auto-unregistered via a health monitor like stdio crashes), and per-request timeout override (the configuredtimeoutis applied session-wide; a per-call override is accepted but not yet plumbed through the SDK session).Upstream Streamable HTTP — let AI clients connect to Baldr itself over HTTP (today Baldr serves its upstream interface over stdio only).
Persistence layer — gateway state is currently in-memory (the only on-disk state is the Learning Lab's progress file).
Interactive architecture diagram (web UI) — a browser-based explorer over the Learning Lab's event and curriculum data.
License
Apache License 2.0 — see LICENSE.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.
The OpenRouter for tools. One MCP connection gives any AI agent 254 hosted tools, pay per call.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenancePresents multiple stdio backend tool servers to an MCP client as one aggregated server, sharing those backend processes across concurrent agent sessions through a background broker while namespacing and routing each tool call to the right backend. Servers can be run shared, per-session, or lazily, with automatic restart and request ID remapping so many sessions coexist without collisions.MIT
- AlicenseNot gradedqualityAmaintenanceAggregates multiple backend MCP servers (Stdio, SSE, and Streamable HTTP) into a single unified endpoint, exposing their combined tools and resources via SSE, Streamable HTTP, or Stdio. It also offers an optional web admin UI for managing server connections, tool enablement, and live configuration reloads.120 npmMIT
- AlicenseNot gradedqualityAmaintenanceAggregates multiple backend MCP resource servers (Stdio, SSE, and Streamable HTTP) into a single unified endpoint exposed over SSE, Streamable HTTP, or Stdio. Enables routing of tool calls, resource access, and per-tool enable/disable or rename overrides through an optional web admin UI with configurable API key or bearer-token authentication.120 npmMIT
- FlicenseNot gradedqualityAmaintenanceAggregates multiple backend MCP servers (Stdio, SSE, and Streamable HTTP) and exposes their combined tools and resources through a single unified SSE or Streamable HTTP endpoint, or as a standalone Stdio server. It routes tool calls and resource requests to the right backend and offers optional web-based admin, authentication, and per-tool control.-