Skip to content

Fix completed request cancellation cleanup - #2624

Open
FU-max-boop wants to merge 1 commit into
modelcontextprotocol:v1.xfrom
FU-max-boop:fix/request-responder-cancel-leak
Open

Fix completed request cancellation cleanup#2624
FU-max-boop wants to merge 1 commit into
modelcontextprotocol:v1.xfrom
FU-max-boop:fix/request-responder-cancel-leak

Conversation

@FU-max-boop

@FU-max-boop FU-max-boop commented May 17, 2026

Copy link
Copy Markdown

Summary

Fixes #2610.

  • Suppress the cancellation raised by CancelScope.__exit__ only after a RequestResponder has completed and sent its response.
  • Keep cancellation propagation unchanged for incomplete responders.
  • Add focused regression coverage for the completed and incomplete cleanup paths.

The implementation is rebased onto the maintained v1.x line (v1.29.0), where BaseSession and this cleanup path remain active.

Validation

  • pytest tests/shared/test_session.py tests/server/test_cancel_handling.py -q — 11 passed
  • ruff format --check src/mcp/shared/session.py tests/shared/test_session.py
  • ruff check src/mcp/shared/session.py tests/shared/test_session.py
  • pyright --pythonpath .venv/bin/python src/mcp/shared/session.py tests/shared/test_session.py
  • git diff --check

@FU-max-boop
FU-max-boop force-pushed the fix/request-responder-cancel-leak branch from 5db90d2 to bb76886 Compare May 17, 2026 08:13
@BossChaos

This comment was marked as abuse.

@BossChaos

This comment was marked as abuse.

@FU-max-boop

Copy link
Copy Markdown
Author

CI note: I inspected the two failed Ubuntu jobs. They fail in existing tests that are not changed by this PR: tests/interaction/lowlevel/test_timeouts.py::test_session_level_timeout_applies_to_every_request timed out during initialize, and tests/shared/test_streamable_http.py::test_client_crash_handled hit an httpx.ConnectError while connecting to the local test server.

Both pass locally on this branch:

uv run --frozen --no-sync pytest tests/interaction/lowlevel/test_timeouts.py::test_session_level_timeout_applies_to_every_request -q
uv run --frozen --no-sync pytest tests/shared/test_streamable_http.py::test_client_crash_handled -q

I do not have permission to rerun Actions on this repository, but these look like transient CI timing/port failures. Happy to patch if they reproduce consistently.

mlorentedev added a commit to mlorentedev/hive that referenced this pull request Aug 8, 2026
Docs-only. `AGENTS.md` is the first file every agent reads in this repo,
and its account of `src/hive/_compat.py` had drifted from the code on
three points. All three were verified against the source and against the
upstream trackers before writing.

## 1. Wrong patch target

AGENTS.md said the shim patches `RequestResponder.__exit__` to swallow
an anyio `CancelledError`. It patches **`RequestResponder.respond`**, to
short-circuit the upstream `assert not self._completed`.

The `__exit__` patch was **removed** — `_compat.py`'s own docstring
records why: the issue-#75 symptom stopped reproducing on `mcp >= 1.27`,
because `Server._handle_request` catches the in-flight cancellation
before it can reach `__exit__`.

## 2. Wrong upstream tracker, and a stale instruction

AGENTS.md pointed at
[python-sdk#2610](modelcontextprotocol/python-sdk#2610)
with an escalation deadline of 2026-06-12 and an instruction to port the
fix ourselves.

The shim's fate is actually tied to
[**#2416**](modelcontextprotocol/python-sdk#2416)
— open, maintainer-bot-confirmed on both `main` and `origin/v1.x`, with
a contributor volunteering to fix it on 2026-07-11 (and our own
reproduction already posted there on 2026-06-19).

Meanwhile #2610 already has an open upstream fix PR,
[#2624](modelcontextprotocol/python-sdk#2624).
Porting it ourselves would duplicate existing work.

**This matters beyond the docs:** #127 is scoped as "if #2610 is still
silent, port the fix as our own upstream PR". That premise no longer
holds. #127 is commented separately rather than being silently rescoped
here.

## 3. Stale pin, and a guard that no longer holds

AGENTS.md quoted `mcp>=1.26,<2.0`, with the rationale that a narrow cap
stops a major `RequestResponder` refactor from silently breaking the
shim.

The actual constraint is now `mcp>=1.27,<3.0` (widened by #316). `<3.0`
admits `mcp` 2.x — precisely the major boundary the cap existed to
exclude.

I recorded this as a **lost guard** rather than silently re-narrowing
the pin: `apply()` degrades loudly (logs a warning and no-ops) rather
than silently, so this is not a live bug, and re-narrowing a dependency
range is a decision with its own consequences, not a docs edit. Flagging
it for a deliberate call.

## Verification

No code changed, so no test impact. The three claims were checked
against `src/hive/_compat.py` (docstring + `apply()`),
`pyproject.toml:49`, and the three upstream trackers via the GitHub API.
@FU-max-boop
FU-max-boop force-pushed the fix/request-responder-cancel-leak branch from edb78c0 to 6cedc87 Compare August 12, 2026 07:35
@FU-max-boop
FU-max-boop changed the base branch from main to v1.x August 12, 2026 07:36
@FU-max-boop

Copy link
Copy Markdown
Author

Rebased this fix onto v1.29.0 / v1.x and retargeted the PR because main is now v2 and no longer contains this BaseSession path. The PR is back to one commit and two files. Local validation passes: 11 focused session/cancellation tests, Ruff, Pyright, and git diff --check.

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.

RequestResponder.__exit__ leaks CancelledError on cancelled request, killing the stdio receive loop

2 participants