import { describe, expect, it } from "vitest"; import { NULL_INPUT_VALUE, parseSchemaArguments } from "../src/schema-input"; import type { ToolDefinition } from "../src/contracts"; const schema = (properties: NonNullable, required: string[] = []): ToolDefinition["inputSchema"] => ({ type: "object", properties, required, }); describe("schema-driven tool arguments", () => { it("parses strings, integers, decimals, booleans, enums, arrays and objects by declared type", () => { const inputSchema = schema({ query: { type: "string" }, limit: { type: "integer", minimum: 1, maximum: 20 }, score: { type: "number" }, includeClosed: { type: "boolean" }, format: { type: "string", enum: ["compact", "full"] }, labels: { type: "array" }, filters: { type: "object" }, }, ["query", "limit", "includeClosed", "format"]); expect(parseSchemaArguments({ query: "pull request", limit: "4", score: "3.5", includeClosed: "false", format: JSON.stringify("full"), labels: "[\"bug\",\"urgent\"]", filters: "{\"author\":\"jens\"}", }, inputSchema)).toEqual({ arguments: { query: "pull request", limit: 4, score: 3.5, includeClosed: false, format: "full", labels: ["bug", "urgent"], filters: { author: "jens" }, }, errors: {}, valid: true, }); }); it("reports missing required values, malformed JSON and type mismatches clearly", () => { const inputSchema = schema({ count: { type: "integer" }, enabled: { type: "boolean" }, tags: { type: "array" }, options: { type: "object" }, }, ["count", "enabled", "tags", "options"]); const parsed = parseSchemaArguments({ count: "2.3", enabled: "sometimes", tags: "{oops", options: "[]" }, inputSchema); expect(parsed.valid).toBe(false); expect(parsed.arguments).toEqual({}); expect(parsed.errors).toEqual({ count: "Enter a whole number.", enabled: "Choose yes or no.", tags: "Enter valid JSON for this array.", options: "Enter a JSON object.", }); }); it("applies string and numeric bounds and rejects enum values outside the schema", () => { const inputSchema = schema({ query: { type: "string", minLength: 2, maxLength: 5 }, limit: { type: "integer", minimum: 1, maximum: 8 }, mode: { type: "string", enum: ["safe", "full"] }, }); const parsed = parseSchemaArguments({ query: "x", limit: "9", mode: JSON.stringify("unsafe") }, inputSchema); expect(parsed.valid).toBe(false); expect(parsed.errors.query).toBe("Enter at least 2 characters."); expect(parsed.errors.limit).toBe("Enter a number no greater than 8."); expect(parsed.errors.mode).toBe("Choose a value listed by this tool's schema."); }); it("omits empty optional fields and accepts explicit null only for nullable fields", () => { const inputSchema = schema({ optional: { type: "string" }, nullable: { type: ["string", "null"] } }); expect(parseSchemaArguments({ optional: "", nullable: NULL_INPUT_VALUE }, inputSchema)).toEqual({ arguments: { nullable: null }, errors: {}, valid: true, }); expect(parseSchemaArguments({ nullable: NULL_INPUT_VALUE }, schema({ nullable: { type: "string" } })).errors.nullable).toBe("This field does not accept null."); }); });