Skip to content

mcp: a stalling roots client stalls every request on a cold legacy connection #100

Description

@carldebilly

Follow-up to a review thread on #71 (#71 (comment)).

The ordering half of that finding is fixed: a roots budget expiring no longer rethrows past the
availability fallback, because the cancellation arm in McpServerHandler now asks whether the
caller withdrew. What remains is the case where there is no fallback to lose.

The residual

On a cold initialize-era connection — no snapshot cached yet — a client that declares roots
and then answers roots/list slowly or never makes every tools/list and tools/call stall for
the full ten-second RootsRequestBudget and then fail. JoinOrStartAsync retracts the faulted
task and nothing negative-caches, so the next request starts a fresh budget. SnapshotGate is held
across it, so concurrent requests on that connection serialize and each pays its own ten seconds.

_primeFailedAt and the thirty-second PrimeRetryCooldown exist for exactly this, but they gate
PrimeCurrentAsync only. GetAsync reads them nowhere, and CallToolAsync reaches
GetSnapshotAsync before the adapter's PrimeFromServicesAsync runs, so the prime's cooldown
cannot rescue a tool call either.

Why it is not a one-line change

Routing the legacy pre-resolution through PrimeFromServicesAsync would cap the cost at one budget
per thirty seconds — it already rethrows only the caller's cancellation and applies the stand-down.
But with roots unresolved the graph would be built as "no roots" and published as current for
that version, so a reduced catalog would be cached until the next routing bump. That is a behaviour
change, not a pure fix. Publishing it stale instead is probably the right shape, and deciding that
deliberately is the point of this issue.

No test covers budget expiry today: the one slow-roots guard uses a five-second client delay under a
ten-second budget, on the request-scope path, and asserts caller-token release rather than expiry.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions