[recipes] Fix ob-graph MCP GET route causing SSE reconnect storm - #452
[recipes] Fix ob-graph MCP GET route causing SSE reconnect storm#452eazene wants to merge 1 commit into
Conversation
The ob-graph recipe served MCP only on POST and returned a static
`{status:"ok"}` JSON on `app.get("*")`. MCP's Streamable HTTP transport
opens a long-lived GET SSE stream for server->client messages; returning
a non-stream on that GET makes clients treat the stream as dropped and
reconnect immediately — a tight ~1 request/sec loop.
Every other Open Brain MCP function routes all methods to the transport
via `app.all("*")`. Only this recipe split GET off, so a connected client
hammered the endpoint continuously (measured ~1.4 req/sec, i.e. millions
of Edge Function invocations/month — enough to blow the 500K free tier on
its own).
Fix:
- Route all methods (GET included) to the transport (`app.post` -> `app.all`)
so the GET opens a real SSE stream.
- Remove the static wildcard health GET that intercepted the SSE stream.
- Move the health probe to dedicated paths (`/health` and `/:fn/health`,
covering Supabase's function-name path prefix) so it can't shadow "*".
Verified against a live deployment: after the fix the GET holds the SSE
stream open (was an instant non-stream response), the reconnect loop is
eliminated, and auth is now enforced on GET. `deno check` passes with the
recipe's import map.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LxP7M99m4EF5Ve7rn3SNKH
OB1 PR Gate✅ Folder structure — All files are in allowed directories Result: All 15 checks passed! Ready for human review. Post-Merge TasksThese don't block merge — they're reminders for admins after this PR lands.
|
Problem
The
ob-graphrecipe serves MCP only on POST and returns a static{"status":"ok"}JSON onapp.get("*"):MCP's Streamable HTTP transport opens a long-lived GET SSE stream for server→client messages. Returning a plain JSON body (not
text/event-stream) on that GET makes clients treat the stream as immediately dropped and reconnect in a tight loop.Every other Open Brain MCP function routes all methods to the transport with
app.all("*"). Only this recipe splits GET off, so a connected client hammers the endpoint continuously.Measured on a live deployment: ~1.4 GET requests/sec to this one function — on the order of millions of Edge Function invocations/month, enough to blow the Supabase 500K free tier by itself, with zero real tool usage.
Fix
app.post→app.all) so the GET opens a real SSE stream./healthand/:fn/health, covering Supabase's/{function-name}path prefix) so it can't intercept"*".Verification
GET /?key=…returned an instant non-stream JSON. After: the same GET holds the SSE stream open (verified via curl timing out on a held connection), the ~1/sec reconnect loop is gone, and auth is now enforced on GET.deno checkpasses with the recipe'sdeno.jsonimport map.recipes/ob-graph/index.ts), +15/−4.🤖 Generated with Claude Code
https://claude.ai/code/session_01LxP7M99m4EF5Ve7rn3SNKH