Skip to content

fix(ci): avoid top-level oneOf in get_ci_catalog_resource input_schema (Fixes #550)#551

Open
zch0227 wants to merge 1 commit into
zereight:mainfrom
zch0227:fix/ci-catalog-resource-oneof
Open

fix(ci): avoid top-level oneOf in get_ci_catalog_resource input_schema (Fixes #550)#551
zch0227 wants to merge 1 commit into
zereight:mainfrom
zch0227:fix/ci-catalog-resource-oneof

Conversation

@zch0227

@zch0227 zch0227 commented Jun 22, 2026

Copy link
Copy Markdown

Problem

get_ci_catalog_resource's input_schema has oneOf at the top level, which the Anthropic Messages API (Claude Code, Kiro, and other MCP clients) rejects:

tools.<N>.custom.input_schema: input_schema does not support oneOf, allOf, or anyOf at the top level

This is reported in #550. Because every enabled tool is serialized on every request, this single tool breaks the entire MCP server for affected clients once it appears in the tool list — the reported tool index (tools.78, tools.62, …) just moves around as users toggle other tools. Current workaround is pinning to 2.1.25 (before #537 added the CI catalog tools).

Root cause

GetCiCatalogResourceSchema is a z.union([...]):

export const GetCiCatalogResourceSchema = z.union([
  GetCiCatalogResourceOptionsSchema.extend({ id: z.string().min(1), full_path: z.string().min(1).optional() }),
  GetCiCatalogResourceOptionsSchema.extend({ id: z.string().min(1).optional(), full_path: z.string().min(1) }),
]).refine(...);

zod-to-json-schema serializes a top-level union into { "oneOf": [...] } at the root of input_schema — exactly what Anthropic forbids.

Fix

Flatten the union into a single z.object(...).refine(...). id and full_path are both optional at the schema level; the "exactly one of" constraint is still enforced at runtime via .refine():

export const GetCiCatalogResourceSchema = GetCiCatalogResourceOptionsSchema.extend({
  id: z.string().min(1).optional()...,
  full_path: z.string().min(1).optional()...,
}).refine(args => Boolean(args.id) !== Boolean(args.full_path), {
  message: "Provide exactly one of id or full_path",
});

Generated input_schema now has type: "object" at the root with no oneOf, while runtime validation is unchanged.

Verification

  • tsc compiles clean.
  • Serialized JSON Schema top-level keys: ['type', 'properties', 'additionalProperties'], type: "object", oneOf/anyOf/allOf all undefined.
  • Runtime .refine() behavior unchanged:
    • { id } → valid ✅
    • { full_path } → valid ✅
    • {} (neither) → rejected ✅
    • { id, full_path } (both) → rejected ✅

Fixes #550

GetCiCatalogResourceSchema was a z.union([...]), which zod-to-json-schema
serializes into JSON Schema with `oneOf` at the root of the tool's
input_schema. The Anthropic Messages API (used by Claude Code, Kiro, and
other MCP clients) rejects any tool whose input_schema has oneOf/allOf/anyOf
at the top level:

  input_schema does not support oneOf, allOf, or anyOf at the top level

Because every enabled tool is sent on every request, this single tool breaks
the whole MCP server for affected clients once it appears in the tool list
(the reported index moves around as users toggle other tools).

Flatten the union into a single z.object(...).refine(...): id and full_path
are both optional at the schema level, and the "exactly one of" constraint
is enforced at runtime via .refine(). The generated input_schema now has
`type: "object"` at the root with no oneOf, while keeping identical runtime
validation.

Fixes zereight#550
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e6239d26-9aa5-48d9-be87-15d0afb14403

📥 Commits

Reviewing files that changed from the base of the PR and between 79f45f8 and 45edc85.

📒 Files selected for processing (1)
  • schemas.ts
📜 Recent review details
🔇 Additional comments (1)
schemas.ts (1)

384-394: LGTM!


📝 Walkthrough

Summary by CodeRabbit

  • Refactor
    • Improved schema validation compatibility with external API integrations while preserving existing validation behavior and error messages.

Walkthrough

GetCiCatalogResourceSchema in schemas.ts is refactored from a top-level z.union(...) of two object branches (one requiring id, one requiring full_path) to a single z.object(...) where both fields are optional. The XOR constraint is enforced via a .refine(...) call with the same error message as before.

Changes

GetCiCatalogResourceSchema: remove top-level oneOf

Layer / File(s) Summary
Schema definition and runtime XOR enforcement
schemas.ts
GetCiCatalogResourceSchema is changed from a top-level z.union([ ...id-required..., ...full_path-required... ]) to a single z.object(...) extending GetCiCatalogResourceOptionsSchema with both id and full_path marked optional. The .refine(...) call is preserved to enforce that exactly one of the two fields is provided at runtime. Comments document that top-level oneOf JSON Schema output is rejected by certain Anthropic Messages API consumers.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: avoiding top-level oneOf in the get_ci_catalog_resource input_schema, with reference to the linked issue #550.
Description check ✅ Passed The description is comprehensive and clearly related to the changeset, explaining the problem, root cause, fix, and verification steps for the schema restructuring.
Linked Issues check ✅ Passed The PR directly addresses issue #550 by refactoring GetCiCatalogResourceSchema from z.union to z.object with .refine(), eliminating the top-level oneOf that breaks Anthropic Messages API clients.
Out of Scope Changes check ✅ Passed The changeset focuses solely on refactoring GetCiCatalogResourceSchema in schemas.ts to fix the top-level oneOf issue, with no extraneous modifications beyond the scope of addressing issue #550.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Comment @coderabbitai help to get the list of available commands and usage tips.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kiro schema issue v2.1.26

1 participant