Repository navigation
2025-11-25 Tool.inputSchema/outputSchema reject boolean sub-schemas in properties, failing the whole tools/list #3353
Description
Initial Checks
- I confirm that I'm using the newest release of my line (the latest 2.x, or the latest 1.x if I'm still on v1)
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Release line
2.x (current stable)
Description
JSON Schema 2020-12 allows a boolean (true / false) anywhere a sub-schema is expected, including as a value under properties. The generated 2025-11-25 wire model types properties as dict[str, dict[str, Any]] on both InputSchema and OutputSchema (src/mcp-types/mcp_types/_v2025_11_25/__init__.py, lines 1361 and 1379), so a tool whose outputSchema contains e.g. "properties": {"result": true} fails surface validation with a pydantic dict_type error.
Because ListToolsResult is validated as a whole, one such tool causes the client to discard the entire tool list for that server on any 2025-11-25-negotiated session (same failure shape as #3337, different trigger).
This is not hypothetical: a publicly reachable server (Microsoft's Azure Resource Manager MCP, https://mcp.management.azure.com) currently returns tools/list where 12 of 17 tools carry
"outputSchema": {"type": "object", "properties": {"result": true}, "required": ["result"]}and the SDK cannot list any of its tools on a 2025-11-25 session.
The 2026-07-28 surface already accepts this payload: its InputSchema/OutputSchema have no typed properties field (SEP-2106, #2792, made both free-form JSON Schema 2020-12). The 2025-11-25 strictness is an artefact of how schema.ts rendered { [key: string]: object } to JSON, not something the 2025-11-25 spec text requires — that spec also says outputSchema is a JSON Schema object and defaults to the 2020-12 dialect.
Expected: validate_server_result("tools/list", "2025-11-25", ...) accepts boolean property sub-schemas (and the client's list_tools() succeeds), matching 2026-07-28 behaviour and JSON Schema 2020-12.
Proposed fix (happy to open the PR): extend the existing SCHEMA_PATCHES["2025-11-25"] list in scripts/gen_surface_types.py to widen $defs/Tool/properties/{inputSchema,outputSchema}/properties/properties/additionalProperties to anyOf: [<object>, {"type": "boolean"}], then regenerate. The generated diff is exactly two lines (dict[str, dict[str, Any]] → dict[str, dict[str, Any] | bool]) and the 2026-07-28 module is unchanged. Plus a regression test in tests/types/test_methods.py.
Example Code
from mcp_types.methods import validate_server_result
tool = {
"name": "get_resource",
"inputSchema": {"type": "object"},
"outputSchema": {"type": "object", "properties": {"result": True}, "required": ["result"]},
}
validate_server_result(
"tools/list", "2026-07-28", {"tools": [tool], "resultType": "complete", "ttlMs": 0, "cacheScope": "private"}
)
print("2026-07-28: ok")
validate_server_result("tools/list", "2025-11-25", {"tools": [tool]}) # raises
print("2025-11-25: ok")Output (identical on the released mcp==2.0.0 wheel and on main @ 57394b0):
2026-07-28: ok
Traceback (most recent call last):
...
pydantic_core._pydantic_core.ValidationError: 1 validation error for ListToolsResult
tools.0.outputSchema.properties.result
Input should be a valid dictionary [type=dict_type, input_value=True, input_type=bool]
For further information visit https://errors.pydantic.dev/2.12/v/dict_type
Python & MCP Python SDK
Python 3.13.15
mcp 2.0.0 / mcp-types 2.0.0 (latest release, PyPI), pydantic 2.13.4
also reproduces on main @ 57394b05 (mcp 2.0.1.dev21+57394b05), pydantic 2.12.5
Activity
z2Ace0107 commentedon Aug 21, 2026
Thanks for the detailed report. I reviewed the current main branch and agree that the failure appears to come from the generated 2025-11-25 InputSchema/OutputSchema types:
propertiescurrently acceptsdict[str, dict[str, Any]], while JSON Schema allows boolean subschemas.The proposed fix seems appropriately scoped: update the existing
SCHEMA_PATCHES["2025-11-25"]code-generation patch, regenerate the surface types, and add a regression test fortools/listwith boolean property schemas. I would also verify that the 2026-07-28 behavior remains unchanged.I am interested in preparing this as a small PR. If this direction is acceptable, please assign the issue to me. I will follow the repository contribution guidelines and disclose the use of AI-assisted coding with manual review.
Ben-Home commentedon Aug 21, 2026
The root cause is a type-model mismatch: the generated 2025-11-25 wire model types
propertiesasdict[str, dict[str, Any]], but JSON Schema 2020-12 permits a boolean anywhere a sub-schema is expected, including as a property value.{"result": true}fails the pydanticdict_typevalidation, and becauseListToolsResultis validated as a whole, one boolean sub-schema discards the entire tool list. The Azure ARM MCP case makes the blast radius concrete: 12 of 17 tools carry"outputSchema": {"properties": {"result": true}}, so the whole server is unlistable on 2025-11-25 sessions.Two complementary fixes:
Model fix (correct long term): type
propertiesvalues asbool | dict[str, Any]in the 2025-11-25InputSchema/OutputSchemamodels, with a field validator that normalizestrueto an empty schema{}(semantically equivalent:truepermits anything,{}restricts nothing) and preservesfalsesemantics if you want to represent it. This mirrors how the 2026-07-28 surface already accepts the payload.Defensive client fix (protects against the whole fleet): when
ListToolsResultvalidation fails ondict_type, fall back to normalizing boolean sub-schemas and retry once, instead of surfacing a hard error. One tool with schema drift should never blind the client to every other tool on that server. At minimum, the error should name the offending tool so the server owner can identify it - a blanket failure gives no diagnosis path.A regression test should use the exact Azure payload shape (
{"type": "object", "properties": {"result": true}, "required": ["result"]}) and assert the client can list and call the tool on a 2025-11-25 session.This is the same normalization strategy we use in the CorpusIQ connector layer: schemas are normalized at registration time so one server's schema drift degrades that tool, not the whole registry.