crewai - ✅(Solved) Fix [BUG] Incomplete follow-up fix for recursive MCP schemas after #5478 [2 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
crewAIInc/crewAI#5490Fetched 2026-04-17 08:30:36
View on GitHub
Comments
0
Participants
1
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×3referenced ×2closed ×1

I think the fix merged in #5478 closes the original recursion crash, but the latest public release candidate (1.14.2rc1) still leaves recursive MCP schemas in a broken state.

In particular, recursive $ref schemas appear to degrade into {} during strict sanitization, and create_model_from_schema() still fails with JsonRefError for the same recursive schema shape.

That means the current behavior is no longer "hard fail on recursion depth", but it is still not a complete end-to-end fix for recursive MCP input schemas.

Error Message

san = sanitize_tool_params_for_anthropic_strict(schema) print(san["properties"]["children"]["items"])

=> {}

create_model_from_schema(schema, model_name="NodeModel")

=> JsonRefError: Error while resolving #/$defs/Node: Unresolvable JSON pointer: '/$defs/Node'

Root Cause

This feels important because recent release notes position MCP schema handling as fixed in 1.14.2rc1, and recursive tool schemas are a realistic pattern for MCP servers exposing tree / graph-like inputs.

Fix Action

Fix / Workaround

I think the fix merged in #5478 closes the original recursion crash, but the latest public release candidate (1.14.2rc1) still leaves recursive MCP schemas in a broken state.

PR fix notes

PR #5497: fix: complete recursive MCP schema handling after #5478

Description (problem / solution / changelog)

Summary

Fixes #5490 — two remaining issues with recursive MCP schemas after the cyclic-schema fix in #5478:

1. resolve_refs() silently degraded recursive $ref to {}

The sanitization pipeline (sanitize_tool_params_for_anthropic_strict, sanitize_tool_params_for_openai_strict) called resolve_refs() which returned {} when it detected a circular reference. This caused recursive schemas like tree-shaped inputs to silently lose type information (e.g. children.items == {}).

Fix: Return a type-preserving stub {"type": "object"} instead of {} when a circular reference is detected. This preserves the type information at the recursion break point.

2. create_model_from_schema() threw JsonRefError on recursive schemas

jsonref.replace_refs() cannot fully inline circular $ref graphs and throws JsonRefError. This caused model creation to fail entirely for recursive MCP tool schemas.

Fix: Catch JsonRefError and fall back to _inline_top_level_ref(), which manually resolves only the top-level $ref while preserving $defs for inner resolution. The existing in_progress cycle detection in _build_model_from_schema then handles recursive references via ForwardRef.

Review & Testing Checklist for Human

  • Verify that the recursive Node schema from the issue ($ref$defs/Node with self-referential children.items) produces a working Pydantic model via create_model_from_schema
  • Verify sanitization pipelines no longer produce {} for recursive refs — they should produce {"type": "object"} at the recursion break point
  • Verify non-recursive schemas still work identically (no regression) — existing tests cover this
  • Test with a real MCP server that exposes recursive tool schemas (e.g. tree/graph-like inputs)

Notes

  • The _inline_top_level_ref helper deliberately shares sub-objects with the deepcopied $defs entries so that id()-based cycle detection in _build_model_from_schema works correctly
  • 10 new test cases added covering: resolve_refs circular handling, both sanitization pipelines, model creation, valid data acceptance, required field rejection, enriched descriptions, and mutual recursion

Link to Devin session: https://app.devin.ai/sessions/c87eca1ed8514885a1a00665f6eca204

<!-- CURSOR_SUMMARY -->

[!NOTE] Medium Risk Touches core schema sanitization and dynamic model creation paths; changes in $ref resolution behavior could affect downstream tool-schema consumers, though coverage is added for recursive and non-recursive cases.

Overview Fixes recursive/circular $ref handling in pydantic_schema_utils so MCP-style self-referential schemas no longer break or silently lose type information.

resolve_refs() now returns a type-preserving stub (instead of {}) when a cycle is detected, and create_model_from_schema() now catches jsonref.JsonRefError and falls back to _inline_top_level_ref() to inline only the top-level $ref while leaving nested refs for lazy resolution.

Adds a focused test suite covering recursive and mutually recursive schemas across resolve_refs(), both strict sanitizers, and create_model_from_schema() (including valid/invalid data cases).

<sup>Reviewed by Cursor Bugbot for commit a3a3f658fe7d4e8e6ac1ad962507462b8f8be6e3. Bugbot is set up for automated code reviews on this repo. Configure here.</sup>

<!-- /CURSOR_SUMMARY -->

Changed files

  • lib/crewai/src/crewai/utilities/pydantic_schema_utils.py (modified, +39/-2)
  • lib/crewai/tests/utilities/test_pydantic_schema_utils.py (modified, +144/-0)

PR #5500: fix: complete recursive MCP schema handling

Description (problem / solution / changelog)

Summary

  • resolve_refs now returns type-preserving stubs ({"type": "object"}) instead of {} when a circular $ref is detected, preventing schema degradation during Anthropic/OpenAI strict sanitization
  • create_model_from_schema catches JsonRefError/RecursionError from jsonref.replace_refs and falls back to _inline_top_level_ref(), which resolves only the top-level $ref and leaves inner refs for lazy resolution via the existing in_progress cycle detection
  • Adds 8 new tests covering resolve_refs, sanitization, and model creation with recursive and mutually-recursive schemas

Closes #5490

Test plan

  • Existing 73 tests pass unchanged
  • New TestResolveRefsRecursive verifies circular refs produce type-preserving stubs
  • New TestSanitizeRecursiveSchemas verifies Anthropic/OpenAI strict pipelines don't degrade recursive schemas
  • New TestCreateModelFromSchemaRecursive verifies model creation, data validation, required-field rejection, and mutual recursion
