This commit is contained in:
parent
3a6f2996cd
commit
837ef904f8
2 changed files with 1757 additions and 567 deletions
|
|
@ -302,12 +302,14 @@ PACKAGE_BY_VERSION = {
|
|||
"2026-07-28": "mcp_types.v2026_07_28",
|
||||
}
|
||||
|
||||
# The three reserved `params._meta` entries the 2026 surface requires on every request.
|
||||
# The reserved `params._meta` entries the 2026 surface accepts on every request.
|
||||
# `clientInfo` is optional (SHOULD-include, spec PR #3002); the other two are required.
|
||||
META_TRIPLE: dict[str, Any] = {
|
||||
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
|
||||
"io.modelcontextprotocol/clientInfo": {"name": "client", "version": "1.0"},
|
||||
"io.modelcontextprotocol/clientCapabilities": {},
|
||||
}
|
||||
META_REQUIRED_KEYS = ("io.modelcontextprotocol/protocolVersion", "io.modelcontextprotocol/clientCapabilities")
|
||||
|
||||
# One minimal valid params mapping per surface request class.
|
||||
REQUEST_PARAMS_FIXTURES: dict[type[BaseModel], dict[str, Any] | None] = {
|
||||
|
|
@ -397,7 +399,6 @@ RESULT_BODY_FIXTURES: dict[type[BaseModel] | UnionType, dict[str, Any]] = {
|
|||
v2026.DiscoverResult: {
|
||||
"supportedVersions": ["2026-07-28"],
|
||||
"capabilities": {},
|
||||
"serverInfo": {"name": "server", "version": "1.0"},
|
||||
"resultType": "complete",
|
||||
"ttlMs": 0,
|
||||
"cacheScope": "private",
|
||||
|
|
@ -651,8 +652,8 @@ def test_unknown_version_strings_raise_value_error_on_every_parse_function():
|
|||
assert "2099-01-01" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_2026_07_28_requests_missing_a_reserved_meta_entry_reject_as_missing():
|
||||
for absent_key in META_TRIPLE:
|
||||
def test_2026_07_28_requests_missing_a_required_meta_entry_reject_as_missing():
|
||||
for absent_key in META_REQUIRED_KEYS:
|
||||
partial_meta = {key: value for key, value in META_TRIPLE.items() if key != absent_key}
|
||||
with pytest.raises(pydantic.ValidationError) as excinfo:
|
||||
methods.parse_client_request("tools/list", "2026-07-28", {"_meta": partial_meta})
|
||||
|
|
@ -661,6 +662,14 @@ def test_2026_07_28_requests_missing_a_reserved_meta_entry_reject_as_missing():
|
|||
]
|
||||
|
||||
|
||||
def test_2026_07_28_requests_accept_meta_without_the_optional_client_info():
|
||||
"""spec PR #3002: `clientInfo` is optional on the 2026 surface - the required
|
||||
pair alone validates."""
|
||||
pair_meta = {key: value for key, value in META_TRIPLE.items() if key != "io.modelcontextprotocol/clientInfo"}
|
||||
parsed = methods.parse_client_request("tools/list", "2026-07-28", {"_meta": pair_meta})
|
||||
assert isinstance(parsed, types.ListToolsRequest)
|
||||
|
||||
|
||||
def test_2026_07_28_results_require_result_type():
|
||||
with pytest.raises(pydantic.ValidationError):
|
||||
methods.parse_server_result("tools/call", "2026-07-28", {"content": []})
|
||||
|
|
@ -861,7 +870,6 @@ MONOLITH_RESULT_FIXTURES: dict[str, types.Result] = {
|
|||
"server/discover": types.DiscoverResult(
|
||||
supported_versions=["2026-07-28"],
|
||||
capabilities=types.ServerCapabilities(),
|
||||
server_info=types.Implementation(name="server", version="1.0"),
|
||||
ttl_ms=0,
|
||||
cache_scope="private",
|
||||
),
|
||||
|
|
@ -927,6 +935,24 @@ def test_serialize_server_result_preserves_open_type_extras():
|
|||
assert sieved["tools"][0]["_meta"] == nested_meta
|
||||
|
||||
|
||||
def test_serialize_server_result_drops_top_level_server_info_on_discover_but_keeps_the_meta_stamp():
|
||||
"""Server identity moved from the discover body to result `_meta` (spec PR #3002):
|
||||
the sieve drops the removed body key and preserves the `_meta` stamp."""
|
||||
stamp = {"name": "server", "version": "1.0"}
|
||||
dumped: dict[str, Any] = {
|
||||
"supportedVersions": ["2026-07-28"],
|
||||
"capabilities": {},
|
||||
"serverInfo": stamp,
|
||||
"_meta": {types.SERVER_INFO_META_KEY: stamp},
|
||||
"resultType": "complete",
|
||||
"ttlMs": 0,
|
||||
"cacheScope": "private",
|
||||
}
|
||||
sieved = methods.serialize_server_result("server/discover", "2026-07-28", dumped)
|
||||
assert "serverInfo" not in sieved
|
||||
assert sieved["_meta"] == {types.SERVER_INFO_META_KEY: stamp}
|
||||
|
||||
|
||||
def test_serialize_server_result_drops_an_unknown_nested_tool_field():
|
||||
tool = {"name": "echo", "inputSchema": {"type": "object"}, "unknownField": 1}
|
||||
sieved = methods.serialize_server_result("tools/list", "2025-11-25", {"tools": [tool], "resultType": "complete"})
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ SKIP: frozenset[str] = frozenset(
|
|||
"v2026_07_28.RequestMetaObject",
|
||||
"v2026_07_28.RequestedSchema",
|
||||
"v2026_07_28.ResourceRequestParams",
|
||||
"v2026_07_28.ResultMetaObject",
|
||||
"v2026_07_28.StringSchema",
|
||||
"v2026_07_28.SubscriptionsListenResultMeta",
|
||||
"v2026_07_28.TitledMultiSelectEnumSchema",
|
||||
|
|
|
|||
Loading…
Reference in a new issue