test(mcp): hold the progress test's upstream notification acks until they are dispatched (#49967)

The MCP SDK server answers a notification POST with 202 before it hands the notification to the session. When the gateway sends DELETE right after `notifications/initialized`, the session can close in that gap, and the SDK logs an ASGI error that fails the test at teardown. Wrap the upstream with the existing `_acknowledge_notifications_after_dispatch` helper the other SDK peers already use, so the 202 only goes out once the session has the notification

Co-authored-by: Joshua Valluru <326636767+joshua-berri@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
joshua-berri 2026-11-03 15:26:48 -08:00 • committed by GitHub
parent bb37a11b3b
commit b5952f243a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -669,7 +669,7 @@ def test_sdk_progress_and_cancellation_reach_upstream_without_losing_session(
import httpx2
import yaml
from integration._support.asgi import asgi_server
from integration._support.mcp import _drain_sse_streams
from integration._support.mcp import _acknowledge_notifications_after_dispatch, _drain_sse_streams
from integration._support.process import owned_proxy
from mcp import ClientSession, MCPError
from mcp.client.streamable_http import streamable_http_client
@ -769,7 +769,9 @@ def test_sdk_progress_and_cancellation_reach_upstream_without_losing_session(
watchers = tuple(task for task in asyncio.all_tasks() if "_shutdown_watcher" in repr(task.get_coro()))
await asyncio.wait_for(asyncio.gather(*watchers), 2)
hosted: Final = Starlette(routes=[Mount("/", app=app)], lifespan=lifespan)
hosted: Final = Starlette(
routes=[Mount("/", app=_acknowledge_notifications_after_dispatch(app))], lifespan=lifespan
)
with asgi_server(hosted) as upstream_url, httpx.Client() as client:
config: Final = tmp_path / "proxy.yaml"
config.write_text(