Skip to content

fix(mcp): bound + parallelize provider discovery so one bad provider can't stall the aggregate#373

Merged
mjudeikis merged 1 commit into
mainfrom
fix/aggregate-provider-discovery-resilience
Jun 29, 2026
Merged

fix(mcp): bound + parallelize provider discovery so one bad provider can't stall the aggregate#373
mjudeikis merged 1 commit into
mainfrom
fix/aggregate-provider-discovery-resilience

Conversation

@mjudeikis

Copy link
Copy Markdown
Contributor

Follow-up to #372. That PR fixed the kuery provider so it stops crashing; this one hardens the hub-side aggregator so any misbehaving provider — present or future — can't take the whole MCP query path down.

What was already safe

registerProviderTools already isolates a provider that errors: a failed tools/list (EOF, 405, …) is logged and continued, and AddTool panics are recovered per-tool. So a crashing provider couldn't poison the aggregate tool list.

The remaining gap: a slow / hung provider

Discovery was a sequential loop, and each tools/list was bounded only by the federation client's 15s timeout. So a single provider whose /mcp hangs would:

  • stall the entire aggregate tools/list for up to 15s, and
  • serialize every healthy provider behind it (worst case ≈ 15s × N).

From the caller's perspective, one hung provider takes the whole query down.

Fix

Fan out per-provider discovery concurrently, each under a short providerDiscoveryTimeout (8s, well under the 15s call timeout):

  • A hung provider now costs at most one short deadline, in parallel with the others — never blocks the healthy ones — then drops out of this round and reappears on the next once it recovers.
  • Tools are still registered sequentially in the original provider order after the fan-out (the SDK's AddTool isn't guaranteed goroutine-safe), so the aggregate tool list stays deterministic.
  • Each discovery goroutine recovers panics individually.

Test

TestRegisterProviderTools_HungProviderDoesNotBlockAggregate: a healthy provider + a provider whose /mcp hangs (released only at cleanup). Asserts the healthy provider's tool still registers, the hung one drops out, and the aggregate builds in well under the client timeout. providerDiscoveryTimeout is a var so the test can lower it to run in ms.

🤖 Generated with Claude Code

…can't stall the aggregate

The hub-side federation already skipped providers that errored (EOF/405) and
recovered AddTool panics, so a crashing provider couldn't poison the aggregate
tool list. The remaining gap was a SLOW/HUNG provider: discovery was a sequential
loop with the client's 15s per-request timeout, so one hung provider stalled the
whole tools/list — and serialized every healthy provider behind it.

Fan out per-provider tools/list concurrently, each under a short
providerDiscoveryTimeout (8s, well under the 15s call timeout). A hung provider
now costs at most that one deadline, in parallel, then drops out of this round
and reappears on the next once it recovers. Tools are still registered
sequentially in the original provider order (AddTool isn't guaranteed
goroutine-safe) so the aggregate tool list stays deterministic. Discovery
goroutines also recover panics individually.

Adds a regression test: a healthy provider + a provider whose /mcp hangs; asserts
the healthy provider's tool still registers, the hung one drops out, and the
aggregate builds in well under the client timeout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 29, 2026 16:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@mjudeikis mjudeikis merged commit c38d93b into main Jun 29, 2026
17 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