Repository navigation
fix(mcp): isolate slow connection teardown from other services - #4081
Merged
lyingbug merged 1 commit intoOct 10, 2026
Merged
Conversation
10 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Closing one slow MCP server can block MCP requests to unrelated services, including services in other workspaces.
Disconnect()is network I/O: the Streamable HTTP transport sends a sessionDELETEwith a timeout of up to 5 seconds, butCloseClient, stale-client replacement, and failed connection cleanup call it while holdingclientsMu.Detach connections under the lock and disconnect outside it. For stale-client replacement, let the existing connection worker close the retired session before connecting its replacement, so callers can still cancel independently. Old cleanup cannot remove a concurrently cached replacement.
Type of Change
Related Issue
No linked issue. #3706 already applies detach-then-disconnect to
CloseAll; this covers the remaining manager teardown paths. #3610 touches the same file but addresses request-scoped headers rather than this lock contention.Testing
Based on
main@602d0c38, using Go 1.26.0 on Windows:go test -race ./internal/mcp -count=1 -timeout 180sgo vet ./internal/mcpgolangci-lint run --new-from-patch=<complete-change.patch> ./...Full-backend experiment: two separate test accounts/workspaces, authenticated MCP tools/deletion APIs, SQLite, and actual HTTP MCP sessions. Workspace A's test server deliberately delays session deletion by 4 seconds. Workspace B lists tools from both an already-cached server and a new server while deletion is pending:
Recovery requests also succeeded. These are controlled fault-injection measurements, not production performance claims. MCP fixture rows were seeded directly into the isolated database because of the unrelated SQLite MCP metadata migration gap tracked in #4060; the replacement case advances the fixture's
updated_at. Connection/list/delete operations use the production backend and MCP transport. No LLM inference was needed for this transport isolation test.Additional
go test ./internal/agent/tools -count=1 -timeout 180shas the same five failing top-level tests with both the original manager and this patch:TestMCPSchemaDoesNotLoadExternalResources(Windows file URI),TestReadFileCombinesSourcesWithoutGrantingHostAccess(symlink privilege), andTestShellStdinPreservesLiteralDataForTheEntireCommand,TestSkillPythonPackageRecoveryInstallsWithoutPip,TestSkillPackageCommandsRefuseMissingDirectory(/bin/bashunavailable). Full-repository tests were not run.Checklist
git diff --check origin/main...HEADpassesPrepared with Codex assistance; findings and results above were verified locally.