Fix MCP adapter stack overflow on self-referencing input types#9850
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes an uncatchable server startup StackOverflowException when generating MCP tool input JSON Schemas for operations whose variable types contain recursive/self-referencing input objects. It does this by emitting named input object types into JSON Schema $defs and referencing them via $ref (default), with an opt-out to inline schemas for MCP clients that cannot handle $ref (collapsing recursive branches to a generic object).
Changes:
- Add cycle-safe JSON Schema generation for MCP tool input schemas using
$defs/$refby default, with deterministic$defsordering. - Introduce
McpToolOptions.UseJsonSchemaReferencesplusModifyMcpToolOptions(...)extension methods for HotChocolate and Fusion builders. - Add/adjust tests and snapshots to cover recursive input variables in both reference-enabled and reference-disabled modes, plus snapshot updates for the new default.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| website/src/docs/hotchocolate/v16/build/adapters/mcp.md | Documents the new default $defs/$ref behavior and how to disable it via ModifyMcpToolOptions. |
| website/src/docs/fusion/v16/adapters/mcp.md | Same documentation update for Fusion gateway usage. |
| src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/OperationToolFactoryTests.cs | Adds coverage for recursive input variables and reference disabling; updates factory construction to pass options. |
| src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/snapshots/OperationToolFactoryTests.CreateTool_WithNullableVariables_CreatesCorrectSchema_Input.json | Snapshot update reflecting $defs/$ref default behavior. |
| src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/snapshots/OperationToolFactoryTests.CreateTool_WithNonNullableVariables_CreatesCorrectSchema_Input.json | Snapshot update reflecting $defs/$ref default behavior. |
| src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/snapshots/OperationToolFactoryTests.CreateTool_WithDefaultedVariables_CreatesCorrectSchema_Input.json | Snapshot update reflecting $defs/$ref default behavior. |
| src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/snapshots/OperationToolFactoryTests.CreateTool_WithComplexVariables_ReferencesDisabled_InlinesWithoutReferences.json | New snapshot ensuring full inlining and no $ref/$defs when references are disabled. |
| src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/snapshots/OperationToolFactoryTests.CreateTool_WithComplexVariables_CreatesCorrectSchema_Input.json | Snapshot update reflecting $defs/$ref default behavior (and ref reuse for shared input types). |
| src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/snapshots/OperationToolFactoryTests.CreateTool_SelfReferencingInputVariable_UsesReferences.json | New snapshot verifying finite schema for self-referencing input via $defs/$ref. |
| src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/snapshots/OperationToolFactoryTests.CreateTool_SelfReferencingInputVariable_ReferencesDisabled_Inlines.json | New snapshot verifying recursion collapse to { "type": "object" } when references are disabled. |
| src/HotChocolate/Adapters/src/Fusion.Adapters.Mcp/Extensions/FusionGatewayBuilderExtensions.cs | Adds ModifyMcpToolOptions(...) for Fusion gateway builder. |
| src/HotChocolate/Adapters/src/Adapters.Mcp/Extensions/McpRequestExecutorBuilderExtensions.cs | Adds ModifyMcpToolOptions(...) for HotChocolate request executor builder. |
| src/HotChocolate/Adapters/src/Adapters.Mcp.Core/OperationToolFactory.cs | Threads schema-generation context/options into input schema generation and emits $defs deterministically. |
| src/HotChocolate/Adapters/src/Adapters.Mcp.Core/Extensions/TypeExtensions.cs | Implements $defs/$ref emission for named input objects plus cycle detection for reference-disabled mode. |
| src/HotChocolate/Adapters/src/Adapters.Mcp.Core/Extensions/ServiceCollectionExtensions.cs | Builds and registers per-schema McpToolOptions from configured delegates; wires into OperationToolFactory. |
| src/HotChocolate/Adapters/src/Adapters.Mcp.Core/Extensions/JsonSchemaContext.cs | Adds shared schema-walk context (defs, cycle detection state, and reference toggle). |
| src/HotChocolate/Adapters/src/Adapters.Mcp.Core/Extensions/InputValueDefinitionExtensions.cs | Passes schema-generation context through when creating field schemas. |
| src/HotChocolate/Adapters/src/Adapters.Mcp.Abstractions/Configuration/McpToolOptions.cs | Introduces the public options surface controlling $ref usage for input schema generation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This was referenced Jun 16, 2026
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.
Summary
and/or: [SelfInput!]) crashed the server at startup with an uncatchableStackOverflowException:TypeExtensions.ToJsonSchemaBuilderwalked the input-type graph with no cycle protection.$defsand referenced with$ref(nullable references useanyOf: [{$ref}, {type:null}]), which represents recursive input types in a finite schema. This is the default.McpToolOptions.UseJsonSchemaReferences, configured via a fluentModifyMcpToolOptions(...)on both the HotChocolate and Fusion builders, lets you turn references off for MCP clients with weak$refsupport. With references off, input objects are inlined and a self-reference is collapsed to a generic object.Test plan
OperationToolFactoryTestscovering a self-referencing input variable in both modes (references on →$defs/$ref; off → inlined with the recursive branch collapsed), plus a non-recursive complex-fixture test with references off that verifies full inlining and no$ref/$defs.$defs/$refconversion.dotnet testonAdapters.Mcp.Tests: 237 passed, 0 failed.