Skip to content

[Bug] ZCode rejects valid MCP tool input when oneOf branches use local $ref #920

Description

@ShadyAV

提交前确认 · Pre-submission checklist

  • I searched existing issues and discussions and found no exact duplicate.
  • I read CONTRIBUTING.md.

问题类别 · Category

工具调用 / MCP · Tool use / MCP

涉及的 Agent 框架 · Agent framework

ZCode Agent(自研)

严重程度 · Severity

Blocking for affected MCP tools: valid calls are rejected before reaching the MCP server.

复现频率 · Reproducibility

Always in the deterministic cases below.

问题描述 · Description

ZCode's tool-input validator counts unresolved local $ref branches as successful matches. When a property uses oneOf with two such branches, an input that should match exactly one branch is rejected as matching both. Replacing the references with their definitions makes the same input pass.

This is client-side argument validation, not a model-generation failure or a remote API error. All references in the example are local JSON Pointers; no external schema retrieval is needed.

Related but different: #387 reports an API failure when a property is literally named $ref. This report concerns actual schema references inside oneOf in the ZCode validator.

复现步骤 · Steps to reproduce

  1. Expose a harmless MCP tool named ref_demo, whose handler simply echoes its arguments, with the following inputSchema:
{
  "type": "object",
  "properties": {
    "job": {
      "oneOf": [
        { "$ref": "#/$defs/ByText" },
        { "$ref": "#/$defs/ById" }
      ]
    }
  },
  "required": ["job"],
  "additionalProperties": false,
  "$defs": {
    "ByText": {
      "type": "object",
      "properties": {
        "kind": { "const": "text" },
        "text": { "type": "string" }
      },
      "required": ["kind", "text"],
      "additionalProperties": false
    },
    "ById": {
      "type": "object",
      "properties": {
        "kind": { "const": "id" },
        "id": { "type": "integer" }
      },
      "required": ["kind", "id"],
      "additionalProperties": false
    }
  }
}
  1. Invoke it with either of these inputs:
{ "job": { "kind": "text", "text": "example" } }
{ "job": { "kind": "id", "id": 1 } }
  1. For a model-independent reproduction in the source tree, pass each input and the schema above to the existing helper:
validateJsonSchemaValue(input, schema);
  1. As a control, replace the two $ref objects in properties.job.oneOf with the contents of $defs.ByText and $defs.ById, leaving the input and all constraints unchanged. Both inputs then validate successfully.

期望表现 · Expected behavior

Each input matches exactly one branch and reaches the echo handler. Local references should be resolved before evaluating oneOf.

If a schema feature is deliberately unsupported, an explicit unsupported-schema error would be preferable to interpreting its reference branches as successful matches and blaming valid input.

实际表现 · Actual behavior

For both inputs, the minimized source-helper reproduction returns:

{
  "valid": false,
  "errors": [
    "$.job must match exactly one oneOf schema, matched 2"
  ],
  "issues": [
    {
      "code": "invalid_union",
      "errors": [[], []],
      "path": ["job"],
      "message": "Invalid input"
    }
  ]
}

The inlined control returns:

{ "valid": true, "errors": [], "issues": [] }

ZCode 版本 · ZCode version

  • Windows Desktop: 3.14.4.7912.
  • Bundled engine: 0.16.9.
  • Public source inspected: 29628c9acdb81b703bbd4080c207a0e7ce5e276e.

设备 / 系统 / 浏览器 · Device / OS / Browser

Windows x64. Installed-runtime integration verification used a deterministic local model endpoint and a real local echo MCP subprocess, so it did not depend on a cloud model, account, or paid provider.

截图 / 录屏 / 日志 · Verification and source evidence

  • In a controlled run of the unmodified installed engine using --prompt --surface desktop, the referenced-schema fixture was rejected with InputValidationError / invalid_union before its MCP handler ran. Its equivalent inlined-schema control reached the MCP handler successfully. This is running-engine integration evidence, not a fresh Desktop GUI replay of the minimized example.
  • The exact two-branch example above was separately executed against the public validateJsonSchemaValue implementation. Both inputs failed with references and passed with equivalent inline definitions. The displayed JSON is the minimal reproduction output, not a user log.
  • In packages/core/src/tool/json-schema.ts, validateNode evaluates each oneOf candidate and counts candidates without errors. It does not resolve $ref; reference-only candidate objects therefore contribute no validation errors.

This report concerns the concrete false rejection. It does not assume a particular negotiated MCP protocol version or make a broader protocol-conformance claim.

中文摘要

ZCode 的工具参数校验器没有解析本地 $ref。当 oneOf 的分支只有引用时,各分支都被当作校验成功,导致本来只匹配一个分支的合法输入被拒绝。上面的两个分支完全使用虚构数据,不需要外部 schema、账户或网络服务。

已分别验证两种输入:使用引用时均返回 matched 2 和两个空错误列表;展开相同定义后均校验成功。另有未修改的已安装引擎与本地模型/MCP 子进程的集成验证,确认同类问题会在到达 MCP handler 前阻断调用。最小示例的验证属于源码函数验证,不声称已重新完成 Desktop GUI 验证。

期望先解析本地引用,再按 oneOf 语义校验;如果有意不支持该 schema 特性,也应明确报告不支持,而不是误报合法参数匹配多个分支。此问题与 #387 的远程 API 崩溃不同。

Activity

  1. github-actions commented on Oct 4, 2026

    @github-actions

    👋 感谢你的反馈,我们已经收到。

    • 维护者看到后会尽快回复你。
    • 状态保持为 status: 待评估,你可以随时补充信息。
    • 信息不全时我们会打上 needs: 更多信息 标签并 @ 你。

    👋 Thanks — we've received your issue.

    • A maintainer will get back to you as soon as we can.
    • Status stays at status: 待评估 (Triage); feel free to add context.
    • If we need more details, we'll add needs: 更多信息 and ping you.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions