Chapter 19

Tool Search Tool: BM25 + Regex Dual-Engine Dynamic Loading for 10,000-Tool Libraries

Chapter 19: Tool Design Patterns: Parameter Schema, Description Optimization, and Type Constraints

19.1 Why Tool Design Matters

The quality of tool definitions directly determines how accurately Claude uses them. A vaguely described tool causes Claude to invoke it in inappropriate scenarios or pass incorrectly formatted parameters. Conversely, a carefully designed tool Schema enables Claude to make correct judgments in complex situations.

Tool design has three core objectives:

  1. Semantic clarity: Claude must accurately understand the tool's purpose and limitations from its name and description
  2. Complete parameter constraints: JSON Schema constraints maximize the reduction of invalid parameter inputs
  3. Error tolerance: Even if Claude passes slightly off-target parameters, the tool handles them gracefully

19.2 Best Practices for Tool Naming

Naming Conventions

Tool names should follow these rules:

# Good names: verb_noun format, semantically clear
good_names = [
    "search_products",
    "create_invoice",
    "get_user_profile",
    "update_order_status",
    "send_notification",
    "calculate_shipping",
]

# Poor names: vague or overly broad
bad_names = [
    "process",     # Process what?
    "data",        # Data operation?
    "helper",      # Help with what?
    "do_thing",    # Do what?
    "api_call",    # Which API?
]

Avoiding Name Conflicts

When multiple tools have similar functionality, names must reflect key differences:

tools_with_clear_scope = [
    {
        "name": "search_internal_knowledge_base",
        "description": "Search the company's internal knowledge base for documents and FAQs. Contains only approved internal documents."
    },
    {
        "name": "search_web",
        "description": "Search publicly available information on the internet via a search engine. Results may include unverified content."
    },
    {
        "name": "search_product_catalog",
        "description": "Search the product catalog for merchandise information including prices, inventory, and specifications."
    }
]

19.3 Optimization Strategies for the description Field

The description is the most overlooked yet most important part of tool design. It is not just human-readable documentation โ€” it is the primary basis Claude uses to decide whether to call a tool.

Five Elements of a Good Description

A good tool description should include:

  1. Core function: What the tool does
  2. Usage scenarios: When it should be used
  3. Output format: What data it returns
  4. Limitations: When it should NOT be used
  5. Differentiation from similar tools: If multiple related tools exist
tool_with_optimized_description = {
    "name": "search_customer_database",
    "description": """Search customer records in the CRM system.

[When to use]
- Need to find a specific customer's contact info, purchase history, or account status
- Need to filter customer groups by criteria (region, spend, registration date)

[Return data]
Returns a list of matching customer records. Each record contains:
customer_id, name, email, phone, registration_date, total_purchases, status

[Important limitations]
- Returns at most 100 records; use the limit parameter to control output size
- Does not include deleted accounts; use the search_deleted_customers tool for those
- Results are queried in real time, not cached

[Difference from search_orders]
This tool searches customer information; search_orders searches order information.
If both are needed, first use this tool to get customer_id, then pass it to search_orders.""",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {"type": "string"},
            "limit": {"type": "integer", "default": 20}
        },
        "required": ["query"]
    }
}

Using Examples to Strengthen Descriptions

Including concrete examples in descriptions helps Claude understand parameter formats more precisely:

{
    "name": "parse_date_range",
    "description": """Parse a user-described date range and return start/end dates in standard format.

[Examples for date_expression parameter]
- "last week" โ†’ {"start": "2026-04-21", "end": "2026-04-28"}
- "previous month" โ†’ {"start": "2026-03-01", "end": "2026-03-31"}
- "Q1 2026" โ†’ {"start": "2026-01-01", "end": "2026-03-31"}
- "this quarter" โ†’ {"start": "2026-04-01", "end": "2026-06-30"}""",
    "input_schema": {
        "type": "object",
        "properties": {
            "date_expression": {
                "type": "string",
                "description": "Natural language date description from the user"
            },
            "reference_date": {
                "type": "string",
                "description": "Reference date in ISO 8601 format, used to resolve relative dates like 'yesterday'. Defaults to today."
            }
        },
        "required": ["date_expression"]
    }
}

19.4 Advanced Constraint Patterns in Parameter Schemas

Basic Type and Format Constraints

advanced_schema_examples = {
    "type": "object",
    "properties": {
        # String format constraints
        "email": {
            "type": "string",
            "format": "email",
            "description": "User email address"
        },
        "date": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
            "description": "Date in YYYY-MM-DD format"
        },
        "phone": {
            "type": "string",
            "pattern": "^\\+?[1-9]\\d{6,14}$",
            "description": "Phone number in international format"
        },
        
        # Numeric range constraints
        "page_size": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 20,
            "description": "Pagination size"
        },
        "discount_rate": {
            "type": "number",
            "minimum": 0.0,
            "maximum": 1.0,
            "description": "Discount rate: 0.1 means 10% off"
        },
        
        # Enum constraints
        "order_status": {
            "type": "string",
            "enum": ["pending", "processing", "shipped", "delivered", "cancelled"],
            "description": "Order status"
        },
        
        # Array constraints
        "tags": {
            "type": "array",
            "items": {"type": "string"},
            "minItems": 1,
            "maxItems": 10,
            "uniqueItems": True,
            "description": "Tag list: 1-10 unique tags"
        },
        
        # String length constraints
        "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Title, no more than 200 characters"
        }
    }
}

