#!/usr/bin/env python3 """ Tool Schema Generator - Generate structured tool schemas for AI agents Given a description of desired tools (name, purpose, inputs, outputs), generates structured tool schemas compatible with OpenAI function calling format and Anthropic tool use format. Includes: input validation rules, error response formats, example calls, rate limit suggestions. Input: tool descriptions JSON Output: tool schemas (OpenAI + Anthropic format) + validation rules + example usage """ import json import argparse import sys import re from typing import Dict, List, Any, Optional, Union, Tuple from dataclasses import dataclass, asdict from enum import Enum class ParameterType(Enum): """Parameter types for tool schemas""" STRING = "string" INTEGER = "integer" NUMBER = "number" BOOLEAN = "boolean" ARRAY = "array" OBJECT = "object" NULL = "null" class ValidationRule(Enum): """Validation rule types""" REQUIRED = "required" MIN_LENGTH = "min_length" MAX_LENGTH = "max_length" PATTERN = "pattern" ENUM = "enum" MINIMUM = "minimum" MAXIMUM = "maximum" MIN_ITEMS = "min_items" MAX_ITEMS = "max_items" UNIQUE_ITEMS = "unique_items" FORMAT = "format" @dataclass class ParameterSpec: """Parameter specification for tool inputs/outputs""" name: str type: ParameterType description: str required: bool = False default: Any = None validation_rules: Dict[str, Any] = None examples: List[Any] = None deprecated: bool = False @dataclass class ErrorSpec: """Error specification for tool responses""" error_code: str error_message: str http_status: int retry_after: Optional[int] = None details: Dict[str, Any] = None @dataclass class RateLimitSpec: """Rate limiting specification""" requests_per_minute: int requests_per_hour: int requests_per_day: int burst_limit: int cooldown_period: int rate_limit_key: str = "user_id" @dataclass class ToolDescription: """Input tool description""" name: str purpose: str category: str inputs: List[Dict[str, Any]] outputs: List[Dict[str, Any]] error_conditions: List[str] side_effects: List[str] idempotent: bool rate_limits: Dict[str, Any] dependencies: List[str] examples: List[Dict[str, Any]] security_requirements: List[str] @dataclass class ToolSchema: """Complete tool schema with validation and examples""" name: str description: str openai_schema: Dict[str, Any] anthropic_schema: Dict[str, Any] validation_rules: List[Dict[str, Any]] error_responses: List[ErrorSpec] rate_limits: RateLimitSpec examples: List[Dict[str, Any]] metadata: Dict[str, Any] class ToolSchemaGenerator: """Generate structured tool schemas from descriptions""" def __init__(self): self.common_patterns = self._define_common_patterns() self.format_validators = self._define_format_validators() self.security_templates = self._define_security_templates() def _define_common_patterns(self) -> Dict[str, str]: """Define common regex patterns for validation""" return { "email": r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", "url": r"^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$", "uuid": r"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", "phone": r"^\+?1?[0-9]{10,15}$", "ip_address": r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", "date": r"^\d{4}-\d{2}-\d{2}$", "datetime": r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z?$", "slug": r"^[a-z0-9]+(?:-[a-z0-9]+)*$", "semantic_version": r"^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$" } def _define_format_validators(self) -> Dict[str, Dict[str, Any]]: """Define format validators for common data types""" return { "email": { "type": "string", "format": "email", "pattern": self.common_patterns["email"], "min_length": 5, "max_length": 254 }, "url": { "type": "string", "format": "uri", "pattern": self.common_patterns["url"], "min_length": 7, "max_length": 2048 }, "uuid": { "type": "string", "format": "uuid", "pattern": self.common_patterns["uuid"], "min_length": 36, "max_length": 36 }, "date": { "type": "string", "format": "date", "pattern": self.common_patterns["date"], "min_length": 10, "max_length": 10 }, "datetime": { "type": "string", "format": "date-time", "pattern": self.common_patterns["datetime"], "min_length": 19, "max_length": 30 }, "password": { "type": "string", "min_length": 8, "max_length": 128, "pattern": r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]" } } def _define_security_templates(self) -> Dict[str, Dict[str, Any]]: """Define security requirement templates""" return { "authentication_required": { "requires_auth": True, "auth_methods": ["bearer_token", "api_key"], "scope_required": ["read", "write"] }, "rate_limited": { "rate_limits": { "requests_per_minute": 60, "requests_per_hour": 1000, "burst_limit": 10 } }, "input_sanitization": { "sanitize_html": True, "validate_sql_injection": True, "escape_special_chars": True }, "output_validation": { "validate_response_schema": True, "filter_sensitive_data": True, "content_type_validation": True } } def parse_tool_description(self, description: ToolDescription) -> ParameterSpec: """Parse tool description into structured parameters""" input_params = [] output_params = [] # Parse input parameters for input_spec in description.inputs: param = self._parse_parameter_spec(input_spec) input_params.append(param) # Parse output parameters for output_spec in description.outputs: param = self._parse_parameter_spec(output_spec) output_params.append(param) return input_params, output_params def _parse_parameter_spec(self, param_spec: Dict[str, Any]) -> ParameterSpec: """Parse individual parameter specification""" name = param_spec.get("name", "") type_str = param_spec.get("type", "string") description = param_spec.get("description", "") required = param_spec.get("required", False) default = param_spec.get("default") examples = param_spec.get("examples", []) # Parse parameter type param_type = self._parse_parameter_type(type_str) # Generate validation rules validation_rules = self._generate_validation_rules(param_spec, param_type) return ParameterSpec( name=name, type=param_type, description=description, required=required, default=default, validation_rules=validation_rules, examples=examples ) def _parse_parameter_type(self, type_str: str) -> ParameterType: """Parse parameter type from string""" type_mapping = { "str": ParameterType.STRING, "string": ParameterType.STRING, "text": ParameterType.STRING, "int": ParameterType.INTEGER, "integer": ParameterType.INTEGER, "float": ParameterType.NUMBER, "number": ParameterType.NUMBER, "bool": ParameterType.BOOLEAN, "boolean": ParameterType.BOOLEAN, "list": ParameterType.ARRAY, "array": ParameterType.ARRAY, "dict": ParameterType.OBJECT, "object": ParameterType.OBJECT, "null": ParameterType.NULL, "none": ParameterType.NULL } return type_mapping.get(type_str.lower(), ParameterType.STRING) def _generate_validation_rules(self, param_spec: Dict[str, Any], param_type: ParameterType) -> Dict[str, Any]: """Generate validation rules for a parameter""" rules = {} # Type-specific validation if param_type == ParameterType.STRING: rules.update(self._generate_string_validation(param_spec)) elif param_type == ParameterType.INTEGER: rules.update(self._generate_integer_validation(param_spec)) elif param_type == ParameterType.NUMBER: rules.update(self._generate_number_validation(param_spec)) elif param_type == ParameterType.ARRAY: rules.update(self._generate_array_validation(param_spec)) elif param_type == ParameterType.OBJECT: rules.update(self._generate_object_validation(param_spec)) # Common validation rules if param_spec.get("required", False): rules["required"] = True if "enum" in param_spec: rules["enum"] = param_spec["enum"] if "pattern" in param_spec: rules["pattern"] = param_spec["pattern"] elif self._detect_format(param_spec.get("name", ""), param_spec.get("description", "")): format_name = self._detect_format(param_spec.get("name", ""), param_spec.get("description", "")) if format_name in self.format_validators: rules.update(self.format_validators[format_name]) return rules def _generate_string_validation(self, param_spec: Dict[str, Any]) -> Dict[str, Any]: """Generate string-specific validation rules""" rules = {} if "min_length" in param_spec: rules["minLength"] = param_spec["min_length"] elif "min_len" in param_spec: rules["minLength"] = param_spec["min_len"] else: # Infer from description desc = param_spec.get("description", "").lower() if "password" in desc: rules["minLength"] = 8 elif "email" in desc: rules["minLength"] = 5 elif "name" in desc: rules["minLength"] = 1 if "max_length" in param_spec: rules["maxLength"] = param_spec["max_length"] elif "max_len" in param_spec: rules["maxLength"] = param_spec["max_len"] else: # Reasonable defaults desc = param_spec.get("description", "").lower() if "password" in desc: rules["maxLength"] = 128 elif "email" in desc: rules["maxLength"] = 254 elif "description" in desc or "content" in desc: rules["maxLength"] = 10000 elif "name" in desc or "title" in desc: rules["maxLength"] = 255 else: rules["maxLength"] = 1000 return rules def _generate_integer_validation(self, param_spec: Dict[str, Any]) -> Dict[str, Any]: """Generate integer-specific validation rules""" rules = {} if "minimum" in param_spec: rules["minimum"] = param_spec["minimum"] elif "min" in param_spec: rules["minimum"] = param_spec["min"] else: # Infer from context name = param_spec.get("name", "").lower() desc = param_spec.get("description", "").lower() if any(word in name + desc for word in ["count", "quantity", "amount", "size", "limit"]): rules["minimum"] = 0 elif "page" in name + desc: rules["minimum"] = 1 elif "port" in name + desc: rules["minimum"] = 1 rules["maximum"] = 65535 if "maximum" in param_spec: rules["maximum"] = param_spec["maximum"] elif "max" in param_spec: rules["maximum"] = param_spec["max"] return rules def _generate_number_validation(self, param_spec: Dict[str, Any]) -> Dict[str, Any]: """Generate number-specific validation rules""" rules = {} if "minimum" in param_spec: rules["minimum"] = param_spec["minimum"] if "maximum" in param_spec: rules["maximum"] = param_spec["maximum"] if "exclusive_minimum" in param_spec: rules["exclusiveMinimum"] = param_spec["exclusive_minimum"] if "exclusive_maximum" in param_spec: rules["exclusiveMaximum"] = param_spec["exclusive_maximum"] if "multiple_of" in param_spec: rules["multipleOf"] = param_spec["multiple_of"] return rules def _generate_array_validation(self, param_spec: Dict[str, Any]) -> Dict[str, Any]: """Generate array-specific validation rules""" rules = {} if "min_items" in param_spec: rules["minItems"] = param_spec["min_items"] elif "min_length" in param_spec: rules["minItems"] = param_spec["min_length"] else: rules["minItems"] = 0 if "max_items" in param_spec: rules["maxItems"] = param_spec["max_items"] elif "max_length" in param_spec: rules["maxItems"] = param_spec["max_length"] else: rules["maxItems"] = 1000 # Reasonable default if param_spec.get("unique_items", False): rules["uniqueItems"] = True if "item_type" in param_spec: rules["items"] = {"type": param_spec["item_type"]} return rules def _generate_object_validation(self, param_spec: Dict[str, Any]) -> Dict[str, Any]: """Generate object-specific validation rules""" rules = {} if "properties" in param_spec: rules["properties"] = param_spec["properties"] if "required_properties" in param_spec: rules["required"] = param_spec["required_properties"] if "additional_properties" in param_spec: rules["additionalProperties"] = param_spec["additional_properties"] else: rules["additionalProperties"] = False if "min_properties" in param_spec: rules["minProperties"] = param_spec["min_properties"] if "max_properties" in param_spec: rules["maxProperties"] = param_spec["max_properties"] return rules def _detect_format(self, name: str, description: str) -> Optional[str]: """Detect parameter format from name and description""" combined = (name + " " + description).lower() format_indicators = { "email": ["email", "e-mail", "email_address"], "url": ["url", "uri", "link", "website", "endpoint"], "uuid": ["uuid", "guid", "identifier", "id"], "date": ["date", "birthday", "created_date", "modified_date"], "datetime": ["datetime", "timestamp", "created_at", "updated_at"], "password": ["password", "secret", "token", "api_key"] } for format_name, indicators in format_indicators.items(): if any(indicator in combined for indicator in indicators): return format_name return None def generate_openai_schema(self, description: ToolDescription, input_params: List[ParameterSpec]) -> Dict[str, Any]: """Generate OpenAI function calling schema""" properties = {} required = [] for param in input_params: prop_def = { "type": param.type.value, "description": param.description } # Add validation rules if param.validation_rules: prop_def.update(param.validation_rules) # Add examples if param.examples: prop_def["examples"] = param.examples # Add default value if param.default is not None: prop_def["default"] = param.default properties[param.name] = prop_def if param.required: required.append(param.name) schema = { "name": description.name, "description": description.purpose, "parameters": { "type": "object", "properties": properties, "required": required, "additionalProperties": False } } return schema def generate_anthropic_schema(self, description: ToolDescription, input_params: List[ParameterSpec]) -> Dict[str, Any]: """Generate Anthropic tool use schema""" input_schema = { "type": "object", "properties": {}, "required": [] } for param in input_params: prop_def = { "type": param.type.value, "description": param.description } # Add validation rules (Anthropic uses subset of JSON Schema) if param.validation_rules: # Filter to supported validation rules supported_rules = ["minLength", "maxLength", "minimum", "maximum", "pattern", "enum", "items"] for rule, value in param.validation_rules.items(): if rule in supported_rules: prop_def[rule] = value input_schema["properties"][param.name] = prop_def if param.required: input_schema["required"].append(param.name) schema = { "name": description.name, "description": description.purpose, "input_schema": input_schema } return schema def generate_error_responses(self, description: ToolDescription) -> List[ErrorSpec]: """Generate error response specifications""" error_specs = [] # Common errors common_errors = [ { "error_code": "invalid_input", "error_message": "Invalid input parameters provided", "http_status": 400, "details": {"validation_errors": []} }, { "error_code": "authentication_required", "error_message": "Authentication required to access this tool", "http_status": 401 }, { "error_code": "insufficient_permissions", "error_message": "Insufficient permissions to perform this operation", "http_status": 403 }, { "error_code": "rate_limit_exceeded", "error_message": "Rate limit exceeded. Please try again later", "http_status": 429, "retry_after": 60 }, { "error_code": "internal_error", "error_message": "Internal server error occurred", "http_status": 500 }, { "error_code": "service_unavailable", "error_message": "Service temporarily unavailable", "http_status": 503, "retry_after": 300 } ] # Add common errors for error in common_errors: error_specs.append(ErrorSpec(**error)) # Add tool-specific errors based on error conditions for condition in description.error_conditions: if "not found" in condition.lower(): error_specs.append(ErrorSpec( error_code="resource_not_found", error_message=f"Requested resource not found: {condition}", http_status=404 )) elif "timeout" in condition.lower(): error_specs.append(ErrorSpec( error_code="operation_timeout", error_message=f"Operation timed out: {condition}", http_status=408, retry_after=30 )) elif "quota" in condition.lower() or "limit" in condition.lower(): error_specs.append(ErrorSpec( error_code="quota_exceeded", error_message=f"Quota or limit exceeded: {condition}", http_status=429, retry_after=3600 )) elif "dependency" in condition.lower(): error_specs.append(ErrorSpec( error_code="dependency_failure", error_message=f"Dependency service failure: {condition}", http_status=502 )) return error_specs def generate_rate_limits(self, description: ToolDescription) -> RateLimitSpec: """Generate rate limiting specification""" rate_limits = description.rate_limits # Default rate limits based on tool category defaults = { "search": {"rpm": 60, "rph": 1000, "rpd": 10000, "burst": 10}, "data": {"rpm": 30, "rph": 500, "rpd": 5000, "burst": 5}, "api": {"rpm": 100, "rph": 2000, "rpd": 20000, "burst": 20}, "file": {"rpm": 120, "rph": 3000, "rpd": 30000, "burst": 30}, "compute": {"rpm": 10, "rph": 100, "rpd": 1000, "burst": 3}, "communication": {"rpm": 30, "rph": 300, "rpd": 3000, "burst": 5} } category_defaults = defaults.get(description.category.lower(), defaults["api"]) return RateLimitSpec( requests_per_minute=rate_limits.get("requests_per_minute", category_defaults["rpm"]), requests_per_hour=rate_limits.get("requests_per_hour", category_defaults["rph"]), requests_per_day=rate_limits.get("requests_per_day", category_defaults["rpd"]), burst_limit=rate_limits.get("burst_limit", category_defaults["burst"]), cooldown_period=rate_limits.get("cooldown_period", 60), rate_limit_key=rate_limits.get("rate_limit_key", "user_id") ) def generate_examples(self, description: ToolDescription, input_params: List[ParameterSpec]) -> List[Dict[str, Any]]: """Generate usage examples""" examples = [] # Use provided examples if available if description.examples: for example in description.examples: examples.append(example) # Generate synthetic examples if len(examples) == 0: synthetic_example = self._generate_synthetic_example(description, input_params) if synthetic_example: examples.append(synthetic_example) # Ensure we have multiple examples showing different scenarios if len(examples) == 1 and len(input_params) > 1: # Generate minimal example minimal_example = self._generate_minimal_example(description, input_params) if minimal_example and minimal_example != examples[0]: examples.append(minimal_example) return examples def _generate_synthetic_example(self, description: ToolDescription, input_params: List[ParameterSpec]) -> Dict[str, Any]: """Generate a synthetic example based on parameter specifications""" example_input = {} for param in input_params: if param.examples: example_input[param.name] = param.examples[0] elif param.default is not None: example_input[param.name] = param.default else: example_input[param.name] = self._generate_example_value(param) # Generate expected output based on tool purpose expected_output = self._generate_example_output(description) return { "description": f"Example usage of {description.name}", "input": example_input, "expected_output": expected_output } def _generate_minimal_example(self, description: ToolDescription, input_params: List[ParameterSpec]) -> Dict[str, Any]: """Generate minimal example with only required parameters""" example_input = {} for param in input_params: if param.required: if param.examples: example_input[param.name] = param.examples[0] else: example_input[param.name] = self._generate_example_value(param) if not example_input: return None expected_output = self._generate_example_output(description) return { "description": f"Minimal example of {description.name} with required parameters only", "input": example_input, "expected_output": expected_output } def _generate_example_value(self, param: ParameterSpec) -> Any: """Generate example value for a parameter""" if param.type == ParameterType.STRING: format_examples = { "email": "user@example.com", "url": "https://example.com", "uuid": "123e4567-e89b-12d3-a456-426614174000", "date": "2024-01-15", "datetime": "2024-01-15T10:30:00Z" } # Check for format in validation rules if param.validation_rules and "format" in param.validation_rules: format_type = param.validation_rules["format"] if format_type in format_examples: return format_examples[format_type] # Check for patterns or enum if param.validation_rules: if "enum" in param.validation_rules: return param.validation_rules["enum"][0] # Generate based on name/description name_lower = param.name.lower() if "name" in name_lower: return "example_name" elif "query" in name_lower or "search" in name_lower: return "search query" elif "path" in name_lower: return "/path/to/resource" elif "message" in name_lower: return "Example message" else: return "example_value" elif param.type == ParameterType.INTEGER: if param.validation_rules: min_val = param.validation_rules.get("minimum", 0) max_val = param.validation_rules.get("maximum", 100) return min(max(42, min_val), max_val) return 42 elif param.type == ParameterType.NUMBER: if param.validation_rules: min_val = param.validation_rules.get("minimum", 0.0) max_val = param.validation_rules.get("maximum", 100.0) return min(max(42.5, min_val), max_val) return 42.5 elif param.type == ParameterType.BOOLEAN: return True elif param.type == ParameterType.ARRAY: return ["item1", "item2"] elif param.type == ParameterType.OBJECT: return {"key": "value"} else: return None def _generate_example_output(self, description: ToolDescription) -> Dict[str, Any]: """Generate example output based on tool description""" category = description.category.lower() if category == "search": return { "results": [ {"title": "Example Result 1", "url": "https://example.com/1", "snippet": "Example snippet..."}, {"title": "Example Result 2", "url": "https://example.com/2", "snippet": "Another snippet..."} ], "total_count": 2 } elif category == "data": return { "data": [{"id": 1, "value": "example"}, {"id": 2, "value": "another"}], "metadata": {"count": 2, "processed_at": "2024-01-15T10:30:00Z"} } elif category == "file": return { "success": True, "file_path": "/path/to/file.txt", "size": 1024, "modified_at": "2024-01-15T10:30:00Z" } elif category == "api": return { "status": "success", "data": {"result": "operation completed successfully"}, "timestamp": "2024-01-15T10:30:00Z" } else: return { "success": True, "message": f"{description.name} executed successfully", "result": "example result" } def generate_tool_schema(self, description: ToolDescription) -> ToolSchema: """Generate complete tool schema""" # Parse parameters input_params, output_params = self.parse_tool_description(description) # Generate schemas openai_schema = self.generate_openai_schema(description, input_params) anthropic_schema = self.generate_anthropic_schema(description, input_params) # Generate validation rules validation_rules = [] for param in input_params: if param.validation_rules: validation_rules.append({ "parameter": param.name, "rules": param.validation_rules }) # Generate error responses error_responses = self.generate_error_responses(description) # Generate rate limits rate_limits = self.generate_rate_limits(description) # Generate examples examples = self.generate_examples(description, input_params) # Generate metadata metadata = { "category": description.category, "idempotent": description.idempotent, "side_effects": description.side_effects, "dependencies": description.dependencies, "security_requirements": description.security_requirements, "generated_at": "2024-01-15T10:30:00Z", "schema_version": "1.0", "input_parameters": len(input_params), "output_parameters": len(output_params), "required_parameters": sum(1 for p in input_params if p.required), "optional_parameters": sum(1 for p in input_params if not p.required) } return ToolSchema( name=description.name, description=description.purpose, openai_schema=openai_schema, anthropic_schema=anthropic_schema, validation_rules=validation_rules, error_responses=error_responses, rate_limits=rate_limits, examples=examples, metadata=metadata ) def main(): parser = argparse.ArgumentParser(description="Tool Schema Generator for AI Agents") parser.add_argument("input_file", help="JSON file with tool descriptions") parser.add_argument("-o", "--output", help="Output file prefix (default: tool_schemas)") parser.add_argument("--format", choices=["json", "both"], default="both", help="Output format") parser.add_argument("--validate", action="store_true", help="Validate generated schemas") args = parser.parse_args() try: # Load tool descriptions with open(args.input_file, 'r') as f: tools_data = json.load(f) # Parse tool descriptions tool_descriptions = [] for tool_data in tools_data.get("tools", []): tool_desc = ToolDescription(**tool_data) tool_descriptions.append(tool_desc) # Generate schemas generator = ToolSchemaGenerator() schemas = [] for description in tool_descriptions: schema = generator.generate_tool_schema(description) schemas.append(schema) print(f"Generated schema for: {schema.name}") # Prepare output output_data = { "tool_schemas": [asdict(schema) for schema in schemas], "metadata": { "generated_by": "tool_schema_generator.py", "input_file": args.input_file, "tool_count": len(schemas), "generation_timestamp": "2024-01-15T10:30:00Z", "schema_version": "1.0" }, "validation_summary": { "total_tools": len(schemas), "total_parameters": sum(schema.metadata["input_parameters"] for schema in schemas), "total_validation_rules": sum(len(schema.validation_rules) for schema in schemas), "total_examples": sum(len(schema.examples) for schema in schemas) } } # Output files output_prefix = args.output or "tool_schemas" if args.format in ["json", "both"]: with open(f"{output_prefix}.json", 'w') as f: json.dump(output_data, f, indent=2, default=str) print(f"JSON output written to {output_prefix}.json") if args.format == "both": # Generate separate files for different formats # OpenAI format openai_schemas = { "functions": [schema.openai_schema for schema in schemas] } with open(f"{output_prefix}_openai.json", 'w') as f: json.dump(openai_schemas, f, indent=2) print(f"OpenAI schemas written to {output_prefix}_openai.json") # Anthropic format anthropic_schemas = { "tools": [schema.anthropic_schema for schema in schemas] } with open(f"{output_prefix}_anthropic.json", 'w') as f: json.dump(anthropic_schemas, f, indent=2) print(f"Anthropic schemas written to {output_prefix}_anthropic.json") # Validation rules validation_data = { "validation_rules": {schema.name: schema.validation_rules for schema in schemas} } with open(f"{output_prefix}_validation.json", 'w') as f: json.dump(validation_data, f, indent=2) print(f"Validation rules written to {output_prefix}_validation.json") # Usage examples examples_data = { "examples": {schema.name: schema.examples for schema in schemas} } with open(f"{output_prefix}_examples.json", 'w') as f: json.dump(examples_data, f, indent=2) print(f"Usage examples written to {output_prefix}_examples.json") # Print summary print(f"\nSchema Generation Summary:") print(f"Tools processed: {len(schemas)}") print(f"Total input parameters: {sum(schema.metadata['input_parameters'] for schema in schemas)}") print(f"Total validation rules: {sum(len(schema.validation_rules) for schema in schemas)}") print(f"Total examples generated: {sum(len(schema.examples) for schema in schemas)}") # Validation if requested if args.validate: print("\nValidation Results:") for schema in schemas: validation_errors = [] # Basic validation checks if not schema.openai_schema.get("parameters", {}).get("properties"): validation_errors.append("Missing input parameters") if not schema.examples: validation_errors.append("No usage examples") if not schema.validation_rules: validation_errors.append("No validation rules defined") if validation_errors: print(f" {schema.name}: {', '.join(validation_errors)}") else: print(f" {schema.name}: ✓ Valid") except Exception as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main()