import * as interfaces from './smartagent.interfaces.js'; import { BaseToolWrapper } from './smartagent.tools.base.js'; /** * JsonValidatorTool - Validates and formats JSON data * Useful for agents to self-validate their JSON output before completing a task */ export class JsonValidatorTool extends BaseToolWrapper { public name = 'json'; public description = 'Validate and format JSON data. Use this to verify your JSON output is valid before completing a task.'; public actions: interfaces.IToolAction[] = [ { name: 'validate', description: 'Validate that a string is valid JSON and optionally check required fields', parameters: { type: 'object', properties: { jsonString: { type: 'string', description: 'The JSON string to validate', }, requiredFields: { type: 'array', items: { type: 'string' }, description: 'Optional list of field names that must be present at the root level', }, }, required: ['jsonString'], }, }, { name: 'format', description: 'Parse and pretty-print JSON string', parameters: { type: 'object', properties: { jsonString: { type: 'string', description: 'The JSON string to format', }, }, required: ['jsonString'], }, }, ]; async initialize(): Promise { this.isInitialized = true; } async cleanup(): Promise { this.isInitialized = false; } async execute( action: string, params: Record ): Promise { this.validateAction(action); switch (action) { case 'validate': return this.validateJson(params); case 'format': return this.formatJson(params); default: return { success: false, error: `Unknown action: ${action}` }; } } /** * Validate JSON string and optionally check for required fields */ private validateJson(params: Record): interfaces.IToolExecutionResult { const jsonString = params.jsonString as string; const requiredFields = params.requiredFields as string[] | undefined; if (!jsonString || typeof jsonString !== 'string') { return { success: false, error: 'jsonString parameter is required and must be a string', }; } try { const parsed = JSON.parse(jsonString); // Check required fields if specified if (requiredFields && Array.isArray(requiredFields)) { const missingFields = requiredFields.filter((field) => { if (typeof parsed !== 'object' || parsed === null) { return true; } return !(field in parsed); }); if (missingFields.length > 0) { return { success: false, error: `Missing required fields: ${missingFields.join(', ')}`, result: { valid: false, missingFields, presentFields: Object.keys(parsed || {}), }, }; } } return { success: true, result: { valid: true, parsed, type: Array.isArray(parsed) ? 'array' : typeof parsed, fieldCount: typeof parsed === 'object' && parsed !== null ? Object.keys(parsed).length : undefined, }, summary: `JSON is valid (${Array.isArray(parsed) ? 'array' : typeof parsed})`, }; } catch (error) { const errorMessage = (error as Error).message; // Extract position from error message if available const posMatch = errorMessage.match(/position\s*(\d+)/i); const position = posMatch ? parseInt(posMatch[1]) : undefined; // Provide context around the error position let context: string | undefined; if (position !== undefined) { const start = Math.max(0, position - 20); const end = Math.min(jsonString.length, position + 20); context = jsonString.substring(start, end); } return { success: false, error: `Invalid JSON: ${errorMessage}`, result: { valid: false, errorPosition: position, errorContext: context, }, }; } } /** * Format/pretty-print JSON string */ private formatJson(params: Record): interfaces.IToolExecutionResult { const jsonString = params.jsonString as string; if (!jsonString || typeof jsonString !== 'string') { return { success: false, error: 'jsonString parameter is required and must be a string', }; } try { const parsed = JSON.parse(jsonString); const formatted = JSON.stringify(parsed, null, 2); return { success: true, result: formatted, summary: `Formatted JSON (${formatted.length} chars)`, }; } catch (error) { return { success: false, error: `Cannot format invalid JSON: ${(error as Error).message}`, }; } } public getToolExplanation(): string { return `## Tool: json Validate and format JSON data. Use this to verify your JSON output is valid before completing a task. ### Actions: **validate** - Validate that a string is valid JSON and optionally check required fields Parameters: - jsonString (required): The JSON string to validate - requiredFields (optional): Array of field names that must be present Example: json validate {"jsonString": "{\\"invoice_number\\":\\"INV-001\\",\\"total\\":99.99}", "requiredFields": ["invoice_number", "total"]} **format** - Parse and pretty-print JSON string Parameters: - jsonString (required): The JSON string to format Example: json format {"jsonString": "{\\"name\\":\\"test\\",\\"value\\":123}"} `; } getCallSummary(action: string, params: Record): string { const jsonStr = (params.jsonString as string) || ''; const preview = jsonStr.length > 50 ? jsonStr.substring(0, 50) + '...' : jsonStr; switch (action) { case 'validate': const fields = params.requiredFields as string[] | undefined; const fieldInfo = fields ? ` (checking fields: ${fields.join(', ')})` : ''; return `Validate JSON: ${preview}${fieldInfo}`; case 'format': return `Format JSON: ${preview}`; default: return `JSON ${action}: ${preview}`; } } }