Skip to content

Commit a51357c

Browse files
committed
Restore full coverage after the BaseSession deletion
Covers MCPError.from_jsonrpc_error and the context-stream sync close() methods, whose only exercisers died with BaseSession and its tests, and restructures three test handler arms that could never take their false branch.
1 parent fe976e5 commit a51357c

5 files changed

Lines changed: 40 additions & 10 deletions

File tree

tests/client/test_session.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -875,8 +875,9 @@ async def boom(progress: float, total: float | None, message: str | None) -> Non
875875
raise RuntimeError("progress boom")
876876

877877
async def handler(msg: object) -> None:
878-
if isinstance(msg, types.ProgressNotification):
879-
delivered.set()
878+
# Only the progress notification is teed to the message handler here.
879+
assert isinstance(msg, types.ProgressNotification)
880+
delivered.set()
880881

881882
async with raw_client_session(message_handler=handler) as (session, to_client, from_client):
882883
async with anyio.create_task_group() as tg:

tests/interaction/lowlevel/test_cancellation.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,10 +268,8 @@ def respond(request_id: types.RequestId, result: types.Result) -> SessionMessage
268268
await server_write.send(respond(9999, EmptyResult()))
269269
await server_write.send(respond(ping.message.id, EmptyResult()))
270270

271-
incoming: list[IncomingMessage] = []
272-
273271
async def message_handler(message: IncomingMessage) -> None:
274-
incoming.append(message)
272+
raise NotImplementedError # unreachable: nothing is surfaced for an unknown-id response
275273

276274
async with (
277275
create_client_server_memory_streams() as ((client_read, client_write), server_streams),
@@ -285,5 +283,4 @@ async def message_handler(message: IncomingMessage) -> None:
285283

286284
assert pong == snapshot(EmptyResult())
287285
# The fabricated response was dropped silently: the ping after it still
288-
# round-tripped, and nothing was surfaced to the message handler.
289-
assert incoming == []
286+
# round-tripped, and the message handler (a tripwire) was never invoked.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""Tests for the contextvars-carrying memory-stream wrappers."""
2+
3+
import anyio
4+
import pytest
5+
6+
from mcp.shared._context_streams import create_context_streams
7+
8+
pytestmark = pytest.mark.anyio
9+
10+
11+
async def test_sync_close_closes_the_underlying_streams() -> None:
12+
"""The wrappers mirror anyio's memory streams: close() is the sync form of aclose()."""
13+
send, receive = create_context_streams[str](1)
14+
await send.send("queued")
15+
send.close()
16+
receive.close()
17+
with pytest.raises(anyio.ClosedResourceError):
18+
await send.send("after close")
19+
with pytest.raises(anyio.ClosedResourceError):
20+
await receive.receive()

tests/shared/test_exceptions.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import pytest
44

55
from mcp.shared.exceptions import MCPError, UrlElicitationRequiredError
6-
from mcp.types import URL_ELICITATION_REQUIRED, ElicitRequestURLParams, ErrorData
6+
from mcp.types import URL_ELICITATION_REQUIRED, ElicitRequestURLParams, ErrorData, JSONRPCError
77

88

99
def test_url_elicitation_required_error_create_with_single_elicitation() -> None:
@@ -162,3 +162,14 @@ def test_url_elicitation_required_error_exception_message() -> None:
162162

163163
# The exception's string representation should match the message
164164
assert str(error) == "URL elicitation required"
165+
166+
167+
def test_from_jsonrpc_error_preserves_code_message_and_data() -> None:
168+
"""Building an MCPError from a wire JSONRPCError keeps every error field."""
169+
wire = JSONRPCError(
170+
jsonrpc="2.0",
171+
id=3,
172+
error=ErrorData(code=URL_ELICITATION_REQUIRED, message="go elsewhere", data={"hint": "y"}),
173+
)
174+
error = MCPError.from_jsonrpc_error(wire)
175+
assert error.error == ErrorData(code=URL_ELICITATION_REQUIRED, message="go elsewhere", data={"hint": "y"})

tests/shared/test_streamable_http.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2247,8 +2247,9 @@ async def test_standalone_stream_teardown_mid_listen_is_not_an_error(caplog: pyt
22472247
async def message_handler(
22482248
message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception,
22492249
) -> None:
2250-
if isinstance(message, types.ResourceUpdatedNotification):
2251-
notified.set()
2250+
# Only the standalone-stream notification is teed to the handler here.
2251+
assert isinstance(message, types.ResourceUpdatedNotification)
2252+
notified.set()
22522253

22532254
async with session_manager.run():
22542255
async with (

0 commit comments

Comments
 (0)