Nested Object Schemas

nested_schema = {
    "name": "create_shipping_order",
    "description": "Create a shipping order",
    "input_schema": {
        "type": "object",
        "properties": {
            "sender": {
                "type": "object",
                "description": "Sender information",
                "properties": {
                    "name": {"type": "string"},
                    "phone": {"type": "string"},
                    "address": {
                        "type": "object",
                        "properties": {
                            "city": {"type": "string"},
                            "state": {"type": "string"},
                            "street": {"type": "string"},
                            "postal_code": {"type": "string", "pattern": "^\\d{5}(-\\d{4})?$"}
                        },
                        "required": ["city", "state", "street"]
                    }
                },
                "required": ["name", "phone", "address"]
            },
            "recipient": {
                "type": "object",
                "description": "Recipient information (same structure as sender)",
                "properties": {
                    "name": {"type": "string"},
                    "phone": {"type": "string"},
                    "address": {
                        "type": "object",
                        "properties": {
                            "city": {"type": "string"},
                            "state": {"type": "string"},
                            "street": {"type": "string"},
                            "postal_code": {"type": "string"}
                        },
                        "required": ["city", "state", "street"]
                    }
                },
                "required": ["name", "phone", "address"]
            },
            "package": {
                "type": "object",
                "properties": {
                    "weight_kg": {"type": "number", "minimum": 0.1, "maximum": 50},
                    "dimensions": {
                        "type": "object",
                        "properties": {
                            "length_cm": {"type": "number"},
                            "width_cm": {"type": "number"},
                            "height_cm": {"type": "number"}
                        }
                    },
                    "declared_value": {"type": "number", "minimum": 0}
                },
                "required": ["weight_kg"]
            }
        },
        "required": ["sender", "recipient", "package"]
    }
}

Conditional Schemas with if/then

For complex scenarios where parameter format depends on other parameters:

conditional_schema = {
    "name": "send_message",
    "description": "Send a message through different channels",
    "input_schema": {
        "type": "object",
        "properties": {
            "channel": {
                "type": "string",
                "enum": ["email", "sms", "push"],
                "description": "Delivery channel"
            },
            "recipient": {
                "type": "string",
                "description": "Recipient: email address for email, phone number for SMS, user ID for push"
            },
            "content": {
                "type": "string",
                "description": "Message content. SMS max 160 chars; email supports HTML."
            },
            "subject": {
                "type": "string",
                "description": "Email subject (required only when channel=email)"
            }
        },
        "required": ["channel", "recipient", "content"],
        "if": {
            "properties": {"channel": {"const": "email"}}
        },
        "then": {
            "required": ["subject"]
        }
    }
}

19.5 Tips for Optimizing Parameter Descriptions

Common Mistakes in Parameter Descriptions

# Poor: description too simple
bad_parameter = {
    "query": {
        "type": "string",
        "description": "Query string"  # Too vague; Claude doesn't know format requirements
    }
}

# Good: specific and complete description
good_parameter = {
    "query": {
        "type": "string",
        "description": """Search keywords. Supports the following syntax:
- Plain keywords: enter directly, e.g. "iPhone 14"
- Exact match: wrap in quotes, e.g. '"apple phone"'
- Exclude terms: use minus prefix, e.g. "phone -used"
- Field search: use colon separator, e.g. "brand:apple"
Maximum 500 characters.""",
        "maxLength": 500
    }
}

Using Descriptions to Guide Parameter Selection

smart_defaults_tool = {
    "name": "export_report",
    "description": "Export a data report",
    "input_schema": {
        "type": "object",
        "properties": {
            "format": {
                "type": "string",
                "enum": ["pdf", "excel", "csv", "json"],
                "description": "Export format. Default to pdf (best for viewing) when unspecified. Choose csv or excel if the user says they want to 'process data' or 'import into a system'.",
                "default": "pdf"
            },
            "date_range": {
                "type": "string",
                "enum": ["today", "week", "month", "quarter", "year", "custom"],
                "description": "Time range. Default to month (last 30 days) when unspecified.",
                "default": "month"
            }
        },
        "required": ["format"]
    }
}

19.6 Tool Group Design Patterns

Pattern 1: CRUD Tool Group

CRUD operations on the same resource should maintain consistent naming and parameter styles:

user_crud_tools = [
    {
        "name": "get_user",
        "description": "Get full information for a single user by ID",
        "input_schema": {
            "type": "object",
            "properties": {
                "user_id": {"type": "string", "description": "User ID"}
            },
            "required": ["user_id"]
        }
    },
    {
        "name": "list_users",
        "description": "List users with pagination and filtering support",
        "input_schema": {
            "type": "object",
            "properties": {
                "page": {"type": "integer", "minimum": 1, "default": 1},
                "page_size": {"type": "integer", "minimum": 1, "maximum": 100, "default": 20},
                "status": {"type": "string", "enum": ["active", "inactive", "all"], "default": "active"},
                "search": {"type": "string", "description": "Search by name or email"}
            }
        }
    },
    {
        "name": "create_user",
        "description": "Create a new user account",
        "input_schema": {
            "type": "object",
            "properties": {
                "name": {"type": "string", "minLength": 1, "maxLength": 100},
                "email": {"type": "string", "format": "email"},
                "role": {"type": "string", "enum": ["admin", "editor", "viewer"], "default": "viewer"},
                "send_welcome_email": {"type": "boolean", "default": True}
            },
            "required": ["name", "email"]
        }
    },
    {
        "name": "update_user",
        "description": "Update user information. Only pass the fields that need to be changed.",
        "input_schema": {
            "type": "object",
            "properties": {
                "user_id": {"type": "string", "description": "ID of the user to update"},
                "name": {"type": "string", "minLength": 1, "maxLength": 100},
                "email": {"type": "string", "format": "email"},
                "role": {"type": "string", "enum": ["admin", "editor", "viewer"]},
                "status": {"type": "string", "enum": ["active", "inactive"]}
            },
            "required": ["user_id"]
        }
    }
]

Pattern 2: Pipeline Tool Group

Tools with a clear dependency ordering:

pipeline_tools = [
    {
        "name": "upload_file",
        "description": "Upload a file; returns a file ID. This is the first step in file processing.",
        "input_schema": {
            "type": "object",
            "properties": {
                "file_path": {"type": "string", "description": "Local file path"},
                "file_type": {"type": "string", "enum": ["csv", "excel", "json", "xml"]}
            },
            "required": ["file_path", "file_type"]
        }
    },
    {
        "name": "validate_file",
        "description": "Validate the format and data integrity of an uploaded file. Requires a file_id from upload_file.",
        "input_schema": {
            "type": "object",
            "properties": {
                "file_id": {"type": "string", "description": "File ID returned by upload_file"},
                "schema_id": {"type": "string", "description": "Schema ID for validation (optional)"}
            },
            "required": ["file_id"]
        }
    },
    {
        "name": "import_file",
        "description": "Import a validated file into the database. Must pass validate_file first.",
        "input_schema": {
            "type": "object",
            "properties": {
                "file_id": {"type": "string"},
                "target_table": {"type": "string"},
                "conflict_strategy": {
                    "type": "string",
                    "enum": ["skip", "overwrite", "append"],
                    "default": "skip"
                }
            },
            "required": ["file_id", "target_table"]
        }
    }
]

19.7 Using Tools for Structured Output

An important use case of Tool Use is forcing Claude to produce structured data rather than free-form text:

extraction_tool = {
    "name": "extract_invoice_data",
    "description": "Extract structured data from invoice text",
    "input_schema": {
        "type": "object",
        "properties": {
            "invoice_number": {"type": "string"},
            "invoice_date": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$"},
            "vendor_name": {"type": "string"},
            "total_amount": {"type": "number"},
            "tax_amount": {"type": "number"},
            "line_items": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "description": {"type": "string"},
                        "quantity": {"type": "number"},
                        "unit_price": {"type": "number"},
                        "total": {"type": "number"}
                    },
                    "required": ["description", "quantity", "unit_price", "total"]
                }
            }
        },
        "required": ["invoice_number", "invoice_date", "vendor_name", "total_amount"]
    }
}

def extract_invoice(invoice_text: str) -> dict:
    """Extract structured data from invoice text"""
    import anthropic
    client = anthropic.Anthropic()
    
    response = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=1024,
        tools=[extraction_tool],
        tool_choice={"type": "tool", "name": "extract_invoice_data"},
        messages=[{
            "role": "user",
            "content": f"Please extract data from this invoice:\n\n{invoice_text}"
        }]
    )
    
    for block in response.content:
        if block.type == "tool_use":
            return block.input
    
    raise ValueError("Failed to extract invoice data")

19.8 Tool Versioning and Backward Compatibility

As systems evolve, tool Schemas need to be updated. Here is a strategy for maintaining backward compatibility:

# Version 1: only query parameter
tool_v1 = {
    "name": "search_products",
    "description": "Search products (v1)",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {"type": "string"}
        },
        "required": ["query"]
    }
}

# Version 2: add new parameters; query remains required, new params have defaults
tool_v2 = {
    "name": "search_products",
    "description": "Search products with category filtering and price range (v2)",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {"type": "string"},
            "category": {
                "type": "string",
                "description": "Product category (optional; omit to search all categories)"
            },
            "min_price": {
                "type": "number",
                "description": "Minimum price filter (optional)"
            },
            "max_price": {
                "type": "number",
                "description": "Maximum price filter (optional)"
            }
        },
        "required": ["query"]  # Only original required parameters
    }
}

Summary

High-quality tool design is the foundation of a successful Tool Use system. The core principles are:

The next chapter explores how to have Claude call multiple tools simultaneously, achieving an efficient parallel processing architecture.

Rate this chapter
4.6  / 5  (16 ratings)

๐Ÿ’ฌ Comments