fix(tools): schema-aware MCP normalization preserves property names#14977
Closed
wzahedi wants to merge 1 commit into
Closed
fix(tools): schema-aware MCP normalization preserves property names#14977wzahedi wants to merge 1 commit into
wzahedi wants to merge 1 commit into
Conversation
`_normalize_mcp_input_schema` recursed blindly into every dict, treating
the `properties` map itself as a schema node. If a tool parameter
happened to be named `properties`, `required`, or `definitions`, the
heuristics misfired — injecting a stray `"type": "object"` sibling into
the property map or renaming a property to `$defs`. Anthropic rejects
the former against JSON Schema draft 2020-12:
tools.N.custom.input_schema: JSON schema is invalid.
('object' is not of type 'object', 'boolean')
Replace the two structure-blind passes with one schema-aware walk that
recurses only into positions holding JSON Schema values (items, values
of properties, members of oneOf, …) and treats everything else as a
leaf. Regression tests cover parameters named `properties`, `required`,
and `definitions`, plus preservation of `additionalProperties: false`.
Repro: any Notion-shaped MCP server (Notion's official
`@notionhq/notion-mcp-server`, user-authored servers with a `properties`
arg mirroring Notion's API) now works with Anthropic models.
Related: NousResearch#14927 (adjacent bug in the same normalization path; distinct
root cause — that issue is a malformed server schema, this is a
hermes-side heuristic misfire on a valid server schema).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
18a8015 to
f172b09
Compare
13 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed and why
_normalize_mcp_input_schemaintools/mcp_tool.pyrecursed blindly into every dict, treating thepropertiesmap itself as a schema node. When a tool parameter happens to share a name with a JSON Schema keyword —properties,required, ordefinitions— the heuristics misfire. The most visible symptom: Anthropic 400s on tool-use requests withbecause Hermes had injected a stray
"type": "object"sibling into thepropertiesmap. The existing checkwas meant to say "this node looks like an object schema" but triggers equally on a property-map whose keys happen to include
propertiesorrequired. Similarly thedefinitions → $defsrewrite applied to a user-named property rather than only the JSON Schema keyword.The fix replaces the two structure-blind passes (
_rewrite_local_refs+_repair_object_shape) with a single schema-aware walk that recurses only into positions holding JSON Schema values:items,additionalProperties,contains,not,if/then/else,propertyNames,unevaluatedItems/unevaluatedProperties,contentSchema,additionalItems(draft-07 compat).allOf,anyOf,oneOf,prefixItems.properties,patternProperties,dependentSchemas,$defs,definitions.Everything else (including
type,required,enum,const,description, and unknown keywords) is treated as a leaf and copied verbatim. Boolean schemas (true,additionalProperties: false) pass through unchanged. No public API change; return shape is identical and walk order is still post-order.Repro
Trivial with any Notion-shaped MCP server. The official
@notionhq/notion-mcp-serverdefines tools likeAPI_post_page/API_patch_page/API_create_a_data_source/API_update_a_data_sourcewhose schemas include a top-levelpropertiesfield mirroring the Notion REST API. User-authored Notion MCP servers (anywhere withz.object({ ..., properties: z.string() })) hit the same trap. In my local setup, 7 tools across two Notion MCP servers all produced schemas Anthropic rejected before this fix.How to test it
New regression tests cover:
properties— property-map stays clean (no injectedtype).required— not pruned / not rewritten.definitions— not renamed to$defs.additionalProperties: falsepreserved.Platforms tested
tests/tools/test_mcp_tool.py: 177 of 178 pass. The one failure (TestShutdown::test_shutdown_is_parallel) is a pre-existing timing-flake unrelated to this change.jsonschema.Draft202012Validator.check_schemafor every tool, and a realclaude-opus-4-7request with the full tools list returns successfully.No platform-specific code paths are touched — the change is pure Python dict/list manipulation — so Linux impact is expected to be identical.
Related issues
additionalProperties: "object"string shorthand; this PR concerns a Hermes-side heuristic misfire on a valid server schema. The two fixes do not overlap textually and can land independently.🤖 Generated with Claude Code