Skip to content

Commit 21655ed

Browse files
committed
fix(core): strip additionalProperties from tool schemas
1 parent ad317b6 commit 21655ed

File tree

3 files changed

+82
-7
lines changed

3 files changed

+82
-7
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { describe, expect, it } from "vitest";
2+
import { z } from "zod";
3+
import { RunHandler } from "../run-handler";
4+
import type { CopilotKitCore } from "../core";
5+
6+
function createRunHandler(): RunHandler {
7+
return new RunHandler({} as CopilotKitCore);
8+
}
9+
10+
describe("RunHandler tool schema generation", () => {
11+
it("strips boolean additionalProperties emitted by zod-to-json-schema", () => {
12+
const runHandler = createRunHandler();
13+
runHandler.initialize([
14+
{
15+
name: "sample",
16+
parameters: z.object({
17+
foo: z.string(),
18+
}),
19+
},
20+
]);
21+
22+
const [tool] = runHandler.buildFrontendTools();
23+
expect(tool.parameters).toEqual({
24+
type: "object",
25+
properties: {
26+
foo: { type: "string" },
27+
},
28+
required: ["foo"],
29+
});
30+
});
31+
32+
it("removes additionalProperties even for catchalls", () => {
33+
const runHandler = createRunHandler();
34+
runHandler.initialize([
35+
{
36+
name: "catchall",
37+
parameters: z.object({}).catchall(z.string()),
38+
},
39+
]);
40+
41+
const [tool] = runHandler.buildFrontendTools();
42+
expect(tool.parameters).not.toHaveProperty("additionalProperties");
43+
});
44+
45+
it("returns an empty schema for tools without parameters", () => {
46+
const runHandler = createRunHandler();
47+
runHandler.initialize([
48+
{
49+
name: "noSchema",
50+
},
51+
]);
52+
53+
const [tool] = runHandler.buildFrontendTools();
54+
expect(tool.parameters).toEqual({
55+
type: "object",
56+
properties: {},
57+
});
58+
});
59+
});

packages/core/src/core/run-handler.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -550,15 +550,14 @@ export class RunHandler {
550550
const EMPTY_TOOL_SCHEMA = {
551551
type: "object",
552552
properties: {},
553-
additionalProperties: false,
554553
} as const satisfies Record<string, unknown>;
555554

556555
/**
557556
* Create a JSON schema from a tool's parameters
558557
*/
559558
function createToolSchema(tool: FrontendTool<any>): Record<string, unknown> {
560559
if (!tool.parameters) {
561-
return EMPTY_TOOL_SCHEMA;
560+
return { ...EMPTY_TOOL_SCHEMA };
562561
}
563562

564563
const rawSchema = zodToJsonSchema(tool.parameters, {
@@ -577,9 +576,28 @@ function createToolSchema(tool: FrontendTool<any>): Record<string, unknown> {
577576
if (typeof schema.properties !== "object" || schema.properties === null) {
578577
schema.properties = {};
579578
}
580-
if (schema.additionalProperties === undefined) {
581-
schema.additionalProperties = false;
582-
}
583579

580+
stripAdditionalProperties(schema);
584581
return schema;
585582
}
583+
584+
function stripAdditionalProperties(schema: unknown): void {
585+
if (!schema || typeof schema !== "object") {
586+
return;
587+
}
588+
589+
if (Array.isArray(schema)) {
590+
schema.forEach(stripAdditionalProperties);
591+
return;
592+
}
593+
594+
const record = schema as Record<string, unknown>;
595+
596+
if (record.additionalProperties !== undefined) {
597+
delete record.additionalProperties;
598+
}
599+
600+
for (const value of Object.values(record)) {
601+
stripAdditionalProperties(value);
602+
}
603+
}

packages/core/src/core/suggestion-engine.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,11 +434,9 @@ const SUGGEST_TOOL: Tool = {
434434
},
435435
},
436436
required: ["title", "message"],
437-
additionalProperties: false,
438437
},
439438
},
440439
},
441440
required: ["suggestions"],
442-
additionalProperties: false,
443441
},
444442
};

0 commit comments

Comments
 (0)