[pending-judge] feat(DC-162 rev2): "Connect your AI" — spec Streamable HTTP MCP + one-click keys

Judge r1 (grade D) blockers, each fixed and tested:

1. TRANSPORT: custom POST-only JSON-RPC → MCP 2025-03-10 Streamable HTTP
   (spec-canonical; replaced HTTP+SSE). POST answers application/json for
   requests; valid notifications -> 202 empty; malformed notifications ->
   no reply (204); batching removed in this revision -> arrays rejected
   -32600. GET (server->client stream) and DELETE (session termination)
   answer 405 + Allow: POST — the spec's explicit allowance for stateless
   request/response servers; documented in /info + modal + OpenAPI.
   Origin header validated when present (DNS-rebinding MUST); Accept
   negotiation (json required, else 406); MCP-Protocol-Version header
   checked when sent. Clients needing server-initiated streams use the
   stdio bridge (unchanged, shares mcp-core).

2. CREDENTIAL TYPE PRESERVATION: routes/mcp.js now forwards the SAME
   scheme the caller authenticated with (req.auth.type from
   middleware.js:549-586): apikey -> x-api-key, jwt -> Authorization
   Bearer (NOT dk_-coerced-x-api-key as r1 did). mcp-core createToolHandler
   accepts explicit headers. Tests assert BOTH shapes downstream
   (apikey carries x-api-key & no Authorization; jwt carries Bearer & no
   x-api-key; zero downstream calls for session denial).

3. JSON-RPC VALIDATION: validateJsonRpc — jsonrpc:'2.0', non-empty string
   method, params object-or-omitted, id string|number|null. Invalid
   Request -32600 (empty body, empty array, batch); tools/call without
   string params.name -> -32602; unknown method -> -32601; error envelopes
   carry id null when unpairable. Transport primitives rejected by the
   app-wide strict JSON parser (400; asserted).

4. REPRODUCIBILITY: status/dist/features.js + sw.js regenerated and
   committed IN THE PATCH (repo convention: DC-130/131 committed bundle
   rebuilds). Verifier greps the built bundle for aiconn- (23),
   mcp/connections (1), httpConfig (2) so a clean `git am` checkout serves
   the real frontend.

Judge r1 polish, all applied:
- verifier accepts 0_PATCH.diff / PATCH.diff naming (indexed artifact);
- connections list filtered server-side to "AI connection (...)" keys
  (new GET /mcp/connections, session-only; modal switched to it);
- URL-encoded tool path params (serviceId, containerId, action,
  templateId, category, zone) in mcp-core.

Config shape: POST /mcp/config (session-only; apikey 403) returns the
standard Streamable-HTTP card {url, transport: streamable-http, headers:
{x-api-key}, mcpProtocolVersion} + per-client note (ChatGPT note states
the public-https requirement honestly; no fabricated connector claims).

Tests: 57 green (34 route incl. auth matrix/type-preservation/transport
rules/JSON-RPC errors + 23 core). Composition-root e2e (8) boots the real
createApp with the mount. eslint 0 errors. Full suite: only the
pre-existing pdf-parse billing family red (unchanged since base 470dfaa).
This commit is contained in:
Hermes committed 2026-10-08 03:46:56 -07:00
1 parent 470dfaac2a
commit c9015b59ad
14 files changed
+2139 -747

No files matched your search

+21
View File
@@ -8,6 +8,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- **DC-162 rev2: "Connect your AI" — spec Streamable HTTP MCP + one-click keys.**
New primary-row button 🤖 Connect AI on the dashboard opens a modal that
mints a dedicated `dk_` key and returns the standard MCP Streamable-HTTP
connection card ({url, transport, headers} + per-client notes) for
Claude / ChatGPT / generic clients. Transport implements MCP 2025-03-10
Streamable HTTP: POST answers application/json (valid notifications →
202; malformed → JSON-RPC -32600 with the spec's no-reply-for-broken-
notifications rule; batching removed in this revision → rejected), GET/
DELETE return 405 + Allow: POST (stateless request/response server —
spec-permitted; clients needing server-initiated streams use the stdio
bridge). Credential TYPE preservation: apikey callers proxy x-api-key
downstream, jwt callers proxy Authorization Bearer — key scopes apply
identically at the loopback API. JSON-RPC validation: -32600 structural
(jsonrpc/method/params/id), -32602 invalid tool params, Origin DNS-
rebinding guard, MCP-Protocol-Version header check, Accept negotiation.
GET /mcp/connections lists only "AI connection (…)" keys. Tool path
params URL-encoded. Backend: routes/mcp.js (POST, GET/DELETE 405, /info,
/connections, /config), src/mcp/mcp-core.js shared by stdio + HTTP,
stale 1.15.0 version literal removed (reads package.json).
57 route+core tests + composition-root e2e proves the mount boots.
OpenAPI: 4 MCP path entries. status/dist + sw.js regenerated in-tree.
- **DC-161: Calendar widget + Radicale auto-wiring.** New top-bar calendar widget
(`status/js/calendar-widget.js`) showing upcoming appointments; settings modal for
adding CalDAV / ICS-feed sources with a Test-connection probe. Backend: