Prerequisites
Toolbox version
Not applicable. The bug is deterministic in the Python SDK transport base and does not require a live Toolbox server.
Environment
- macOS
- Python 3.13.5
- toolbox-core from current
upstream/main
Expected behavior
When concurrent first requests share the MCP transport initialization task, cancelling one request should cancel only that waiter. Other concurrent requests and later requests should still be able to complete the same one-time initialization.
Current behavior
_McpHttpTransportBase._ensure_initialized() awaits its shared _init_task directly. Cancelling any waiter propagates cancellation into that shared task, which cancels other waiters and causes every later request to raise CancelledError.
Observed output from a deterministic local reproduction:
first_waiter=cancelled
second_waiter=cancelled
shared_init_cancelled=True
later_initialization=cancelled
Steps to reproduce
- Create a concrete
_McpHttpTransportBase whose _initialize_session() waits on an asyncio.Event.
- Start two concurrent
_ensure_initialized() waiters.
- Cancel the first waiter while initialization is pending.
- Release initialization and await the second waiter.
- Call
_ensure_initialized() again.
The second and later waits are cancelled even though only the first caller was cancelled.
Root cause and proposed scope
The one shared task is awaited without cancellation isolation. The narrow fix is to await it through asyncio.shield(), with a regression test proving one cancelled waiter does not cancel the shared initialization. Genuine initialization failures should continue to propagate unchanged.
This is independent of open PRs #735, #736, #737, #740, and #741 and does not touch their files or behaviors.
Additional details
No credentials, live services, or timing sleeps are required; the regression can use controlled asyncio events. This report and proposed patch were prepared with AI assistance and manually validated against current upstream source and tests.
Prerequisites
CancelledError,asyncio.shield,_ensure_initialized, and_init_task.upstream/mainat88cc7959d9c7ebc1d92dedeae90e56fb10d2a943.Toolbox version
Not applicable. The bug is deterministic in the Python SDK transport base and does not require a live Toolbox server.
Environment
upstream/mainExpected behavior
When concurrent first requests share the MCP transport initialization task, cancelling one request should cancel only that waiter. Other concurrent requests and later requests should still be able to complete the same one-time initialization.
Current behavior
_McpHttpTransportBase._ensure_initialized()awaits its shared_init_taskdirectly. Cancelling any waiter propagates cancellation into that shared task, which cancels other waiters and causes every later request to raiseCancelledError.Observed output from a deterministic local reproduction:
Steps to reproduce
_McpHttpTransportBasewhose_initialize_session()waits on anasyncio.Event._ensure_initialized()waiters._ensure_initialized()again.The second and later waits are cancelled even though only the first caller was cancelled.
Root cause and proposed scope
The one shared task is awaited without cancellation isolation. The narrow fix is to await it through
asyncio.shield(), with a regression test proving one cancelled waiter does not cancel the shared initialization. Genuine initialization failures should continue to propagate unchanged.This is independent of open PRs #735, #736, #737, #740, and #741 and does not touch their files or behaviors.
Additional details
No credentials, live services, or timing sleeps are required; the regression can use controlled asyncio events. This report and proposed patch were prepared with AI assistance and manually validated against current upstream source and tests.