feat(mcp-bridge): Streamable HTTP transport (epic #87 item 14, PR1) - #105
Merged
Merged
Conversation
Implements the bridge half of ADR-0013: HTTP+SSE transport alongside stdio. PR1 of 2 — Workers / Durable-Objects shim and mTLS/OIDC auth are owed in PR2. What's in: - BOJ_TRANSPORT=stdio|http|both mode selection in main.js - lib/http-transport.js — POST /mcp + GET /mcp (SSE) + DELETE /mcp + GET /healthz; per-session UUID issuance via Mcp-Session-Id; 30-min idle expiry; bearer / none auth; loopback-refuse on auth=none + non-loopback bind; zero new deps (Deno.serve / node:http) - lib/dispatcher.js — transport-neutral dispatch core consumed by both stdio (main.js) and HTTP, same hardeningGate, same tool surface - boj://capabilities/deployment resource flagging the 5 host-local-only cartridges (browser-mcp, container-mcp, local-coord-mcp, sandbox-mcp, ffmpeg-mcp) so clients can detect Worker-incompatible capabilities - glama.json declares BOJ_TRANSPORT, BOJ_HTTP_PORT (7780), BOJ_HTTP_BIND, BOJ_HTTP_AUTH, BOJ_HTTP_AUTH_TOKENS - tests/http_transport_test.js — 26 tests covering session manager, auth, parse / size / unknown-session paths, end-to-end POST flow, bearer reject/accept, DELETE teardown, healthz, deployment resource Test plan: - node --test mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/http_transport_test.js → 41/41 pass (was 15/15 baseline; +26 new) - deno test --allow-net --allow-env --allow-read mcp-bridge/tests/ → 41/41 pass - stdio smoke (Deno + Node): unchanged behavior - http smoke (Deno + Node): Mcp-Session-Id issued, dispatch parity - bearer reject (no token / bad token) → 401; valid token → 200 - BOJ_HTTP_AUTH=none + BOJ_HTTP_BIND=0.0.0.0 refuses to start, exit 1 What's owed in PR2 (tracked): - mTLS + OIDC auth modes - Cloudflare Workers deployment guide + wrangler.toml example - Durable-Object wrapper around SessionManager - Static cartridge-manifest bundling for cold-start - Per-cartridge `requires_local` flag wired from cartridge.json (deployment resource list is static for PR1) Refs #87 (item 14), ADR-0013. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
🔍 Hypatia Security ScanFindings: 31 issues detected
View findings[
{
"reason": "Stale AI session file -- delete",
"type": "stale",
"file": "GEMINI.md",
"action": "delete",
"rule_module": "root_hygiene",
"severity": "medium"
},
{
"reason": "Issue in quality.yml",
"type": "missing_workflow",
"file": "quality.yml",
"action": "create",
"rule_module": "workflow_audit",
"severity": "high"
},
{
"reason": "Issue in security-policy.yml",
"type": "missing_workflow",
"file": "security-policy.yml",
"action": "create",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
"type": "unpinned_action",
"file": "governance.yml",
"action": "pin_sha",
"rule_module": "workflow_audit",
"severity": "high"
},
{
"reason": "Python file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/boj-server/boj-server/.github/scripts/validate-eclexiaiser.py",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/boj-server/boj-server/cartridges/sanctify-mcp/adapter/mod.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/boj-server/boj-server/cartridges/academic-workflow-mcp/adapter/mod.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/boj-server/boj-server/cartridges/fireflag-mcp/adapter/mod.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/boj-server/boj-server/cartridges/ephapax-mcp/adapter/mod.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/boj-server/boj-server/cartridges/bofig-mcp/adapter/mod.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR1 of 2 implementing ADR-0013 — Streamable HTTP transport alongside the existing stdio bridge.
BOJ_TRANSPORT=stdio(default) is unchanged.BOJ_TRANSPORT=httpexposesPOST /mcp+GET /mcp(SSE) +DELETE /mcp+GET /healthz, with per-session UUIDs inMcp-Session-Idand bearer / loopback-only-noneauth. SamehardeningGate, same tool surface, zero new dependencies (built onDeno.serve/node:http).What's in
mcp-bridge/lib/http-transport.js— listener, session manager (30-min idle expiry, SSE-fan-out seam for ADR-0011), bearer / none auth, refuse-to-start onauth=none+ non-loopback bind. Deno + Node parity. Zero deps.mcp-bridge/lib/dispatcher.js— transport-neutral JSON-RPC dispatch core extracted frommain.js. Both stdio (main.js) and HTTP (http-transport.js) call it; the hardening gate runs once, in the dispatcher.mcp-bridge/main.js—BOJ_TRANSPORT=stdio|http|bothselector. Stdio path preserved exactly; HTTP path delegates to the transport module; SIGINT / SIGTERM graceful shutdown.boj://capabilities/deploymentresource — per-deployment cartridge availability flagging the five host-local-only cartridges (browser-mcp,container-mcp,local-coord-mcp,sandbox-mcp,ffmpeg-mcp). Clients can detect Worker-incompatible capabilities up front instead of at first failed invoke. (Static list in PR1; PR2 will derive from per-cartridgerequires_localflags.)glama.jsonenv-var declarations:BOJ_TRANSPORT,BOJ_HTTP_PORT(default7780),BOJ_HTTP_BIND,BOJ_HTTP_AUTH,BOJ_HTTP_AUTH_TOKENS.mcp-bridge/tests/http_transport_test.js— 26 new tests (session manager, auth modes, parse / size / unknown-session errors, end-to-end POST flow, bearer reject/accept, DELETE teardown, healthz, deployment resource).What's deferred to PR 2
Explicitly owed; tracked here:
BOJ_HTTP_AUTH=mtls) —configFromEnvrejects this mode with a clear "owed in PR2" message.BOJ_HTTP_AUTH=oidc) — same.docs/deployment/CLOUDFLARE-WORKERS.md).requires_localflag plumbed from eachcartridge.json(the deployment resource is static in PR1).wrangler.tomlexample.Test plan
node --test mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/http_transport_test.js— 41/41 pass (baseline 15 + 26 new)deno test --allow-net --allow-env --allow-read mcp-bridge/tests/— 41/41 passecho '{...initialize...}' | deno run .../... | node ...)BOJ_TRANSPORT=http BOJ_HTTP_PORT=7780 ... main.js &+curl -X POST http://localhost:7780/mcp ...returnsMcp-Session-Idheader on Deno + NodeBOJ_HTTP_AUTH=bearer BOJ_HTTP_AUTH_TOKENS=...→ 401 with no/bad token, 200 with valid tokenBOJ_HTTP_AUTH=none BOJ_HTTP_BIND=0.0.0.0refuses to start, logs error, exits 1GET /mcpreturnsevent: readywith session id on connectSequencing notes
handleMessageis structured; every existing test still passes. The only observable change is that an uncaught dispatch exception now yields a-32603JSON-RPC error response instead of being silently swallowed by the top-level.catch(() => {})— strictly an improvement.hardeningGateper-process rate limit applies; per-session rate limiting and global queue depth are PR2.7780); same-port path-routing to the Cowboy listener (7700) tracked as a future consolidation in ADR-0013.Refs #87 (item 14), ADR-0013.
🤖 Generated with Claude Code