Skip to content

Commit 6f2ee7d

Browse files
committed
refactor(agent): validate input schema properties with arktype
1 parent 1ff0696 commit 6f2ee7d

2 files changed

Lines changed: 49 additions & 7 deletions

File tree

‎src/agent/tool-search.test.ts‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,47 @@ describe("createToolSearchTool", () => {
628628
);
629629
});
630630

631+
async function schemaCardLine(
632+
inputSchema: Record<string, unknown>,
633+
): Promise<string | undefined> {
634+
const def: ToolDefinition = {
635+
name: "mcp__linear__big",
636+
description: "Linear issue with a large schema",
637+
inputSchema,
638+
};
639+
const tool = createToolSearchTool({
640+
search: () => [def.name],
641+
lookup: () => def,
642+
});
643+
const out = await call(tool, { query: "linear" });
644+
return out
645+
.split("\n")
646+
.find((line) => line.startsWith(" {"))
647+
?.trim();
648+
}
649+
650+
test("an oversized input schema's card lists its property names", async () => {
651+
const line = await schemaCardLine({
652+
type: "object",
653+
properties: {
654+
alpha: { type: "string", description: "x".repeat(400) },
655+
beta: { type: "string", description: "y".repeat(400) },
656+
},
657+
required: [],
658+
});
659+
expect(line).toBe('{"properties":["alpha","beta"],"required":[]}');
660+
});
661+
662+
test("an oversized card lists no property names when properties is not an object", async () => {
663+
const line = await schemaCardLine({
664+
type: "object",
665+
properties: null,
666+
description: "z".repeat(TOOL_SEARCH_SCHEMA_MAX),
667+
required: ["alpha"],
668+
});
669+
expect(line).toBe('{"properties":[],"required":["alpha"]}');
670+
});
671+
631672
test("search does not promote matches and keeps description lines short", async () => {
632673
const tool = createToolSearchTool({
633674
search: (q) => linearIndex.search(q),

‎src/agent/tool-search.ts‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -438,18 +438,19 @@ function isEmptyInputSchema(schema: unknown): boolean {
438438
);
439439
}
440440

441+
const InputSchemaProperties = type({ properties: "Record<string, unknown>" });
442+
443+
function schemaProperties(schema: unknown): Record<string, unknown> {
444+
const parsed = InputSchemaProperties(schema);
445+
return parsed instanceof type.errors ? {} : parsed.properties;
446+
}
447+
441448
function compactInputSchema(schema: unknown): string | undefined {
442449
if (isEmptyInputSchema(schema)) return undefined;
443450
const json = JSON.stringify(schema);
444451
if (json.length <= TOOL_SEARCH_SCHEMA_MAX) return json;
445452
const obj = schema as Record<string, unknown>;
446-
const properties = obj.properties;
447-
const keys =
448-
properties !== undefined &&
449-
typeof properties === "object" &&
450-
properties !== null
451-
? Object.keys(properties)
452-
: [];
453+
const keys = Object.keys(schemaProperties(schema));
453454
const required = Array.isArray(obj.required) ? obj.required : [];
454455
const stub = JSON.stringify({ properties: keys, required });
455456
if (stub.length <= TOOL_SEARCH_SCHEMA_MAX) return stub;

0 commit comments

Comments
 (0)