From 9fd56be9cd758d231a62d4ab09739bea9daf7050 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Fri, 5 Dec 2025 01:17:08 +1000 Subject: [PATCH] feat(create-basic-tools): add defensive parameter validation to generated tools - Add defensive checks for required parameters in generated tool code - Prevents crashes when tools are called with missing/empty params - Returns descriptive error messages instead of undefined errors - Update README with explanation of defensive pattern and best practices - Bump to v1.0.5 Based on learnings from emoji-magic deployment: - LLMs sometimes make probe calls with empty params - Defensive checks prevent crashes and provide better error messages - Even with Zod validation, runtime checks are valuable for robustness --- packages/tools/create-basic-tools/README.md | 25 +++++++++++++++++++ .../tools/create-basic-tools/package.json | 2 +- .../src/generators/source-code.ts | 9 +++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/tools/create-basic-tools/README.md b/packages/tools/create-basic-tools/README.md index daf0064..7ae8114 100644 --- a/packages/tools/create-basic-tools/README.md +++ b/packages/tools/create-basic-tools/README.md @@ -102,6 +102,16 @@ export const exampleTool = tool({ description: 'An example tool - customize this for your use case', inputSchema: ExampleToolSchema, async execute(input: z.infer) { + // Defensive check: Validate required parameters + // This prevents crashes when tools are called with missing/empty params + if (!input.text || input.text.trim().length === 0) { + return { + success: false, + error: 'Missing required parameter: text', + message: 'The "text" parameter is required and cannot be empty.', + }; + } + // TODO: Implement the tool logic here console.log('exampleTool called with:', input); @@ -114,6 +124,21 @@ export const exampleTool = tool({ }); ``` +### Why Defensive Parameter Validation? + +Generated tools include defensive checks for required parameters. While Zod validates the schema, these checks prevent crashes in edge cases where: + +- Tools are called with empty/missing parameters during AI exploration +- Parameters are undefined due to serialization issues +- The LLM makes initial "probe" calls to understand tool capabilities + +**Best Practice**: Always validate critical required parameters before using them, especially when: +- The parameter is used in string operations (`.toLowerCase()`, `.trim()`, etc.) +- The parameter is required for the tool's core functionality +- Missing the parameter would cause a runtime error + +This defensive approach ensures tools return helpful error messages instead of crashing. + ## After Generation Once the package is generated: diff --git a/packages/tools/create-basic-tools/package.json b/packages/tools/create-basic-tools/package.json index c85b3aa..57e2f91 100644 --- a/packages/tools/create-basic-tools/package.json +++ b/packages/tools/create-basic-tools/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/create-basic-tools", - "version": "1.0.4", + "version": "1.0.5", "description": "CLI generator for scaffolding production-ready TPMJS tool packages", "type": "module", "bin": { diff --git a/packages/tools/create-basic-tools/src/generators/source-code.ts b/packages/tools/create-basic-tools/src/generators/source-code.ts index 7f5d556..2aceab7 100644 --- a/packages/tools/create-basic-tools/src/generators/source-code.ts +++ b/packages/tools/create-basic-tools/src/generators/source-code.ts @@ -23,6 +23,15 @@ export const ${exportName} = tool({ description: '${description}', inputSchema: ${schemaName}, async execute(input: z.infer) { + // Defensive check: Validate required parameters + if (!input.text || input.text.trim().length === 0) { + return { + success: false, + error: 'Missing required parameter: text', + message: 'The "text" parameter is required and cannot be empty.', + }; + } + // TODO: Implement the tool logic here console.log('${exportName} called with:', input);