Skip to content

fix(mcp): fail RPCs when transport sessions restart - #82071

Closed
embwl0x wants to merge 1 commit into
NousResearch:mainfrom
embwl0x:agent/mcp-session-invalidation
Closed

fix(mcp): fail RPCs when transport sessions restart#82071
embwl0x wants to merge 1 commit into
NousResearch:mainfrom
embwl0x:agent/mcp-session-invalidation

Conversation

@embwl0x

@embwl0x embwl0x commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • bind each managed MCP RPC to the ClientSession generation that started it
  • signal that generation before reconnect/shutdown teardown, cancelling stale tool, resource, prompt, and dynamic-refresh calls immediately
  • reject new calls during the narrow teardown window while allowing the replacement session to accept fresh calls

Root cause

A keepalive or lifecycle signal could rebuild an MCP transport while session.call_tool() was still awaiting the old SDK session. The server's session reference changed, but the in-flight coroutine had no ownership signal, so it remained attached to the old transport until the configured tool timeout (300 seconds by default).

The lifecycle now owns a per-session invalidation event. RPCs race their SDK operation against that event, cancel the stale operation when the transport is invalidated, and return a retryable error. The session reference remains available until the transport context exits, but its set invalidation event prevents new work from entering the stale generation.

Fixes #81995

Coverage

The sibling-path audit covers:

  • tools/call
  • resources/list and resources/read
  • prompts/list and prompts/get
  • notification-driven tools/list refresh
  • stdio and HTTP/SSE lifecycle exits, including reconnect, recycle, shutdown, exception, and cancellation paths

Validation

  • current-main reproduction: replacing a hung session did not wake its in-flight handler; it consumed the full configured timeout
  • scripts/run_tests.sh tests/tools/test_mcp_session_invalidation.py — 8 passed
  • scripts/run_tests.sh tests/tools/test_mcp_*.py — 459 passed across 51 files
  • remaining MCP suites under tests/ — 240 passed across 24 files (with the lockfile-pinned acp extra for ACP suites)
  • uv run ruff check . — passed
  • python scripts/check-windows-footguns.py --all — passed, 939 files scanned
  • python -m compileall -q tools/mcp_tool.py tests/tools/test_mcp_session_invalidation.py — passed
  • ty diagnostics improved from 10 on exact base to 4 on this branch; no new diagnostics
  • contribution publish gate and gitleaks — passed

No live MCP server, credentials, or user Hermes state was used; all lifecycle behavior is exercised with isolated fake transports.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists tool/mcp MCP client and OAuth duplicate This issue or pull request already exists labels Aug 8, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #48069. Both changes address in-flight MCP RPCs that outlive a reconnect or shutdown and otherwise wait for the full tool timeout. #48069 is the earlier, broader repair.

@embwl0x

embwl0x commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

Closing as a duplicate of #48069 after collaborator triage. That earlier PR owns the in-flight MCP request cancellation/reconnect repair. Our current-main implementation and broader resource, prompt, and tool-list regression coverage remain available in commit 684ebb6 if maintainers want to salvage any of those tests. Thanks for catching the overlap.

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

Labels

duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists tool/mcp MCP client and OAuth type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Stalled stdio MCP cold-spawn leaves in-flight tool call attached to dead subprocess for full 300s timeout — no fail-fast on supervisor respawn

2 participants