fix(mcp): circular-ref-safe schema deref, quiet child stderr, structuredContent fallback - #33
Open
itsbrex wants to merge 1 commit into
Conversation
…redContent fallback
Three independent robustness fixes to McpCommunicationProtocol, all battle-
tested for several weeks as a patch-package shim over @utcp/mcp 1.1.1-1.1.3
in a code-mode deployment federating 15+ MCP servers / 400+ tools:
1. $RefParser.dereference(..., { dereference: { circular: 'ignore' } })
Recursive JSON Schemas (e.g. self-referencing SOQL/SOSL filter grammars
from Salesforce MCP servers) throw on dereference and take down the whole
manual's tool discovery. 'ignore' keeps the cycle as a live reference and
discovery succeeds.
2. StdioClientTransport stderr: default 'ignore', opt-in 'inherit' via
UTCP_MCP_CHILD_STDERR=inherit. Child MCP servers inherit the host's stderr
today and flood the terminal during discovery. Deliberately NOT 'pipe':
with no reader attached the OS pipe buffer fills and a chatty child
deadlocks.
3. _processMcpToolResult: when a result carries an empty content array but a
non-null structuredContent (MCP spec field), return structuredContent
instead of collapsing to [] and silently losing the payload.
Note: the package's dts build step currently fails on an @types/node
resolution issue on a clean checkout of main as well (bun install layout) —
unrelated to this change; the esbuild JS bundle builds clean.
There was a problem hiding this comment.
Pull request overview
This PR improves the robustness of the MCP integration by making schema dereferencing resilient to recursive JSON Schemas, reducing terminal noise from stdio MCP child processes, and preserving tool-call payloads that are returned via MCP’s structuredContent field.
Changes:
- Make JSON Schema dereferencing tolerant of circular
$refgraphs by setting$RefParser.dereference(..., { dereference: { circular: 'ignore' } }). - Default stdio MCP child
stderrto'ignore', withUTCP_MCP_CHILD_STDERR=inheritto opt back into inherited stderr for debugging. - Add a fallback in
_processMcpToolResultto returnstructuredContentwhencontentis empty.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
Three independent robustness fixes to
McpCommunicationProtocol, all battle-tested for several weeks as a patch-package shim over@utcp/mcp1.1.1→1.1.3 in a code-mode deployment federating 15+ MCP servers / 400+ tools.1. Circular-reference-safe schema dereferencing
$RefParser.dereference(schema)throws on recursive JSON Schemas (e.g. self-referencing SOQL/SOSL filter grammars returned by Salesforce MCP servers). Because the error propagates before the per-schema try/catch can help every time the same schema is re-encountered, the affected manual's tool discovery degrades. Passing{ dereference: { circular: 'ignore' } }keeps the cycle as a live reference and discovery succeeds; acyclic schemas are byte-identical.2. Child stderr: default
'ignore', opt-in'inherit'StdioClientTransportis constructed without astderroption, so every stdio MCP child inherits the host's stderr and floods the terminal during discovery (banners, telemetry notices, auth chatter — multiplied by N servers).'ignore'.UTCP_MCP_CHILD_STDERR=inheritrestores the old behavior for debugging.'pipe': with no reader attached the OS pipe buffer fills and a chatty child deadlocks.3.
structuredContentfallback in_processMcpToolResultSome MCP servers return results with an empty
contentarray and the payload only instructuredContent(an MCP spec field). Today that collapses to[]and the payload is silently lost. Whencontentis empty andstructuredContent != null, returnstructuredContent. Results that carrycontentare untouched, and the existingstructured_outputbranch still wins.Notes
packages/mcp/src/mcp_communication_protocol.ts.tsupdts step currently fails on a clean checkout ofmaintoo (an@types/noderesolution issue under bun's node_modules layout) — pre-existing and unrelated.Summary by cubic
Improve MCP stability by handling circular JSON Schemas, quieting child stderr by default, and preserving results returned via structuredContent. Reduces discovery failures and terminal noise when federating many MCP servers.
$RefParser.dereference(..., { dereference: { circular: 'ignore' } })to safely handle recursive schemas without breaking discovery.'ignore'; setUTCP_MCP_CHILD_STDERR=inheritto debug. Not'pipe'to avoid deadlocks.contentis empty andstructuredContentexists, returnstructuredContentso payloads aren’t lost.Written for commit 7df273b. Summary will update on new commits.