Skip to content

MCP tools/list emits unresolvable $ref in outputSchema (upload_design_md dangling, ScreenInstance recursive) — clients drop all tools #367

Description

@Roelsoft

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

  1. For upload_design_md, emit the $defs block alongside the inlined root schema (or drop the variantScreenInstance property from that response schema if it is not actually returned).
  2. For the recursive ScreenInstance, the cleanest option is to break the cycle in the generated schema — omit variantScreenInstance, or bound it to one level of nesting — since a self-referential $defs entry 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions