|
2 | 2 |
|
3 | 3 | These call the registered handler via the public `Server.get_request_handler` |
4 | 4 | accessor without spinning up a `ServerRunner` or any transport, so they verify |
5 | | -the handler's contract in isolation from the dispatch pipeline. The exception |
6 | | -is the server-identity pair: the serverInfo `_meta` stamp is applied by the |
7 | | -runner (spec 2026-07-28, #3002), not the handler, so those two drive one |
8 | | -request through `serve_one` to observe it. |
| 5 | +the handler's contract in isolation from the dispatch pipeline. The exceptions |
| 6 | +are the server-identity pair and the handshake comparison. The serverInfo |
| 7 | +`_meta` stamp is applied by the runner (spec 2026-07-28, #3002), not the |
| 8 | +handler, so that pair drives one request through `serve_one` to observe it. |
| 9 | +The handshake comparison needs a real `initialize` result to set beside the |
| 10 | +`server/discover` one, so it connects a `Client` in each mode. |
9 | 11 | """ |
10 | 12 |
|
11 | 13 | from collections.abc import Mapping |
|
15 | 17 | import anyio |
16 | 18 | import mcp_types as types |
17 | 19 | import pytest |
18 | | -from mcp_types.version import MODERN_PROTOCOL_VERSIONS |
| 20 | +from mcp_types.version import HANDSHAKE_PROTOCOL_VERSIONS, MODERN_PROTOCOL_VERSIONS |
19 | 21 |
|
| 22 | +from mcp import Client |
20 | 23 | from mcp.server import NotificationOptions, Server, ServerRequestContext |
21 | 24 | from mcp.server.connection import Connection |
22 | 25 | from mcp.server.runner import serve_one |
@@ -271,3 +274,22 @@ async def list_tools( |
271 | 274 |
|
272 | 275 | opted_in = server.get_capabilities(NotificationOptions(tools_changed=True)) |
273 | 276 | assert opted_in.tools is not None and opted_in.tools.list_changed is True |
| 277 | + |
| 278 | + |
| 279 | +@pytest.mark.anyio |
| 280 | +async def test_unconfigured_experimental_is_omitted_from_both_initialize_and_discover() -> None: |
| 281 | + """SDK-defined: a server with no experimental capabilities configured leaves |
| 282 | + `experimental` out of the `initialize` result as well as the `server/discover` |
| 283 | + result, so a client reads the same thing whichever way it connects.""" |
| 284 | + server = Server("bare") |
| 285 | + |
| 286 | + async with Client(server, mode="legacy") as legacy: |
| 287 | + assert legacy.protocol_version in HANDSHAKE_PROTOCOL_VERSIONS |
| 288 | + initialize_capabilities = legacy.server_capabilities |
| 289 | + |
| 290 | + async with Client(server, mode="auto") as modern: |
| 291 | + assert modern.protocol_version in MODERN_PROTOCOL_VERSIONS |
| 292 | + discover_capabilities = modern.server_capabilities |
| 293 | + |
| 294 | + assert initialize_capabilities.experimental is None |
| 295 | + assert discover_capabilities.experimental is None |
0 commit comments