Skip to content

MCP: outputSchema anyOf/oneOf rejected — valid JSON Schema combinators cause entire server to fail #57643

Open

Description

@bmironov

Description

Summary

OpenCode v2.1.0 rejects valid MCP servers whose tools/list returns outputSchema using anyOf or oneOf instead of a simple type: "object". The validation error rejects the entire server before any tools become available, even though the schemas are valid JSON Schema per the MCP specification.
This is a regression — many legitimate MCP servers (e.g., cqs (https://github.com/jamie8johnson/cqs)) intentionally use anyOf/oneOf because tools return different output structures depending on input.

Environment

  • opencode version: 2.1.0
  • OS: Linux 6.12.77-1-generic (linux x64)
  • Terminal: GhosTTY, tmux, xterm-256color
  • Shell: /bin/bash
  • Install/channel: latest
  • Active plugins: None found

Expected Behavior

OpenCode should accept anyOf/oneOf in outputSchema as valid JSON Schema. These are standard JSON Schema combinators supported by the MCP specification. The server should connect and tools should be available.

Actual Behavior

OpenCode's zod validation strictly requires type: "object" at the top level of each outputSchema. When a tool uses anyOf or oneOf without a top-level type field, OpenCode rejects the entire server and marks it as failed. All tools from that server are unavailable.

Additional Context

Example of valid schema rejected by OpenCode
This is a real outputSchema from the cqs cqs_callers tool:

{
  "$defs": {
    "CallersOutput": { "type": "object", "properties": { ... } },
    "KindFallbackOutput": { "type": "object", "properties": { ... } }
  },
  "anyOf": [
    { "$ref": "#/$defs/CallersOutput", "description": "Function path" },
    { "$ref": "#/$defs/KindFallbackOutput", "description": "Kind mismatch" }
  ]
}

This is valid JSON Schema and valid MCP protocol. The tool genuinely returns either a CallersOutput or a KindFallbackOutput depending on input — there is no single type: "object" that captures both shapes.

Affected tools in cqs (10 of 31)

Tool Schema type Reason
cqs_task anyOf Full result vs budgeted output
cqs_callers anyOf Callers result vs kind fallback
cqs_callees anyOf Callees result vs kind fallback
cqs_deps anyOf Multiple possible output shapes
cqs_impact oneOf Function vs fallback impact
cqs_test_map anyOf Test map vs kind fallback
cqs_trace anyOf Trace result vs kind fallback
cqs_context anyOf 3 possible shapes (compact/summary/full)
cqs_plan anyOf Plan result vs budgeted output
cqs_read anyOf Full read vs focused read

The remaining 21 tools have type: "object" and work fine.

cqs maintainers are aware

The cqs project explicitly documents this design decision in issue #2203 (jamie8johnson/cqs#2203) (July 2026): tools use anyOf/oneOf because their outputs are genuinely heterogeneous — there is no single type: "object" that accurately describes the possible return shapes.

Plugins

No response

OpenCode version

2.1.0

Steps to reproduce

  1. Configure a remote MCP server that returns anyOf in tools.list output schemas (e.g., cqs (https://github.com/jamie8johnson/cqs) at localhost:8117)
  2. Start a new OpenCode session
  3. Server fails with error:
MCP server failed: <server_name>
Invalid result for tools/list:
  path: ["tools", 3, "outputSchema", "type"]
  message: "Invalid input: expected \"object\""
  1. The same server works fine with curl — tools execute correctly, only the metadata validation fails.

Screenshot

No response

Operating System

Debian 12 / Linux 6.12.77-1-generic (linux x64)

Terminal

GhosTTY, tmux

Activity

  1. opencode-agent commented on Oct 21, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report. I reproduced this on v2.1.0, and it still happens on the latest dev (15adc3d91).

    What I did:

    • I set up a minimal local MCP server with two tools. One has an ordinary outputSchema (type: "object"). The other uses $defs plus a top-level anyOf with no type, like cqs_callers.
    • opencode mcp list reports failed: Invalid result for tools/list ... path ["tools",1,"outputSchema","type"] ... expected "object", and neither tool is available.
    • Adding a top-level "type": "object" to the second schema makes the server connect.

    The check comes from the MCP SDK's validation of the tools/list result, and OpenCode has no fallback for it, so one tool's output schema takes down the whole server. The spec does declare outputSchema with type: "object". Even so, failing every tool on the server is clearly worse than skipping or loosening just that tool's schema, and OpenCode v1 used to tolerate this kind of error.

    The script below reproduces it. Related: #56116 (a tool call hard-fails when the result doesn't match its output schema).

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Repro for anomalyco/opencode#57643: an MCP tool whose outputSchema is $defs + anyOf (no top-level "type")
    # makes the whole server fail tools/list validation, so none of its tools load.
    # Requires: opencode v2 on PATH (bun add -g @opencode/cli@2.1.0), node.
    set -euo pipefail
    DIR=$(mktemp -d); cd "$DIR"; git init -q
    cat > server.mjs <<'EOF'
    let buf = ""
    const send = (m) => process.stdout.write(JSON.stringify(m) + "\n")
    process.stdin.on("data", (c) => {
      buf += c
      let i
      while ((i = buf.indexOf("\n")) >= 0) {
        const line = buf.slice(0, i).trim(); buf = buf.slice(i + 1)
        if (!line) continue
        const m = JSON.parse(line)
        if (m.id === undefined) continue
        if (m.method === "initialize")
          send({ jsonrpc: "2.0", id: m.id, result: { protocolVersion: m.params?.protocolVersion ?? "2025-06-18", capabilities: { tools: {} }, serverInfo: { name: "anyof-repro", version: "1.0.0" } } })
        else if (m.method === "tools/list")
          send({ jsonrpc: "2.0", id: m.id, result: { tools: [
            { name: "plain", inputSchema: { type: "object", properties: {} },
              outputSchema: { type: "object", properties: { ok: { type: "boolean" } } } },
            { name: "callers", inputSchema: { type: "object", properties: {} },
              outputSchema: {
                $defs: {
                  CallersOutput: { type: "object", properties: { callers: { type: "array", items: { type: "string" } } } },
                  KindFallbackOutput: { type: "object", properties: { kind: { type: "string" } } },
                },
                anyOf: [ { $ref: "#/$defs/CallersOutput" }, { $ref: "#/$defs/KindFallbackOutput" } ],
              } },
          ] } })
        else send({ jsonrpc: "2.0", id: m.id, error: { code: -32601, message: "Method not found" } })
      }
    })
    EOF
    echo "{ \"mcp\": { \"servers\": { \"anyof\": { \"type\": \"local\", \"command\": [\"node\", \"$DIR/server.mjs\"] } } } }" > opencode.json
    opencode --version
    # The first call can race service startup and print "No MCP servers configured"; ask twice.
    opencode mcp list || true; sleep 3; opencode mcp list
    # Expected: "anyof connected" with 2 tools (or at least the "plain" tool).
    # Actual:   "✗ anyof failed: Invalid result for tools/list ... path [tools,1,outputSchema,type] expected \"object\""

    Run it: bash repro.sh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions