chore: import upstream snapshot with attribution
Deploy Docs / deploy-docs (push) Failing after 1s
Conformance Tests / client-conformance (push) Failing after 3s
Conformance Tests / server-conformance (push) Failing after 1s
GitHub Actions Security Analysis / zizmor (push) Failing after 1s
CI / checks (push) Failing after 59m20s
CI / all-green (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:10:27 +08:00
commit 49b9bb6724
992 changed files with 161690 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
"""Transport-level composed flows: multi-client isolation, reconnection, and dual-transport hosting.
These scenarios are about how the transport layer holds together across more than one connection
or more than one transport, so they connect real `Client`s against one mounted server rather than
running over the matrix.
"""
import anyio
import httpx
import pytest
from inline_snapshot import snapshot
from mcp_types import CallToolResult, LoggingMessageNotificationParams, TextContent
from mcp.client.session import LoggingFnT
from mcp.server.mcpserver import Context, MCPServer
from tests.interaction._connect import client_via_http, connect_over_sse, mounted_app
from tests.interaction._requirements import requirement
pytestmark = pytest.mark.anyio
@requirement("flow:multi-client:stateful-isolation")
async def test_concurrent_clients_on_one_stateful_server_receive_only_their_own_notifications() -> None:
"""Two clients on one stateful manager each receive only the notifications their own request produced.
Complements `test_terminating_one_session_leaves_others_working` (which proves session
independence under termination) with the notification-isolation dimension: a notification
emitted by one session's handler does not leak to another session's client.
"""
mcp = MCPServer("multi")
@mcp.tool()
async def announce(label: str, ctx: Context) -> str:
"""Emit one info-level log carrying the caller's label, then return it."""
await ctx.info(label) # pyright: ignore[reportDeprecated]
return label
received_a: list[object] = []
received_b: list[object] = []
async def collect_a(params: LoggingMessageNotificationParams) -> None:
received_a.append(params.data)
async def collect_b(params: LoggingMessageNotificationParams) -> None:
received_b.append(params.data)
async with mounted_app(mcp) as (http, _):
with anyio.fail_after(5):
async with anyio.create_task_group() as tg: # pragma: no branch
async def call(label: str, collect: LoggingFnT) -> None:
async with client_via_http(http, logging_callback=collect) as client:
await client.call_tool("announce", {"label": label})
tg.start_soon(call, "a", collect_a)
tg.start_soon(call, "b", collect_b)
assert received_a == ["a"]
assert received_b == ["b"]
@requirement("flow:session:terminate-then-reconnect")
async def test_a_fresh_connection_after_termination_obtains_a_new_session_and_operates() -> None:
"""After a client terminates, a fresh connection to the same manager gets a distinct session.
Steps: (1) connect a client and call list_tools, (2) the client exits (its DELETE fires),
(3) connect a second client to the same mounted app, (4) the second client's call_tool
succeeds and the recorded session ids show two distinct sessions were issued.
"""
mcp = MCPServer("reconnectable")
@mcp.tool()
def echo(text: str) -> str:
"""Return the input unchanged."""
return text
session_ids: list[str] = []
async def record(request: httpx.Request) -> None:
session_id = request.headers.get("mcp-session-id")
if session_id is not None:
session_ids.append(session_id)
async with mounted_app(mcp, on_request=record) as (http, _):
async with client_via_http(http) as first:
first_result = await first.list_tools()
async with client_via_http(http) as second:
second_result = await second.call_tool("echo", {"text": "again"})
assert {tool.name for tool in first_result.tools} == {"echo"}
assert second_result == snapshot(
CallToolResult(content=[TextContent(text="again")], structured_content={"result": "again"})
)
distinct = set(session_ids)
assert len(distinct) == 2, f"expected two distinct session ids across the two connections, saw {distinct}"
@requirement("flow:compat:dual-transport-server")
async def test_one_server_serves_streamable_http_and_sse_clients_concurrently() -> None:
"""One MCPServer instance serves a streamable-HTTP client and a legacy-SSE client at the same time.
The two transports have independent connection management (the streamable-HTTP session manager
versus a per-connection SSE handler), but both dispatch into the same server's request
handlers. The test connects one client over each transport against the same instance and
proves both reach the same tool. Uses MCPServer because the low-level Server has no SSE
convenience; the entry is about hosting composition, not the low-level API.
"""
mcp = MCPServer("dual")
@mcp.tool()
def echo(text: str) -> str:
"""Return the input unchanged."""
return text
async with (
mounted_app(mcp) as (http, _),
connect_over_sse(mcp) as sse_client,
client_via_http(http) as shttp_client,
):
with anyio.fail_after(5):
shttp_result = await shttp_client.call_tool("echo", {"text": "via http"})
sse_result = await sse_client.call_tool("echo", {"text": "via sse"})
assert shttp_result == snapshot(
CallToolResult(content=[TextContent(text="via http")], structured_content={"result": "via http"})
)
assert sse_result == snapshot(
CallToolResult(content=[TextContent(text="via sse")], structured_content={"result": "via sse"})
)