Skip to content

fix(binding-mcp): defer mcp(client) lifecycle connect until guard decides - #2426

Merged
jfallows merged 2 commits into
developfrom
fix/mcp-lifecycle-guard-connect
Aug 25, 2026
Merged

fix(binding-mcp): defer mcp(client) lifecycle connect until guard decides#2426
jfallows merged 2 commits into
developfrom
fix/mcp-lifecycle-guard-connect

Conversation

@jfallows

Copy link
Copy Markdown
Contributor

Description

McpLifecycleStream — the one persistent south connection an mcp(client) binding multiplexes every subsequent method call over — inherited the base McpStream#proceedWithRequest, which resolves the configured guard synchronously and connects regardless of the outcome. A guard whose authorization decision is asynchronous, or needs an interactive step (GuardHandler#NEEDS_PREAUTHORIZE), never actually got the chance to gate or challenge this connection: proceedWithRequest returned true before the guard had decided anything, so the connection was established unauthorized/unchallenged rather than deferred.

McpLifecycleStream now overrides proceedWithRequest to defer the actual connect until guard.reauthorize's completion callback fires, mirroring the pattern already used for the per-request McpRequestStream. A NEEDS_PREAUTHORIZE decision is surfaced as an elicitCreate challenge — the same challenge already sent for a backend-initiated elicitation — so the existing north-facing challenge relay carries it through unchanged; the login callback answer resumes the deferred connect through the existing onReauthorized handling. Resuming also grants the app-level window, matching McpRequestStream#onAcquireCompleted, since omitting it left the connection timing out and reconnecting in a loop.

Validated via zilla dump frame-level tracing against a lifecycle-style mcp(client) binding guarded by a DCR + authorization-code guard: the connection now correctly defers, emits the elicitCreate challenge, and resumes cleanly once the login callback answers it, with no reconnect storm.

./mvnw -pl runtime/binding-mcp -am test passes (178 tests, 0 failures/errors).

Fixes # (issue)


Generated by Claude Code

claude added 2 commits August 25, 2026 00:41
…ides

McpLifecycleStream -- the one persistent south connection an mcp(client)
binding multiplexes every subsequent method call over -- inherited the
base McpStream#proceedWithRequest, which resolves the configured guard
synchronously and connects regardless of the outcome. A guard whose
authorization decision is asynchronous, or needs an interactive step
(GuardHandler#NEEDS_PREAUTHORIZE), never actually got the chance to gate
or challenge this connection: proceedWithRequest returned true before the
guard had decided anything.

McpLifecycleStream now overrides proceedWithRequest to defer the actual
connect until guard.reauthorize's completion callback fires, mirroring
the pattern already used for the per-request McpRequestStream. A
NEEDS_PREAUTHORIZE decision is surfaced as an elicitCreate challenge --
the same challenge already sent for a backend-initiated elicitation --
so the existing north-facing challenge relay carries it through
unchanged; the login callback answer resumes the deferred connect
through the existing onReauthorized handling. Resuming also grants the
app-level window, matching McpRequestStream#onAcquireCompleted, since
omitting it left the connection timing out and reconnecting in a loop.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7N8Z9vABdU8pVPGthRodY
…ect fix

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7N8Z9vABdU8pVPGthRodY

@jfallows jfallows left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jfallows
jfallows merged commit 804a5e1 into develop Aug 25, 2026
2 of 3 checks passed
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.

2 participants