The MCP HTTP server serves connections sequentially on the single
current-thread runtime (the browser session is !Send). The SSE branch
(GET /mcp, Accept: text/event-stream) ran an infinite keep-alive ping loop
inline, so handle_connection never returned and the accept loop never ran
again — one open SSE stream hung every later POST /mcp tool call
indefinitely (functional break + trivial unauthenticated DoS).
The keep-alive carries no browser state and its write half is Send + 'static,
so spawn the ping loop on its own task and return from the handler. POST
handling stays serialized (correct for a single browser session), but a live
SSE stream no longer blocks the accept loop.
Fixes#489
Reject request bodies above 16 MiB with 413 before allocating, stopping an
unauthenticated Content-Length OOM (vec![0u8; len]). Add an optional,
default-off OBSCURA_MCP_ALLOWED_ORIGINS allowlist; unset keeps the permissive
default so issue #175 hosted dashboards are unaffected.
Add a --host CLI flag to 'obscura mcp --http' so the server can bind to
addresses other than 127.0.0.1 (e.g. 0.0.0.0 for Docker Compose sidecar
deployments). The default remains 127.0.0.1 for security.
Closes#253
The OPTIONS preflight hardcoded Access-Control-Allow-Headers to just
Content-Type. Any browser MCP client sending mcp-protocol-version (per
the MCP spec) or Authorization / X-API-Key (typical for hosted
deployments) failed the preflight and the browser blocked the actual
request.
Add those three to the allow-list and a 24h Max-Age so browsers don't
re-preflight every call. Adds an integration test that drives the
server with a real preflight request.
Closes#175.