forked from zereight/gitlab-mcp
-
Notifications
You must be signed in to change notification settings - Fork 1
refactor(schemas): Replace discriminated unions with flat schemas for Claude API compatibility #29
Copy link
Copy link
Description
Problem
Claude/Anthropic API returns error when tools use oneOf, allOf, or anyOf at the top level of input schemas:
API Error: 400 {"type":"error","error":{"type":"invalid_request_error",
"message":"tools.19.custom.input_schema: input_schema does not support oneOf, allOf, or anyOf at the top level"}}
Zod's z.discriminatedUnion() compiles to JSON Schema with oneOf at the root level, making our CQRS tools incompatible with Claude API.
Affected Schemas (18 total)
Core Tools
| Schema | File | Actions |
|---|---|---|
BrowseProjectsSchema |
src/entities/core/schema-readonly.ts |
search, list, get |
BrowseNamespacesSchema |
src/entities/core/schema-readonly.ts |
list, get, verify |
BrowseCommitsSchema |
src/entities/core/schema-readonly.ts |
list, get, diff |
BrowseEventsSchema |
src/entities/core/schema-readonly.ts |
user, project |
ManageRepositorySchema |
src/entities/core/schema.ts |
create, fork |
ManageTodosSchema |
src/entities/core/schema.ts |
mark_done, mark_all_done, restore |
MRS Tools
| Schema | File | Actions |
|---|---|---|
BrowseMergeRequestsSchema |
src/entities/mrs/schema-readonly.ts |
list, get, diffs, compare |
BrowseMrDiscussionsSchema |
src/entities/mrs/schema-readonly.ts |
list, drafts, draft |
ManageMergeRequestSchema |
src/entities/mrs/schema.ts |
create, update, merge |
ManageMrDiscussionSchema |
src/entities/mrs/schema.ts |
comment, thread, reply, update |
ManageDraftNotesSchema |
src/entities/mrs/schema.ts |
create, update, publish, publish_all, delete |
Files Tools
| Schema | File | Actions |
|---|---|---|
BrowseFilesSchema |
src/entities/files/schema-readonly.ts |
tree, content |
ManageFilesSchema |
src/entities/files/schema.ts |
single, batch, upload |
Pipelines Tools
| Schema | File | Actions |
|---|---|---|
BrowsePipelinesSchema |
src/entities/pipelines/schema-readonly.ts |
list, get, jobs, triggers, job, logs |
ManagePipelineSchema |
src/entities/pipelines/schema.ts |
create, retry, cancel |
ManagePipelineJobSchema |
src/entities/pipelines/schema.ts |
play, retry, cancel |
Work Items Tools
| Schema | File | Actions |
|---|---|---|
BrowseWorkItemsSchema |
src/entities/workitems/schema-readonly.ts |
list, get |
ManageWorkItemSchema |
src/entities/workitems/schema.ts |
create, update, delete |
Solution
Replace z.discriminatedUnion() with flat z.object() schemas using .refine() for conditional validation:
// BEFORE (generates oneOf at top level - INCOMPATIBLE)
const Schema = z.discriminatedUnion("action", [
z.object({ action: z.literal("list"), project_id: z.string() }),
z.object({ action: z.literal("get"), project_id: z.string(), sha: z.string() }),
]);
// AFTER (flat object - COMPATIBLE)
const Schema = z.object({
action: z.enum(["list", "get"]),
project_id: z.string(),
sha: z.string().optional(),
}).refine(
(data) => data.action !== "get" || data.sha !== undefined,
{ message: "sha is required for 'get' action", path: ["sha"] }
);Acceptance Criteria
- All 18 schemas refactored to flat object pattern
- Runtime validation via `.refine()` for action-specific required fields
- TypeScript types still correctly infer required fields per action
- All existing tests pass
- JSON Schema output has no `oneOf`/`allOf`/`anyOf` at top level
- Update CLAUDE.md with "AI-Compatible Schema" pattern documentation
Related Issues
This pattern MUST be applied to future CQRS consolidations:
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels