fix(mcp): rewrite draft-only const keywords in published tool schemas - #265
fix(mcp): rewrite draft-only const keywords in published tool schemas#265Sarfaraz85 wants to merge 2 commits into
Conversation
Published tool input schemas cross MCP clients that re-encode them for
model providers with restricted schema dialects. Providers that reject
the JSON Schema const keyword and type enum members as strings fail
tool registration with 'Invalid value ... (TYPE_STRING), 1' whenever a
published schema carries {"const": 1} — ten such fragments ship today
via the verification server's contract schemas (schema_version and the
must_surface surface rule). Fixes psi-oss#239.
Rather than spot-fixing each literal, sanitize once at the publish
chokepoint (set_published_tool_input_schema): exact string values
become single-member enums, exact numeric values become equal
minimum/maximum bounds, and boolean consts inside conditional subtrees
become single-member enums that dialect-restricted clients strip along
with the enclosing conditional. Internal schemas stay draft-expressive;
server-side argument validation remains the enforcement layer.
A repo-wide regression test walks every FastMCP server's published
tool schemas and fails on const or non-string enum members outside
conditional subtrees, so the whole class stays fixed.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughPublished MCP tool schemas now recursively convert unsupported ChangesSchema portability
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Gentle bump on this one — CLA is signed and CodeRabbit's review came back clean. I think it's just waiting on a maintainer to approve the workflow run. Happy to rebase or adjust scope if that makes review easier — the guard test can also be split out into a separate PR if a smaller diff is preferred. |
Brings docstring coverage on this PR's new functions to 100%, addressing the CodeRabbit pre-merge warning (25% against an 80% threshold). Documents the recursive rewriter in servers/__init__.py and the helpers and regression guards in the new portable-schema suite.
|
Correction to my note above — I said the review came back clean, but I'd only looked at the check summary and missed the docstring-coverage warning inside it. Sorry about that. Fixed in 8103602: docstrings on the recursive rewriter in One judgment call worth flagging: the existing test suite doesn't use function docstrings ( |
Fixes #239.
Root cause
Published tool input schemas cross MCP clients that re-encode them for model providers with restricted schema dialects. The failing provider's schema proto types
enumas a repeated string field and rejects the JSON Schemaconstkeyword outright — soschema_version: {"type": "integer", "const": 1}fails registration withInvalid value ... (TYPE_STRING), 1, and the commonly suggested{"type": "integer", "enum": [1]}fails identically (non-string enum member). Details in this comment on #239. Tenconstfragments ship in published schemas today, all via the verification server's contract schemas.Fix
Sanitize once at the publish chokepoint —
set_published_tool_input_schema— instead of spot-fixing each literal. Newportable_published_schema()deep-copies the schema and rewrites draft-only constructs into forms valid in both dialects while keeping exactness machine-checkable:constenumconstminimum/maximumboundsconst(occurs only insideif/thenconditionals)enumnull)"Must be exactly …"description noteInternal schemas stay draft-expressive; server-side argument validation remains the enforcement layer, unchanged.
Regression guard
tests/mcp/test_portable_published_schemas.pywalks every FastMCP server's actually-published tool schemas (mcp.list_tools()) and fails on anyconstor non-string enum member outside conditional subtrees — keeping the whole class fixed, not just these instances. At currentmainit red-fails with 10 violations, including the exactproperties.contract.properties.schema_versionpath from the issue's error trace. (arxiv_bridgebuilds a lowlevelmcp.Serverin its entry point and publishes no draft-only keywords; it's exercised by its own suite.)Plus unit tests for each rewrite rule, and
test_tool_contract_visibility.pyupdated for themust_surfacefragment's published form.Validation
ruff format/ repo meta-tests (test inventory, runtime-name policy) greenSummary by CodeRabbit