|
13 | 13 | from mcp import MCPError, types |
14 | 14 | from mcp.client import ClientSession |
15 | 15 | from mcp.server import Server, ServerRequestContext |
16 | | -from mcp.shared.memory import create_client_server_memory_streams |
| 16 | +from mcp.shared.memory import MessageStream, create_client_server_memory_streams |
17 | 17 | from mcp.shared.message import SessionMessage |
18 | 18 | from mcp.types import ( |
19 | 19 | CallToolResult, |
@@ -170,63 +170,65 @@ async def test_a_response_for_an_unknown_request_id_surfaces_to_the_message_hand |
170 | 170 | scripted-peer mechanism is the in-memory stream pair, not because the behaviour is |
171 | 171 | transport-specific. |
172 | 172 | """ |
173 | | - async with create_client_server_memory_streams() as (client_streams, server_streams): |
174 | | - client_read, client_write = client_streams |
175 | | - server_read, server_write = server_streams |
176 | | - |
177 | | - async def scripted_server() -> None: |
178 | | - def respond(request_id: types.RequestId, result: types.Result) -> SessionMessage: |
179 | | - return SessionMessage( |
180 | | - JSONRPCResponse( |
181 | | - jsonrpc="2.0", |
182 | | - id=request_id, |
183 | | - # Serialized exactly as a real server serializes results onto the wire. |
184 | | - result=result.model_dump(by_alias=True, mode="json", exclude_none=True), |
185 | | - ) |
186 | | - ) |
187 | 173 |
|
188 | | - init = await server_read.receive() |
189 | | - assert isinstance(init, SessionMessage) |
190 | | - assert isinstance(init.message, JSONRPCRequest) |
191 | | - assert init.message.method == "initialize" |
192 | | - await server_write.send( |
193 | | - respond( |
194 | | - init.message.id, |
195 | | - InitializeResult( |
196 | | - protocol_version="2025-11-25", |
197 | | - capabilities=ServerCapabilities(), |
198 | | - server_info=Implementation(name="scripted", version="0.0.1"), |
199 | | - ), |
| 174 | + async def scripted_server(streams: MessageStream) -> None: |
| 175 | + server_read, server_write = streams |
| 176 | + |
| 177 | + def respond(request_id: types.RequestId, result: types.Result) -> SessionMessage: |
| 178 | + return SessionMessage( |
| 179 | + JSONRPCResponse( |
| 180 | + jsonrpc="2.0", |
| 181 | + id=request_id, |
| 182 | + # Serialized exactly as a real server serializes results onto the wire. |
| 183 | + result=result.model_dump(by_alias=True, mode="json", exclude_none=True), |
200 | 184 | ) |
201 | 185 | ) |
202 | 186 |
|
203 | | - initialized = await server_read.receive() |
204 | | - assert isinstance(initialized, SessionMessage) |
205 | | - assert isinstance(initialized.message, JSONRPCNotification) |
206 | | - assert initialized.message.method == "notifications/initialized" |
207 | | - |
208 | | - ping = await server_read.receive() |
209 | | - assert isinstance(ping, SessionMessage) |
210 | | - assert isinstance(ping.message, JSONRPCRequest) |
211 | | - assert ping.message.method == "ping" |
212 | | - # First answer with a fabricated id that matches nothing in flight, then the real id. |
213 | | - await server_write.send(respond(9999, EmptyResult())) |
214 | | - await server_write.send(respond(ping.message.id, EmptyResult())) |
215 | | - |
216 | | - incoming: list[IncomingMessage] = [] |
217 | | - |
218 | | - async def message_handler(message: IncomingMessage) -> None: |
219 | | - incoming.append(message) |
220 | | - |
221 | | - async with anyio.create_task_group() as task_group: |
222 | | - task_group.start_soon(scripted_server) |
223 | | - async with ClientSession(client_read, client_write, message_handler=message_handler) as session: |
224 | | - with anyio.fail_after(5): |
225 | | - await session.initialize() |
226 | | - pong = await session.send_request(PingRequest(), EmptyResult) |
227 | | - |
228 | | - assert pong == snapshot(EmptyResult()) |
229 | | - assert len(incoming) == 1 |
230 | | - assert isinstance(incoming[0], RuntimeError) |
231 | | - # The full message embeds the response object's repr; only the prefix is stable. |
232 | | - assert str(incoming[0]).startswith("Received response with an unknown request ID:") |
| 187 | + init = await server_read.receive() |
| 188 | + assert isinstance(init, SessionMessage) |
| 189 | + assert isinstance(init.message, JSONRPCRequest) |
| 190 | + assert init.message.method == "initialize" |
| 191 | + await server_write.send( |
| 192 | + respond( |
| 193 | + init.message.id, |
| 194 | + InitializeResult( |
| 195 | + protocol_version="2025-11-25", |
| 196 | + capabilities=ServerCapabilities(), |
| 197 | + server_info=Implementation(name="scripted", version="0.0.1"), |
| 198 | + ), |
| 199 | + ) |
| 200 | + ) |
| 201 | + |
| 202 | + initialized = await server_read.receive() |
| 203 | + assert isinstance(initialized, SessionMessage) |
| 204 | + assert isinstance(initialized.message, JSONRPCNotification) |
| 205 | + assert initialized.message.method == "notifications/initialized" |
| 206 | + |
| 207 | + ping = await server_read.receive() |
| 208 | + assert isinstance(ping, SessionMessage) |
| 209 | + assert isinstance(ping.message, JSONRPCRequest) |
| 210 | + assert ping.message.method == "ping" |
| 211 | + # First answer with a fabricated id that matches nothing in flight, then the real id. |
| 212 | + await server_write.send(respond(9999, EmptyResult())) |
| 213 | + await server_write.send(respond(ping.message.id, EmptyResult())) |
| 214 | + |
| 215 | + incoming: list[IncomingMessage] = [] |
| 216 | + |
| 217 | + async def message_handler(message: IncomingMessage) -> None: |
| 218 | + incoming.append(message) |
| 219 | + |
| 220 | + async with ( |
| 221 | + create_client_server_memory_streams() as ((client_read, client_write), server_streams), |
| 222 | + anyio.create_task_group() as task_group, |
| 223 | + ClientSession(client_read, client_write, message_handler=message_handler) as session, |
| 224 | + ): |
| 225 | + task_group.start_soon(scripted_server, server_streams) |
| 226 | + with anyio.fail_after(5): |
| 227 | + await session.initialize() |
| 228 | + pong = await session.send_request(PingRequest(), EmptyResult) |
| 229 | + |
| 230 | + assert pong == snapshot(EmptyResult()) |
| 231 | + assert len(incoming) == 1 |
| 232 | + assert isinstance(incoming[0], RuntimeError) |
| 233 | + # The full message embeds the response object's repr; only the prefix is stable. |
| 234 | + assert str(incoming[0]).startswith("Received response with an unknown request ID:") |
0 commit comments