<!-- CURSOR_SUMMARY -->

[!NOTE] Medium Risk Touches schema dereferencing and dynamic model generation used by MCP/A2A flows; mistakes could change tool schema output or model validation for complex schemas, but changes are narrowly scoped and covered by new tests.

Overview Fixes recursive JSON Schema handling so circular $ref graphs no longer degrade into empty schemas during dereferencing/sanitization.

resolve_refs now returns a type-preserving stub (e.g. {"type":"object"} plus optional description) when a cycle is detected, and create_model_from_schema now falls back to a new _inline_top_level_ref strategy when jsonref.replace_refs errors/recurses, enabling model generation for recursive and mutually-recursive schemas.

Adds targeted tests covering circular ref resolution, OpenAI/Anthropic strict sanitization, and successful Pydantic model creation/validation for recursive schemas.

<sup>Reviewed by Cursor Bugbot for commit 878a98463eebcda37d8aa8803b5e132dc0648c71. Bugbot is set up for automated code reviews on this repo. Configure here.</sup>

<!-- /CURSOR_SUMMARY -->

Changed files

  • lib/crewai/src/crewai/utilities/pydantic_schema_utils.py (modified, +28/-2)
  • lib/crewai/tests/utilities/test_pydantic_schema_utils.py (modified, +107/-0)

Code Example

schema = {
    "$defs": {
        "Node": {
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "children": {
                    "type": "array",
                    "items": {"$ref": "#/$defs/Node"}
                }
            },
            "required": ["name"]
        }
    },
    "$ref": "#/$defs/Node"
}

---

san = sanitize_tool_params_for_anthropic_strict(schema)
print(san["properties"]["children"]["items"])
# => {}

create_model_from_schema(schema, model_name="NodeModel")
# => JsonRefError: Error while resolving `#/$defs/Node`: Unresolvable JSON pointer: '/$defs/Node'
RAW_BUFFERClick to expand / collapse

Summary

I think the fix merged in #5478 closes the original recursion crash, but the latest public release candidate (1.14.2rc1) still leaves recursive MCP schemas in a broken state.

In particular, recursive $ref schemas appear to degrade into {} during strict sanitization, and create_model_from_schema() still fails with JsonRefError for the same recursive schema shape.

That means the current behavior is no longer "hard fail on recursion depth", but it is still not a complete end-to-end fix for recursive MCP input schemas.

Why this seems worth tracking separately

  • #5474 is closed
  • #5478 is merged and shipped in 1.14.2rc1
  • but the latest code still reproduces a broken recursive-schema path

So this looks more like a follow-up / incomplete-fix bug than a duplicate of #5474.

Reproduction

Minimal recursive schema:

schema = {
    "$defs": {
        "Node": {
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "children": {
                    "type": "array",
                    "items": {"$ref": "#/$defs/Node"}
                }
            },
            "required": ["name"]
        }
    },
    "$ref": "#/$defs/Node"
}

Observed locally against 1.14.2rc1 source:

san = sanitize_tool_params_for_anthropic_strict(schema)
print(san["properties"]["children"]["items"])
# => {}

create_model_from_schema(schema, model_name="NodeModel")
# => JsonRefError: Error while resolving `#/$defs/Node`: Unresolvable JSON pointer: '/$defs/Node'

Expected behavior

One of these should happen consistently:

  1. recursive MCP schemas are converted into a usable typed model / back-reference form, or
  2. recursive MCP schemas fail clearly and explicitly without silently degrading parts of the schema to {}.

Actual behavior

The schema partially degrades during sanitization (children.items == {}), but model generation still fails later with JsonRefError.

Context

This feels important because recent release notes position MCP schema handling as fixed in 1.14.2rc1, and recursive tool schemas are a realistic pattern for MCP servers exposing tree / graph-like inputs.

Related

  • #5474
  • #5478

This report was drafted with AI assistance. I was not able to apply an llm-generated label from my account.

extent analysis

TL;DR

The issue can be addressed by revisiting the sanitization process for recursive MCP schemas to prevent degradation and ensure consistent error handling.

Guidance

  • Review the sanitize_tool_params_for_anthropic_strict function to understand why recursive $ref schemas are being degraded to {}.
  • Investigate the create_model_from_schema function to determine why it fails with JsonRefError despite the fix in #5478.
  • Consider implementing a check for recursive schemas during sanitization to either convert them into a usable form or raise a clear error.
  • Verify that the fix does not introduce any regressions for non-recursive schemas.

Example

# Example of a recursive schema
schema = {
    "$defs": {
        "Node": {
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "children": {
                    "type": "array",
                    "items": {"$ref": "#/$defs/Node"}
                }
            },
            "required": ["name"]
        }
    },
    "$ref": "#/$defs/Node"
}

# Potential fix: add a check for recursive schemas during sanitization
def sanitize_tool_params_for_anthropic_strict(schema):
    # ... existing code ...
    if is_recursive_schema(schema):
        # either convert to usable form or raise an error
        pass
    # ... existing code ...

def is_recursive_schema(schema):
    # implement a check for recursive schemas
    pass

Notes

The provided code snippet is a minimal example and may not be a complete solution. Further investigation is needed to determine the root cause of the issue.

Recommendation

Apply a workaround by modifying the sanitize_tool_params_for_anthropic_strict function to handle recursive schemas correctly, as the current behavior is incomplete and may cause issues with recursive MCP input schemas.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

One of these should happen consistently:

  1. recursive MCP schemas are converted into a usable typed model / back-reference form, or
  2. recursive MCP schemas fail clearly and explicitly without silently degrading parts of the schema to {}.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING