-
Notifications
You must be signed in to change notification settings - Fork 128
MCP tools/list emits unresolvable $ref in outputSchema (upload_design_md dangling, ScreenInstance recursive) — clients drop all tools #367
Description
Summary
Four tools returned by tools/list at https://stitch.googleapis.com/mcp carry an outputSchema with a $ref that MCP clients cannot dereference. One is a dangling reference; three are self-recursive. Clients that eagerly resolve $ref when registering tools reject the entire tool list, so the server connects successfully and then exposes zero tools.
Filing here as the public repo for Stitch's API surface — the defect is in the schemas the hosted MCP server emits, not in the SDK code itself.
Affected tools
Captured from a live tools/list (15 tools total):
1. upload_design_md — dangling reference
Its outputSchema is the ScreenInstance object inlined at the root, but it retains a self-reference with no $defs block present to resolve it:
outputSchema.$defs → absent
outputSchema./properties/variantScreenInstance/$ref → "#/$defs/ScreenInstance"
2. create_project, get_project, list_projects — recursive reference
Here ScreenInstance is defined under $defs, but it refers to itself:
outputSchema./$defs/ScreenInstance/properties/variantScreenInstance/$ref → "#/$defs/ScreenInstance"
list_projects additionally reaches it via /$defs/Project/properties/screenInstances/items.
The remaining 11 tools have schemas that resolve cleanly.
Impact
In Claude Code the server reports:
stitch: https://stitch.googleapis.com/mcp (HTTP) - ! Connected · tools fetch failed —
can't resolve reference #/$defs/ScreenInstance from id #
Authentication and tool calls work fine — the failure is purely schema registration — but because a single unloadable schema aborts the whole fetch, all 15 tools become unavailable and the integration is unusable as documented.
Corresponding client-side reports: anthropics/claude-code#76040 and anthropics/claude-code#81111 (both open, reproducing on current releases).
Reproduction
curl -s -X POST https://stitch.googleapis.com/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "X-Goog-Api-Key: $STITCH_API_KEY" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \ | python3 -c ' import json,sys def refs(n,a=[]): if isinstance(n,dict): if isinstance(n.get("$ref"),str): a.append(n["$ref"]) for v in n.values(): refs(v,a) elif isinstance(n,list): for v in n: refs(v,a) return a def resolve(root,ref): cur=root for p in ref[2:].split("/"): if isinstance(cur,dict) and p in cur: cur=cur[p] else: return None return cur for t in json.load(sys.stdin)["result"]["tools"]: s=t.get("outputSchema") if not s: continue bad=[r for r in refs(s,[]) if resolve(s,r) is None] rec=[r for r in refs(s,[]) if r.endswith("ScreenInstance") and resolve(s,r) is not None and r in refs(resolve(s,r),[])] if bad or rec: print(t["name"], "dangling:", set(bad), "recursive:", set(rec)) '
Suggested fix
- For
upload_design_md, emit the$defsblock alongside the inlined root schema (or drop thevariantScreenInstanceproperty from that response schema if it is not actually returned). - For the recursive
ScreenInstance, the cleanest option is to break the cycle in the generated schema — omitvariantScreenInstance, or bound it to one level of nesting — since a self-referential$defsentry is legal JSON Schema but not universally consumable by MCP clients.
Clients arguably should tolerate a recursive $ref, and that is being tracked separately upstream. The dangling reference in upload_design_md is unambiguously a server-side bug either way.
Workaround
For anyone blocked today: put a stdio shim in front of the endpoint that forwards everything untouched and strips any outputSchema containing an unresolvable or cyclic $ref. outputSchema is optional and advisory in MCP, so all 15 tools stay callable and results are unaffected.