fix(mcp): tolerate unresolvable tool output-schema $refs#1128
Conversation
Some MCP servers (e.g. Google Stitch) advertise tool output schemas containing $refs the SDK cannot resolve (e.g. #/$defs/ScreenInstance). The strict listTools() decode throws on these, so defs() returned undefined, the create flow closed the client, and the whole server was marked "failed" with no tools — even though every other tool was fine. On a schema-validation error, re-list tools via a raw tools/list request that relaxes output-schema validation (TolerantListToolsResultSchema), keeping only name/description/inputSchema. Non-schema errors (e.g. transport closed) still fail as before. Re-implemented for PawWork from upstream anomalyco/opencode 79d6b10d7c (PR #26614, thanks Kit Langton); adapted to PawWork's withTimeout-based listTools and zod/v4 schemas. Excludes nothing from upstream's index.ts change. Regression tests cover both the fallback and the non-schema path.
|
Warning Review limit reached
More reviews will be available in 1 minute and 23 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Code Review
This pull request introduces a fallback mechanism in the Model Context Protocol (MCP) client to handle cases where tool output schemas contain unresolved references (e.g., $refs). Instead of failing the entire server connection, the client catches output schema validation errors and retries the tools/list request with a relaxed schema that ignores the outputSchema. Unit tests have been added to verify this tolerant fallback behavior and ensure that other non-schema errors still correctly trigger failures. There are no review comments, so no additional feedback is provided.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Summary
When an MCP server advertises a tool whose
outputSchemacontains a$refthe SDK cannot resolve, the strictlistTools()decode throws.defs()then returnedundefined, the create flow closed the client, and the entire server was markedfailedwith zero tools — even when every other tool was valid.This adds a fallback: on a schema-validation error, re-list tools via a raw
tools/listrequest with output-schema validation relaxed (TolerantListToolsResultSchema), keeping onlyname/description/inputSchema. Genuine non-schema failures (e.g. transport closed) still fail exactly as before.Why
Google Stitch and similar MCP servers ship
outputSchemaentries like{ "screen": { "$ref": "#/$defs/ScreenInstance" } }. PawWork'sdefs()atmcp/index.tsis byte-identical to upstream's pre-fix version: a single strictlistTools()whose failure collapses the whole server tofailed. The result is a confusing "MCP server failed / no tools" with no usable tools for an otherwise-healthy server.Related Issue
No PawWork issue. Re-implemented from upstream
anomalyco/opencode79d6b10d7c(PR #26614, thanks @kitlangton).devandupstream/devhave no common ancestor, so this is a semantic re-implementation, not a cherry-pick — adapted to PawWork'swithTimeout-basedlistToolsandzod/v4schemas. Scope matches upstream'sindex.tschange exactly (the upstreamcallbackPort-style features are unrelated and not present here).Human Review Status
Pending
Review Focus
isOutputSchemaValidationError). Over-matching is low-risk: a wrongly-triggered fallback just attempts one extratools/listrequest that, if the underlying failure is genuine, also fails and falls through to the samefailedstatus — it can never mask a real failure, becauseTolerantToolSchemastill validatesinputSchemastrictly.{ name, description, inputSchema }withMCPToolDefconsumers (convertMcpToolreads only those three fields).Risk Notes
Low. New code path runs only when the first
listTools()throws a schema-shaped error. Local-only engine change, no platform/packaging/UI surface touched. None of the conditional checklist items apply (no UI/copy, no platform surface, no docs/deps/credentials).How To Verify
```text
bun test test/mcp/lifecycle.test.ts → 21 pass / 0 fail (incl. 2 new: fallback path + non-schema-error path)
bun test test/mcp/ → 36 pass / 0 fail
bun run typecheck (packages/opencode) → clean
```
Screenshots or Recordings
N/A — no visible UI change.
Checklist
bug,enhancement,task,documentation.app,ui,platform,harness,ci. The labeler bot assigns these on PR open based on changed paths. Confirm the bot's choice (or override if wrong), then tick this.P0,P1,P2,P3. The priority-triage bot suggests one on PR open. Confirm or override, then tick this.Pending,Approved by @<reviewer>, orNot required: <reason>(default isPending; "not required" is restricted to bot-authored low-risk PRs).dev, and my PR title and commit messages use Conventional Commits in English.