PanDA Gateway
БесплатноНе проверенA thin, stateless MCP routing layer that exposes a single Streamable HTTP endpoint and routes tool calls to upstream servers (e.g., Bamboo MCP, PanDA MCP) based
Описание
A thin, stateless MCP routing layer that exposes a single Streamable HTTP endpoint and routes tool calls to upstream servers (e.g., Bamboo MCP, PanDA MCP) based on namespace prefixes, enabling unified access and tool catalog management.
README
A thin, stateless MCP routing layer for the PanDA ecosystem. The gateway sits
between the PanDA Monitor (or any MCP client) and upstream MCP servers such as
Bamboo MCP and PanDA MCP, exposing a single MCP endpoint (Streamable HTTP) and
routing each tools/call to the correct upstream based on a namespace prefix
in the tool name.
The gateway carries no LLM logic, no planning, no synthesis — those remain in Bamboo MCP.
PanDA Monitor (MCP client)
│ MCP / Streamable HTTP (Bearer token)
▼
┌─────────────────────────────────────────────┐
│ PanDA Gateway │
│ GatewayServer · UpstreamRegistry · Router │
└─────────────────────────────────────────────┘
│ │ │ │
Bamboo MCP PanDA MCP Rucio MCP CRIC MCP
bamboo.* panda.* (future) (future)
Developed under DOE REDWOOD WBS 2.4.3 (Bamboo MCP / Agentic PanDA).
Installation
pip install -e . # runtime
pip install -e ".[dev]" # + tests, linting, type checking
pip install -e ".[observability]" # + OpenTelemetry tracing
Requires Python ≥ 3.11.
Quick start (minimal: Bamboo MCP only, no tokens)
This is the smallest working setup: one Bamboo MCP upstream, no authentication anywhere. Use it for local development and first integration tests.
Start your Bamboo MCP server (assumed below at
http://localhost:8000/mcp).Use the provided
gateway.minimal.toml(edit theurlif Bamboo runs elsewhere):[gateway] host = "127.0.0.1" port = 8090 auth_disabled = true # no inbound token; local development only separator = "." [rag] enabled = true [[upstreams]] namespace = "bamboo" url = "http://localhost:8000/mcp" # no bearer_token_env / token_file -> unauthenticated upstream connectionRun the gateway:
panda-gateway --config gateway.minimal.toml # equivalently: python -m panda_gateway --config gateway.minimal.tomlVerify:
curl http://127.0.0.1:8090/healthz # -> {"service": "panda-gateway", "status": "ok", ..., # "upstreams": [{"namespace": "bamboo", "state": "up", "tools": N, ...}]}The MCP endpoint is
http://127.0.0.1:8090/mcp(Streamable HTTP). Point any MCP client at it; Bamboo's tools appear asbamboo.<tool>, e.g.bamboo.bamboo_answer. From Python:import anyio from mcp.client.session import ClientSession from mcp.client.streamable_http import streamablehttp_client async def main(): async with streamablehttp_client("http://127.0.0.1:8090/mcp") as (r, w, _): async with ClientSession(r, w) as session: await session.initialize() tools = await session.list_tools() print([t.name for t in tools.tools]) result = await session.call_tool( "bamboo.bamboo_answer", {"prompt": "How many jobs failed today?"} ) print(result.content[0].text) anyio.run(main)
auth_disabled = true logs a prominent warning at startup; never use it
beyond localhost or a trusted network.
Running in production
export PANDA_GATEWAY_TOKEN=... # inbound token (required)
export BAMBOO_TOKEN=... # per-upstream tokens as configured
panda-gateway --config gateway.toml
# development alternative: panda-gateway --config gateway.toml --stdio
If --config is omitted, the path is read from PANDA_GATEWAY_CONFIG.
Clients must then send Authorization: Bearer $PANDA_GATEWAY_TOKEN; only
GET /healthz stays unauthenticated.
Configuration
See gateway.example.toml for a complete annotated example. Minimal form:
[gateway]
host = "0.0.0.0"
port = 8090
bearer_token_env = "PANDA_GATEWAY_TOKEN"
separator = "." # namespace separator in tool names
[[upstreams]]
namespace = "bamboo"
url = "https://aipanda033.cern.ch:8000/mcp"
bearer_token_env = "BAMBOO_TOKEN"
tls_verify = true
ca_bundle_env = "SSL_CERT_FILE"
[[upstreams]]
namespace = "panda"
url = "https://panda-mcp.cern.ch/mcp"
token_file = "~/.panda_id_token" # OIDC token, re-read on every reconnect
use_sse = false # set true if PanDA MCP serves SSE only
Each upstream authenticates with either bearer_token_env (token from an
environment variable) or token_file — or neither, for open endpoints.
token_file understands the JSON token cache written by get-panda-token
(the id_token field is used) as well as plain-text token files, and is
re-read on every reconnect so externally renewed tokens apply automatically.
Following Bamboo's panda_mcp_session.py, the token is sent as both
Authorization and X-Auth-Token, and an optional origin = "<vo>" is sent
as the Origin header.
Note on the separator: the handover convention is bamboo.* / panda.*, but
some MCP clients validate tool names against ^[a-zA-Z0-9_-]+$ and reject
dots. If the Monitor's client stack does, set separator = "__" — routing is
separator-agnostic.
Behaviour
- Routing is a single dict lookup on the namespace prefix. Unknown
namespaces return JSON-RPC
-32602; a configured but unavailable upstream returns-32603naming the upstream, so operators can see which capability is missing. - Degraded service is visible: tools of a down upstream are absent from
tools/list; other namespaces keep working. - Health checks are two-tier: a liveness ping every 45 s and a
tools/listprobe every 12 min per upstream (both configurable). An upstreamnotifications/tools/list_changedtriggers an immediate probe. Failures cause reconnection with exponential backoff and jitter. - Tool catalog is served from a probe-refreshed cache, with a ChromaDB
semantic index exposed via the
gateway.search_toolstool — see The tool catalog below for how and why. GET /healthz(unauthenticated) returns per-upstream status JSON — machine-readable groundwork for the Phase 2 dashboard.
The tool catalog
The catalog is the gateway's answer to two different questions, and it is important to keep them apart:
- "Which tools exist right now?" — answered exactly, from a cache.
- "Which tools are relevant to what I'm trying to do?" — answered approximately, from a semantic index.
Routing is involved in neither: a tools/call is dispatched purely by its
namespace prefix (bamboo.… → Bamboo MCP), a single dict lookup. The
ChromaDB index never decides where a call goes.
How the catalog is built and kept fresh
On startup and on every reconnect, the gateway calls tools/list on each
upstream and caches the result per namespace. The cache is then refreshed by
the periodic tools/list health probe (default every 12 min per upstream)
and immediately whenever an upstream sends a tools/list_changed
notification. Downstream tools/list requests are served from this cache
with the namespace prefix applied — the gateway never fans a listing out to
the upstreams on request, so a slow or flapping upstream can never stall a
listing. Staleness is bounded by the probe interval, and in practice by the
list_changed path, since well-behaved MCP servers announce tool changes.
Availability is part of the answer: only namespaces whose upstream is
currently UP contribute tools. If PanDA MCP is down, panda.* tools simply
disappear from the listing while bamboo.* keeps working — clients see a
smaller catalog rather than errors, and operators see the gap.
Each probe result is fingerprinted (SHA-256 over every tool's name, description, and input schema). If the fingerprint is unchanged from the previous probe — the overwhelmingly common case — the probe costs one RPC and a hash comparison, and nothing downstream happens.
Why a ChromaDB index
The merged catalog will grow: Bamboo MCP plus PanDA MCP already contribute
dozens of tools, and Rucio MCP and CRIC MCP will add more. An LLM-driven
client (the Monitor's assistant, or Bamboo's planner) that receives the full
catalog on every request pays for it twice — in context-window tokens and in
tool-selection accuracy, which measurably degrades as the tool list grows.
The standard MCP tools/list has no way to say "only the tools relevant to
this prompt".
Rather than extending the protocol (a custom prompt parameter on
tools/list would tie every client to gateway-specific behaviour), the
gateway keeps the wire format plain MCP and exposes the narrowing capability
as an ordinary tool: gateway.search_tools. Under the hood, every tool is
embedded as a small document — "<namespace>.<name>: <description>" — into
a ChromaDB collection with cosine similarity. A query embeds the caller's
natural-language description of what they want ("kill a stuck task",
"why did my jobs fail on that site") and returns the nearest tool names with
their descriptions and distances. Embeddings capture meaning rather than
keywords, which is what makes this robust: "terminate a job" finds
panda.kill_task even though no word matches.
A typical client flow, entirely in standard MCP:
tools/call gateway.search_tools {"query": "kill a stuck task", "limit": 5}
→ [{"name": "panda.kill_task", "description": ..., "distance": 0.31}, …]
tools/call panda.kill_task {"task_id": 12345}
Clients that don't care (few tools, no LLM in the loop) ignore the search
tool and use tools/list as usual.
Index lifecycle
The index is a derived, disposable cache — the upstream servers remain
the sole source of truth. It is rebuilt per namespace only when a probe's
fingerprint actually changed, so the steady state does no embedding work at
all. By default it lives in memory and is rebuilt on startup; set
[rag] persist_dir to keep it across restarts. Deleting a persisted index
is always safe.
Embedding uses ChromaDB's built-in model (all-MiniLM-L6-v2, ~80 MB,
downloaded to ~/.cache/chroma/ on first indexing). Indexing failures —
for example the model download being blocked on an offline host — are
logged, retried on the next probe, and never affect tools/list, routing,
or upstream health; the gateway degrades to an empty search result rather
than a broken catalog. To run without the index entirely, set
[rag] enabled = false (which also hides gateway.search_tools).
Development
python -m pytest tests/ # 47 tests
flake8 panda_gateway tests
pyright
Tests run entirely in-process (fake upstream MCP servers over memory streams, deterministic embeddings) — no network and no model downloads.
Attribution
Session-lifecycle, health-check, retry, and observability patterns are adapted
from IBM ContextForge (mcp-contextforge-gateway, Apache-2.0). See NOTICE.
Установка PanDA Gateway
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/PalNilsson/panda-gatewayFAQ
PanDA Gateway MCP бесплатный?
Да, PanDA Gateway MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для PanDA Gateway?
Нет, PanDA Gateway работает без API-ключей и переменных окружения.
PanDA Gateway — hosted или self-hosted?
Доступен hosted-вариант: Unyly запускает сервер в облаке, локальная установка не обязательна.
Как установить PanDA Gateway в Claude Desktop, Claude Code или Cursor?
Открой PanDA Gateway на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectCompare PanDA Gateway with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
