Contributor notes for the engine's built-in HTTP MCP server. The public doc —
endpoint, auth, setup, the tool surface — lives in docs/public/mcp/http/
(site page /connect/mcp/http); this page covers only what a contributor
needs that lives nowhere else. Deep design history: git log on
packages/ai/src/ai/modules/mcp/.
packages/ai/src/ai/modules/mcp/ is a first-class engine module, loaded at
boot via server.use('mcp') in packages/ai/src/ai/eaas.py and mounted at
/mcp on the engine web server — no separate process. The test suite is
packages/ai/tests/ai/modules/mcp/; run it with ./builder ai:test or
python -m pytest packages/ai/tests/ai/modules/mcp/.
Tool and resource handlers never touch the WS/DAP SDK directly — they depend on
the EngineClient protocol in engine.py, so the transport is swappable (a
future in-process implementation replaces one file, not the tool code). A
request that carries its own credential (API key or verified OAuth JWT) gets a
fresh per-request client under that identity, closed when the request ends;
credential-less requests share one lazy singleton built from
ROCKETRIDE_AUTH/ROCKETRIDE_APIKEY.
Every client connects to one engine URI, resolved once in initModule by
_resolve_engine_uri (which also feeds the widget CSP origin and the
upload/dropper links): explicit rocketride_uri / ROCKETRIDE_URI wins;
otherwise a loopback bind uses this engine's own ws://127.0.0.1:<port>, and
any other bind uses the MCP_RESOURCE_IDENTIFIER origin as wss:///ws://.
That URI must be encrypted when it addresses a remote engine: the caller's
own credential rides the first DAP auth frame, so a ws:///http:// URI
pointing at a non-loopback host fails boot with a message naming the value and
the variable it came from. The rule keys on the TARGET host, never on the bind,
and every resolved value goes through it — explicit or derived, on any bind. A
loopback-bound engine pointed at ws://engine.remote:5565 still puts the
credential on the network, so the bind excuses nothing. ws://127.0.0.1:5565
or http://localhost:5565 is kept whatever the bind, because that credential
never reaches a wire anyone can tap (and the shipped dist/server/.env carries
exactly such a value into the engine image).
credentials.json (sibling to the module code) maps credential-shaped node
config fields to suggested ROCKETRIDE_* variable names; it powers the
integration-readiness tools. Two builder actions maintain it:
nodes:credentials-generate— scans every node'sservices*.jsonfor credential-shaped fields and reconciles them into the catalog (runs insidenodes:build, right afternodes:docs-generate). Human-curated entries are never overwritten; newly detected fields get areview: truestub.nodes:credentials-check— the drift gate. A node with new credential fields fails this gate until the catalog covers them; areview: truestub still awaiting curation only warns.
Variable names are all the catalog and the tools ever handle — values never transit MCP.
MCP Apps widgets (running-pipelines table, dropper, trace viewer) are a vite
workspace embedded at packages/ai/src/ai/modules/mcp/apps/, registered as the
mcp-widgets builder module (build, clean, test).
mcp-widgets:build produces single-file HTML bundles into apps/dist/ and is
sequenced before ai:build (see packages/server/scripts/tasks.js); the
server advertises the MCP Apps capability only when at least one built bundle
exists on disk.