Skip to content

Stream self-host MCP responses so native elicitation reaches the client - #2142

Open
vstreame wants to merge 1 commit into
UsefulSoftwareCo:mainfrom
vstreame:fix/native-elicitation-json-response
Open

vstreame wants to merge 1 commit into
UsefulSoftwareCo:mainfrom
vstreame:fix/native-elicitation-json-response

Conversation

@vstreame

Copy link
Copy Markdown

Summary

Self-hosted MCP now delivers elicitation_mode=native approvals to the client. The shared in-memory session store created its transport with enableJsonResponse: true. That buffers each tools/call answer into a single JSON body, so an elicitation/create sent during the call had no stream to travel on. The call then failed after 60s with -32001 Request timed out. The local app fixed the same bug in #1556 (#1555); this applies that fix to the store self-host uses.

  • Streaming: the transport now uses SSE streaming, the spec default.
  • Idle sweep: with streaming, handleRequest resolves as soon as the stream opens, while the tool call is still running. The sweep's in-flight claim on a session now lasts until the POST's SSE body ends. The standalone GET stream still doesn't count, so idle sessions are still evicted.
  • Timeout: native elicitations now use PAUSED_APPROVAL_TIMEOUT_MS (4 min) instead of the SDK's 60s default, since a person has to answer them.

Linked issue

Fixes #2140

Verification

  • bun run format:check: whole repo clean
  • bun run lint: whole repo, 0 warnings and 0 errors
  • bun run typecheck: all 45 turbo tasks pass
  • bun run test: not run in full. packages/hosts/mcp (252 tests) and apps/host-selfhost (158 tests) pass
  • e2e: new scenario e2e/selfhost/mcp-native-elicitation.test.ts. It passes, in 65s, on --project selfhost. No browser is involved, so there is no recording.
    • The client accepts, then declines, then accepts after 61s.
    • On main, it fails with McpError: MCP error -32001: Request timed out, the error from the issue.
    • With streaming but without the timeout change, only the 61s approval fails.

Other test changes:

  • in-memory-session-store.test.ts has a regression test that sends a real SDK client through store.dispatch. It hangs in JSON mode and passes with this change.
  • The in-flight eviction test now also checks that the session stays claimed after the streamed Response is returned but before the call finishes.
  • Store and self-host tests that parsed tool-call answers as JSON now read the SSE data: line, through a small helper (apps/host-selfhost/src/testing/mcp-sse.ts).

Checklist

  • Added a changeset (bun run changeset), or this change needs none.
  • Added or updated tests for the new behaviour.
  • No secrets, credentials, or private data in the diff.

The shared in-memory session store (self-host) created its transport with
enableJsonResponse, which buffers a POST's answer into one JSON body. An
elicitation/create issued during a tools/call had no stream to ride, so
native-mode approvals never reached the client and the call died on the
SDK's 60s request timeout. The local app fixed the same bug in UsefulSoftwareCo#1556.

Use SSE streaming (the spec default). Streaming answers a POST as soon as
its stream opens, so the idle sweep's in-flight claim now lasts until the
POST's SSE body ends rather than when handleRequest resolves; the
standalone GET stream still does not count.

Native elicitations also get PAUSED_APPROVAL_TIMEOUT_MS (4 min) instead of
the SDK's 60s default, since a human answers them.

Fixes UsefulSoftwareCo#2140

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Self-host: elicitation_mode=native approvals never reach the client (JSON response mode) and time out after 60s

1 participant