Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(coderd/x/chatd): skip MCP servers with recently timed-out connects - #28406

Closed
ibetitsmike wants to merge 1 commit into
mike/chatd-stream-silence-timeoutfrom
mike/chatd-mcp-negative-cache
Closed

feat(coderd/x/chatd): skip MCP servers with recently timed-out connects#28406
ibetitsmike wants to merge 1 commit into
mike/chatd-stream-silence-timeoutfrom
mike/chatd-mcp-negative-cache

Conversation

@ibetitsmike

@ibetitsmike ibetitsmike commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Stack context

Top of the MCP connect-stall incident stack: #28400 (connect budget), #28402 (observability), #28405 (stream silence timeout). This layer stops a bad server from taxing every generation step.

Why

chatd reconnects to every configured MCP server on every generation step. Even with #28400 capping each connect at 10s, a black-holed server (as registry.coder.com was during the incident) still costs 10 seconds per step of every chat on the pod until it recovers.

Changes

  • New mcpclient.NegativeCache: after a connect timeout, the server is skipped for 60s (NegativeCacheTTL). Entries are keyed by config ID and bound to the config's updated_at, so editing the server config retries immediately. Fast failures (auth, DNS) are deliberately not cached; they are cheap to retry and may be fixed mid-conversation.
  • Skips are logged (skipping MCP server due to recent connect timeout) and surfaced in the chat debug run's mcp_connect summary with a skipped outcome, riding the observability from feat: add MCP connect and generation-prep observability #28402.
  • Wired in prepareGeneration: configs are filtered through the cache before ConnectAll, and timeout outcomes are recorded after.

Tests

  • Unit tests for the cache: TTL expiry, config-edit busting, only-timeouts-cached, nil-cache no-op (mock clock).
  • End-to-end wiring test (TestGeneration_MCPNegativeCacheSkipsTimedOutServer): a real chatd worker with a black-holed MCP server records outcome timeout on turn 1 and skipped on turn 2, read from the debug run summaries. Red-green verified: unwiring the cache from prepareGeneration fails only this test (both turns time out).

🤖 Mux authored this PR on Mike's behalf.

chatd reconnects to every configured MCP server on every generation
step, so a black-holed server (even with the connect budget enforced)
still costs the full budget per step of every chat on the pod. Cache
connect timeouts per process and skip the server for 60 seconds,
keyed by config ID and updated_at so config edits retry immediately.
Skips are logged and recorded in the debug run's mcp_connect summary
with a "skipped" outcome. Fast failures (auth, DNS) are not cached:
they are cheap to retry and may be fixed mid-conversation.
@ibetitsmike
ibetitsmike force-pushed the mike/chatd-mcp-negative-cache branch from 76bb414 to 7afa2c9 Compare August 20, 2026 17:20
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

Dropping this PR: the negative cache is disproportionate to the residual problem. With #28400's connect budget in place, a wedged MCP server costs a bounded ~10s per generation step instead of hanging indefinitely; caching that failure for 60s only shaves the repeat cost during an active incident and is not worth the extra machinery. Closing without merge; the rest of the stack (#28400, #28402, #28405) stands on its own.

Shux acted on Mike's behalf.

@github-actions github-actions Bot locked and limited conversation to collaborators Aug 20, 2026
@ibetitsmike
ibetitsmike deleted the mike/chatd-mcp-negative-cache branch August 20, 2026 19:31
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant