Backport: Dereference $ref in tool schemas for MCP client compatibility#2861
Backport: Dereference $ref in tool schemas for MCP client compatibility#2861jlowin merged 2 commits intorelease/2.xfrom
Conversation
WalkthroughThe pull request adds JSON Schema dereferencing capabilities to FastMCP. Changes include documentation updates explaining automatic Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/fastmcp/utilities/json_schema.py (1)
38-61: Consider moving the success return into anelseblock for clarity.The static analysis tool flagged that the
return dereferencedon line 56 could be moved into anelseblock to make the control flow more explicit. This is a minor style improvement.♻️ Optional refactor to address TRY300
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) # Merge sibling keywords that were lost during dereferencing # Pydantic puts description, default, examples as siblings to $ref defs = schema.get("$defs", {}) merged = _merge_ref_siblings(schema, dereferenced, defs) # Type assertion: top-level schema is always a dict assert isinstance(merged, dict) dereferenced = merged # Remove $defs since all references have been resolved if "$defs" in dereferenced: dereferenced = {k: v for k, v in dereferenced.items() if k != "$defs"} - return dereferenced - except JsonRefError: # Self-referencing/circular schemas can't be fully dereferenced # Fall back to resolving only root-level $ref (for MCP spec compliance) return resolve_root_ref(schema) + else: + return dereferenced
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
.loq_cacheis excluded by none and included by nonepyproject.tomlis excluded by none and included by nonetests/utilities/test_json_schema.pyis excluded by none and included by noneuv.lockis excluded by!**/*.lockand included by none
📒 Files selected for processing (2)
docs/servers/tools.mdxsrc/fastmcp/utilities/json_schema.py
🧰 Additional context used
📓 Path-based instructions (2)
docs/**/*.mdx
📄 CodeRabbit inference engine (docs/.cursor/rules/mintlify.mdc)
docs/**/*.mdx: Use clear, direct language appropriate for technical audiences
Write in second person ('you') for instructions and procedures in MDX documentation
Use active voice over passive voice in MDX technical documentation
Employ present tense for current states and future tense for outcomes in MDX documentation
Maintain consistent terminology throughout all MDX documentation
Keep sentences concise while providing necessary context in MDX documentation
Use parallel structure in lists, headings, and procedures in MDX documentation
Lead with the most important information using inverted pyramid structure in MDX documentation
Use progressive disclosure in MDX documentation: present basic concepts before advanced ones
Break complex procedures into numbered steps in MDX documentation
Include prerequisites and context before instructions in MDX documentation
Provide expected outcomes for each major step in MDX documentation
End sections with next steps or related information in MDX documentation
Use descriptive, keyword-rich headings for navigation and SEO in MDX documentation
Focus on user goals and outcomes rather than system features in MDX documentation
Anticipate common questions and address them proactively in MDX documentation
Include troubleshooting for likely failure points in MDX documentation
Provide multiple pathways (beginner vs advanced) but offer an opinionated path to avoid overwhelming users in MDX documentation
Always include complete, runnable code examples that users can copy and execute in MDX documentation
Show proper error handling and edge case management in MDX code examples
Use realistic data instead of placeholder values in MDX code examples
Include expected outputs and results for verification in MDX code examples
Test all code examples thoroughly before publishing in MDX documentation
Specify language and include filename when relevant in MDX code examples
Add explanatory comments for complex logic in MDX code examples
Document all API...
Files:
docs/servers/tools.mdx
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Python ≥3.10 with full type annotations required for all code
Never use bareexcept- be specific with exception types in Python code
Files:
src/fastmcp/utilities/json_schema.py
🧠 Learnings (3)
📚 Learning: 2026-01-12T16:24:54.978Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: .cursor/rules/core-mcp-objects.mdc:0-0
Timestamp: 2026-01-12T16:24:54.978Z
Learning: Applies to src/tools/**/*.{ts,tsx,js,jsx} : Changes affecting MCP Tools (like adding tags, importing, etc.) must be adopted, applied, and tested consistently
Applied to files:
docs/servers/tools.mdx
📚 Learning: 2026-01-13T03:11:40.907Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T03:11:40.907Z
Learning: Applies to **/*.py : Python ≥3.10 with full type annotations required for all code
Applied to files:
docs/servers/tools.mdx
📚 Learning: 2026-01-13T03:11:40.907Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T03:11:40.907Z
Learning: Applies to **/__init__.py : Be intentional about module re-exports - only re-export fundamental types to fastmcp.*; prefer users importing from specific submodules
Applied to files:
docs/servers/tools.mdx
🪛 Ruff (0.14.10)
src/fastmcp/utilities/json_schema.py
56-56: Consider moving this statement to an else block
(TRY300)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run tests: Python 3.10 on ubuntu-latest
- GitHub Check: Run tests: Python 3.10 on windows-latest
- GitHub Check: Run tests with lowest-direct dependencies
- GitHub Check: Run tests: Python 3.13 on ubuntu-latest
🔇 Additional comments (4)
src/fastmcp/utilities/json_schema.py (3)
64-128: LGTM!The recursive merge function correctly handles:
- Sibling keyword restoration for
$refnodes (description, default, examples)- Circular reference prevention via the
visitedset- Parallel traversal of dict and list structures
- Local siblings taking precedence over definition content (line 107)
The implementation is well-documented and handles edge cases properly.
170-170: LGTM!The type annotation updates to use
dict[str, Any]are consistent with Python 3.10+ style and align with the project's type annotation requirements. Based on learnings, Python ≥3.10 with full type annotations is required for all code.Also applies to: 191-196, 364-370
6-7: Imports fromjsonrefare appropriate and properly declared.The
jsonref>=1.1.0dependency is listed inpyproject.toml. Both imports (JsonRefErrorandreplace_refs) are used correctly in the new dereferencing functions, with proper full type annotations and specific exception handling throughout.docs/servers/tools.mdx (1)
176-178: LGTM!The documentation note clearly explains the automatic
$refdereferencing behavior in a concise manner. The note:
- Uses clear, direct language appropriate for technical audiences
- Correctly identifies the affected MCP clients (VS Code Copilot, Claude Desktop)
- Is placed appropriately before the Type Annotations section where users learn about complex types
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e956422ace
ℹ️ 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".
| <Note> | ||
| FastMCP automatically dereferences `$ref` entries in tool schemas to ensure compatibility with MCP clients that don't fully support JSON Schema references (e.g., VS Code Copilot, Claude Desktop). This means complex Pydantic models with shared types are inlined in the schema rather than using `$defs` references. | ||
| </Note> |
There was a problem hiding this comment.
Wire dereference_refs into tool schema generation
The docs state that FastMCP automatically dereferences $ref in tool schemas, but the new dereference_refs helper is never called from tool schema generation (schemas are still built via compress_schema in src/fastmcp/tools/tool.py and src/fastmcp/tools/tool_transform.py). As a result, $ref/$defs remain in tool schemas and clients that strip $defs will still break; integrate dereference_refs into the schema pipeline or remove this note.
Useful? React with 👍 / 👎.
Test Failure AnalysisSummary: Windows test timeout in Root Cause: The test failure occurs during fixture initialization in The stack trace shows: Why This Is Unrelated to PR Changes: This PR only modifies:
None of these changes affect OAuth proxy initialization or disk storage operations. Suggested Solution:
# At the top of tests/server/auth/test_enhanced_error_responses.py
import sys
import pytest
pytestmark = pytest.mark.timeout(15 if sys.platform == 'win32' else 5)
# In the oauth_proxy fixture
from key_value.aio.stores import MemoryStore
return OAuthProxy(
# ... other params ...
key_value_store=MemoryStore(), # Instead of letting it default to DiskStore
)Detailed AnalysisPlatform-Specific Behavior: The test passes on ubuntu-latest (3207 passed) but times out on windows-latest. This is a known issue with Windows having slower file I/O, especially for SQLite operations through diskcache. Test File: The failing fixture creates an OAuthProxy with default disk storage, which initializes a SQLite database. On Windows, this can take longer than 5 seconds. Files Changed in This PR:
None of these touch OAuth, authentication, or disk storage code. Related Files
|
Backports #2814 to release/2.x for inclusion in a 2.x release.
MCP clients like VS Code Copilot and Claude Desktop strip
$defsbefore sending schemas to the LLM, leaving dangling$refreferences that break enum parameters and complex types. This adds automatic dereferencing of all$refentries at tool creation time.For circular schemas that can't be fully dereferenced, falls back to resolving only root-level
$ref.