fix: add type validation for tool parameters (#45)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2025-10-31 14:47:07 -07:00
committed by GitHub
parent b54b30af14
commit af5a2fa68a
4 changed files with 319 additions and 1 deletions

View File

@@ -1,7 +1,8 @@
import { readdir, stat } from "node:fs/promises";
import { join, resolve } from "node:path";
import picomatch from "picomatch";
import { validateRequiredParams } from "./validation.js";
import LSSchema from "../schemas/LS.json";
import { validateParamTypes, validateRequiredParams } from "./validation.js";
interface LSArgs {
path: string;
@@ -17,6 +18,7 @@ export async function ls(
args: LSArgs,
): Promise<{ content: Array<{ type: string; text: string }> }> {
validateRequiredParams(args, ["path"], "LS");
validateParamTypes(args, LSSchema, "LS");
const { path: inputPath, ignore = [] } = args;
const dirPath = resolve(inputPath);
try {

View File

@@ -12,3 +12,77 @@ export function validateRequiredParams<T extends object>(
);
}
}
interface JsonSchema {
type?: string;
properties?: Record<string, JsonSchema>;
items?: JsonSchema;
required?: string[];
[key: string]: unknown;
}
/**
* Validates that parameter values match their expected types from the JSON schema.
* Throws a clear error if types don't match.
*/
export function validateParamTypes(
args: Record<string, unknown>,
schema: JsonSchema,
toolName: string,
): void {
if (!schema.properties) return;
for (const [paramName, paramSchema] of Object.entries(schema.properties)) {
const value = args[paramName];
// Skip undefined optional parameters
if (value === undefined) continue;
const expectedType = paramSchema.type;
if (!expectedType) continue;
const actualType = getJsonSchemaType(value);
if (actualType !== expectedType) {
const article = ["array", "object", "integer"].includes(expectedType)
? "an"
: "a";
throw new Error(
`${toolName}: Parameter '${paramName}' must be ${article} ${expectedType}, received ${actualType}`,
);
}
// Additional validation for arrays to ensure they contain the right element types
if (expectedType === "array" && Array.isArray(value) && paramSchema.items) {
const itemType = paramSchema.items.type;
if (itemType) {
for (let i = 0; i < value.length; i++) {
const itemActualType = getJsonSchemaType(value[i]);
if (itemActualType !== itemType) {
const article = ["array", "object", "integer"].includes(itemType)
? "an"
: "a";
throw new Error(
`${toolName}: Parameter '${paramName}[${i}]' must be ${article} ${itemType}, received ${itemActualType}`,
);
}
}
}
}
}
}
/**
* Gets the JSON Schema type name for a JavaScript value.
*/
function getJsonSchemaType(value: unknown): string {
if (value === null) return "null";
if (Array.isArray(value)) return "array";
if (typeof value === "object") return "object";
if (typeof value === "boolean") return "boolean";
if (typeof value === "number") {
return Number.isInteger(value) ? "integer" : "number";
}
if (typeof value === "string") return "string";
return "unknown";
}