diff --git a/kiro/config.py b/kiro/config.py index 9f1f6ce0..cb5aa218 100644 --- a/kiro/config.py +++ b/kiro/config.py @@ -297,6 +297,20 @@ def _get_raw_env_value(var_name: str, env_file: str = ".env") -> Optional[str]: # Set to 0 to disable (not recommended - will cause Kiro API errors). TOOL_DESCRIPTION_MAX_LENGTH: int = int(os.getenv("TOOL_DESCRIPTION_MAX_LENGTH", "10000")) +# ================================================================================================== +# Tool Schema Compression +# ================================================================================================== + +# Strip verbose fields (description, title, examples, $comment) from tool input schemas. +# Reduces token usage significantly when many MCP tools are present. +# Default: true (enabled) +KIRO_COMPRESS_TOOL_SCHEMAS: bool = os.getenv("KIRO_COMPRESS_TOOL_SCHEMAS", "true").lower() in ("true", "1", "yes") + +# Maximum characters for tool descriptions. Longer descriptions are truncated. +# Set to 0 to disable truncation. +# Default: 200 +KIRO_TOOL_DESC_MAX_CHARS: int = int(os.getenv("KIRO_TOOL_DESC_MAX_CHARS", "200")) + # ================================================================================================== # Truncation Recovery Settings # ================================================================================================== diff --git a/kiro/converters_core.py b/kiro/converters_core.py index 21cf758b..054115d1 100644 --- a/kiro/converters_core.py +++ b/kiro/converters_core.py @@ -533,16 +533,61 @@ def validate_tool_names(tools: Optional[List[UnifiedTool]]) -> None: ) +def compress_json_schema(schema: Dict[str, Any]) -> Dict[str, Any]: + """ + Strips verbose fields from JSON Schema properties recursively. + + Removes description, title, examples, $comment, and markdownDescription, + keeping only structural fields needed for tool call generation. + + Args: + schema: JSON Schema dictionary to compress. + + Returns: + New dictionary with verbose fields removed. + """ + if not schema: + return {} + + STRIP_KEYS = {"description", "title", "examples", "$comment", "markdownDescription"} + result = {} + + for key, value in schema.items(): + if key in STRIP_KEYS: + continue + if key == "properties" and isinstance(value, dict): + result[key] = { + pn: compress_json_schema(pv) if isinstance(pv, dict) else pv + for pn, pv in value.items() + } + elif isinstance(value, dict): + result[key] = compress_json_schema(value) + elif isinstance(value, list): + result[key] = [ + compress_json_schema(item) if isinstance(item, dict) else item + for item in value + ] + else: + result[key] = value + + return result + + def convert_tools_to_kiro_format(tools: Optional[List[UnifiedTool]]) -> List[Dict[str, Any]]: """ Converts unified tools to Kiro API format. + Optionally compresses tool schemas and truncates descriptions + based on KIRO_COMPRESS_TOOL_SCHEMAS and KIRO_TOOL_DESC_MAX_CHARS config. + Args: tools: List of tools in unified format Returns: List of tools in Kiro toolSpecification format """ + from kiro.config import KIRO_COMPRESS_TOOL_SCHEMAS, KIRO_TOOL_DESC_MAX_CHARS + if not tools: return [] @@ -550,12 +595,20 @@ def convert_tools_to_kiro_format(tools: Optional[List[UnifiedTool]]) -> List[Dic for tool in tools: # Sanitize parameters from fields that Kiro API doesn't accept sanitized_params = sanitize_json_schema(tool.input_schema) + + # Optionally compress schema to reduce token usage + if KIRO_COMPRESS_TOOL_SCHEMAS: + sanitized_params = compress_json_schema(sanitized_params) # Kiro API requires non-empty description description = tool.description if not description or not description.strip(): description = f"Tool: {tool.name}" logger.debug(f"Tool '{tool.name}' has empty description, using placeholder") + + # Optionally truncate long descriptions + if KIRO_TOOL_DESC_MAX_CHARS and description and len(description) > KIRO_TOOL_DESC_MAX_CHARS: + description = description[:KIRO_TOOL_DESC_MAX_CHARS] + "..." kiro_tools.append({ "toolSpecification": {