You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Client treats JSON-null structuredContent as missing, skipping outputSchema validation #3345
On current main (57394b0548d1e2dc2dce8d67d84985769df3b8bb), ClientSession.validate_tool_result treats structured_content is None as "the tool did not return structured content".
That collapses two different wire shapes:
omitted structuredContent (field absent)
explicit JSON null ("structuredContent": null)
SEP-2106 / spec 2026-07-28 allow structuredContent to be any JSON value, including null. The TypeScript SDK already checks === undefined (not falsy / not null) for this reason.
Pydantic stores both omitted and JSON null as None. model_fields_set distinguishes them: a CallToolResult parsed from {"content": [], "structuredContent": null} has "structured_content" in model_fields_set, while an omitted field does not.
Client raises Tool {name} has an output schema but did not return structured content and never runs jsonschema against the value.
What I expected
Omitted structuredContent still raises the existing missing-field error.
Explicit JSON null is validated against the advertised schema: accept if the schema allows null, reject as a schema mismatch if it does not.
Falsy JSON values (0, false, "") stay validated (they already are, because the current check is is None rather than falsy).
This is not #3224 (server injecting nulls for NotRequired keys). That issue is about serializing omitted object keys as null. This one is the client presence check before outputSchema validation.
I hit this while checking official SDK conformance of declared outputSchema against structuredContent. I have a small backwards-compatible test and fix ready and would like to send the PR if a maintainer wants it.
AI assistance: researched and drafted with Grok 4.6; I reviewed the spec text, the TypeScript v2 presence check, and the Pydantic model_fields_set behavior before filing.
Against a tool whose outputSchema is {"type": "null"}, validate_tool_result currently raises the missing-field RuntimeError for explicit_null. After a presence check that uses model_fields_set, that result validates.
Python & MCP Python SDK
Python 3.12
MCP Python SDK main at 57394b0548d1e2dc2dce8d67d84985769df3b8bb (2.x)
I've confirmed the diagnosis on main (6705402): validate_tool_result keys the
missing-structured-content check on result.structured_content is None, which
cannot distinguish an omitted structuredContent from an explicit JSON null —
pydantic stores both as None, and only model_fields_set separates them.
My plan is the narrow fix you describe:
key the check on "structured_content" in result.model_fields_set, matching
the === undefined line the TypeScript SDK draws;
omitted stays as the existing error, message unchanged;
explicit null goes through the compiled validator, so it's accepted when the
advertised schema allows null and reported as a schema mismatch when it doesn't.
One note for whoever reviews it: servers built on this SDK can't produce the
explicit-null shape at all, because outbound results are dumped with exclude_none=True in mcp/shared/peer.py. So the tests have to parse CallToolResult from a wire payload rather than construct it — constructing the
model can't express the distinction. That also means the change is inert for
in-SDK servers and only affects sessions with peers that do send null.
The two explicit-null tests fail without the fix and pass with it; the third pins
the omitted-field behaviour so it can't drift. Locally: ruff check, ruff format --check and pyright (strict) clean on both touched files, the
changed lines fully covered with no new no cover pragmas, and the full suite
shows no new failures.
Happy to open the PR as soon as you assign me — I understand the intake gate
closes it otherwise.
Disclosure: I used an AI coding agent to write the patch and the tests. I ran
the reproduction and the suite myself and can explain the change.
I reproduced this against current main (6705402e246dc4eb1fcdf4d9902b78d3c9c36e1b). An explicitly supplied structuredContent: null has structured_content in model_fields_set, while an omitted field does not; both currently raise Tool nullable has an output schema but did not return structured content before JSON Schema validation runs.
I would keep the fix narrow in ClientSession.validate_tool_result: use field presence for the missing-content check, then let the existing validator accept or reject the explicit JSON null according to outputSchema. I would add focused regression coverage for an explicit null accepted by {"type": "null"}, an explicit null rejected by an object schema, and an omitted field retaining the existing missing-content error. This should not require a public API, dependency, or protocol change.
I used AI assistance while inspecting the code and preparing this reproduction, and I am accountable for verifying the behavior and any resulting patch. If this direction matches the intended contract, I would appreciate assignment before opening a PR.
Activity
lucianoon commentedon Aug 27, 2026
I'd like to take this one if it's still open.
I've confirmed the diagnosis on
main(6705402):validate_tool_resultkeys themissing-structured-content check on
result.structured_content is None, whichcannot distinguish an omitted
structuredContentfrom an explicit JSONnull—pydantic stores both as
None, and onlymodel_fields_setseparates them.My plan is the narrow fix you describe:
"structured_content" in result.model_fields_set, matchingthe
=== undefinedline the TypeScript SDK draws;nullgoes through the compiled validator, so it's accepted when theadvertised schema allows null and reported as a schema mismatch when it doesn't.
One note for whoever reviews it: servers built on this SDK can't produce the
explicit-null shape at all, because outbound results are dumped with
exclude_none=Trueinmcp/shared/peer.py. So the tests have to parseCallToolResultfrom a wire payload rather than construct it — constructing themodel can't express the distinction. That also means the change is inert for
in-SDK servers and only affects sessions with peers that do send
null.The change and three tests are ready and pushed, if it's useful to look before
deciding:
https://github.com/lucianoon/mcp-python-sdk/tree/fix/structured-content-explicit-null
The two explicit-null tests fail without the fix and pass with it; the third pins
the omitted-field behaviour so it can't drift. Locally:
ruff check,ruff format --checkandpyright(strict) clean on both touched files, thechanged lines fully covered with no new
no coverpragmas, and the full suiteshows no new failures.
Happy to open the PR as soon as you assign me — I understand the intake gate
closes it otherwise.
Disclosure: I used an AI coding agent to write the patch and the tests. I ran
the reproduction and the suite myself and can explain the change.
Alqudimi commentedon Aug 27, 2026
I reproduced this against current
main(6705402e246dc4eb1fcdf4d9902b78d3c9c36e1b). An explicitly suppliedstructuredContent: nullhasstructured_contentinmodel_fields_set, while an omitted field does not; both currently raiseTool nullable has an output schema but did not return structured contentbefore JSON Schema validation runs.I would keep the fix narrow in
ClientSession.validate_tool_result: use field presence for the missing-content check, then let the existing validator accept or reject the explicit JSONnullaccording tooutputSchema. I would add focused regression coverage for an explicit null accepted by {"type": "null"}, an explicit null rejected by an object schema, and an omitted field retaining the existing missing-content error. This should not require a public API, dependency, or protocol change.I used AI assistance while inspecting the code and preparing this reproduction, and I am accountable for verifying the behavior and any resulting patch. If this direction matches the intended contract, I would appreciate assignment before opening a PR.
maxisbey commentedon Oct 2, 2026
#3621 fixes this with the same
model_fields_setcheck you proposed in #3346, so thank you for both the report and the approach.An explicit null
structuredContentis now validated against the tool'soutputSchema, and only an absent field counts as missing.If your case still isn't covered, feel free to open a new issue.
AI Disclaimer