Dereference $ref in tool schemas for MCP client compatibility#2808
Dereference $ref in tool schemas for MCP client compatibility#2808
Conversation
Some MCP clients (e.g., VS Code Copilot) don't properly handle $ref in tool input schemas, causing enum parameters to fail. This change dereferences all $ref entries by inlining definitions using jsonref. Self-referencing schemas fall back to resolve_root_ref() since they can't be fully dereferenced. Closes #2807, closes #1193
WalkthroughThis pull request refactors schema dereferencing by introducing a new Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 81e2bd8365
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| try: | ||
| # Use jsonref to resolve all $ref references | ||
| # proxies=False returns plain dicts (not proxy objects) | ||
| # lazy_load=False resolves immediately | ||
| dereferenced = replace_refs(schema, proxies=False, lazy_load=False) | ||
|
|
There was a problem hiding this comment.
Restrict dereferencing to local $defs
dereference_refs() calls replace_refs(schema, proxies=False, lazy_load=False) without filtering for local #/$defs references. jsonref resolves external URIs eagerly when lazy_load=False, so any schema that includes an external $ref (valid in JSON Schema and possible in user‑supplied tool/input schemas) will now trigger network fetches or raise resolution errors during schema compression. Previously those refs were left intact, so this is a regression that can break tool creation or hang in offline environments. Consider limiting dereferencing to local refs or providing a loader that rejects non‑local refs and leaves them untouched.
Useful? React with 👍 / 👎.
Some MCP clients (like VS Code Copilot) don't properly handle
$refin tool input schemas. When Pydantic generates schemas for Enum types, it uses$defswith$refpointers:{ "$defs": {"Category": {"enum": ["food", "transport"], "type": "string"}}, "properties": {"category": {"$ref": "#/$defs/Category"}} }VS Code Copilot appears to strip
$defsbefore sending to Claude, leaving dangling$refpointers that cause tool calls to fail.This PR dereferences all
$refentries incompress_schema()usingjsonref, producing clean inlined schemas:{ "properties": {"category": {"enum": ["food", "transport"], "type": "string"}} }Self-referencing schemas (which can't be fully dereferenced) fall back to
resolve_root_ref()for MCP spec compliance.Background
We previously closed PRs #1192 and #1641 which proposed similar solutions. Our position was that FastMCP's obligation is MCP spec compliance, not working around individual client bugs. However, this issue has persisted for a long time across multiple popular clients (Claude Desktop, VS Code Copilot) that are otherwise excellent. The tradeoff is minimal—slightly larger schemas due to inlining—with no functional compromise. Given how widespread and long-standing this is, it feels reasonable to make this accommodation.
Closes #2807, closes #1193, fixes #2236