Published on

Building Tool Functions for Claude: Extending AI Capabilities

Authors
  • avatar
    Name
    Anablock
    Twitter

    AI Insights & Innovations

Claude Code

Building Tool Functions for Claude: Extending AI Capabilities

When building AI applications with Claude, you'll often need to give it access to real-time information or the ability to perform actions. This is where tool functions come in - they're Python functions that Claude can call when it needs additional data to help users.

What Are Tool Functions?

A tool function is a plain Python function that gets executed automatically when Claude decides it needs extra information to help a user. For example, if someone asks "What time is it?", Claude would call your date/time tool to get the current time.

Here's an example of a weather tool function:

def get_weather(location):
    if not location:
        raise ValueError("Location cannot be empty")
    # Fetch weather data for location
    return weather_data

Notice how it validates inputs and provides clear error messages - these are important best practices.

Best Practices for Tool Functions

When writing tool functions, follow these guidelines:

  • Use descriptive names: Both your function name and parameter names should clearly indicate their purpose
  • Validate inputs: Check that required parameters aren't empty or invalid, and raise errors when they are
  • Provide meaningful error messages: Claude can see error messages and might retry the function call with corrected parameters

The validation is particularly important because Claude learns from errors. If you raise a clear error like "Location cannot be empty", Claude might try calling the function again with a proper location value.

Building Your First Tool Function

Let's create a function to get the current date and time. This function will accept a date format parameter so Claude can request the time in different formats:

def get_current_datetime(date_format="%Y-%m-%d %H:%M:%S"):
    if not date_format:
        raise ValueError("date_format cannot be empty")
    return datetime.now().strftime(date_format)

This function uses Python's datetime module to get the current time and format it according to the provided format string. The default format gives us year-month-day hour:minute:second.

You can test it with different formats:

# Default format: "2024-01-15 14:30:25"
get_current_datetime()

# Just hour and minute: "14:30"
get_current_datetime("%H:%M")

The validation check ensures Claude can't pass an empty string for the date format. While this specific error is unlikely, it demonstrates the pattern of validating inputs and providing helpful error messages that Claude can learn from.

Tool Schemas

After writing your tool function, the next step is creating a JSON schema that tells Claude what arguments your function expects and how to use it. This schema acts as documentation that Claude reads to understand when and how to call your tools.

Understanding JSON Schema

JSON Schema isn't specific to AI or tool calling - it's a widely-used data validation specification that's been around for years. The AI community adopted it because it's a convenient way to describe function parameters and validate data.

The complete tool specification has three main parts:

  • name - A clear, descriptive name for your tool (like "get_weather")
  • description - What the tool does, when to use it, and what it returns
  • input_schema - The actual JSON schema describing the function's arguments

Writing Effective Descriptions

Your tool description is crucial for helping Claude understand when to use your function. Best practices include:

  • Aim for 3-4 sentences explaining what the tool does
  • Describe when Claude should use it
  • Explain what kind of data it returns
  • Provide detailed descriptions for each argument

The Easy Way to Generate Schemas

Instead of writing JSON schemas from scratch, you can use Claude itself to generate them. Here's the process:

  1. Copy your tool function code
  2. Go to Claude and ask it to write a JSON schema for tool calling
  3. Include the Anthropic documentation on tool use as context
  4. Let Claude generate a properly formatted schema following best practices

The prompt should be something like: "Write a valid JSON schema spec for the purposes of tool calling for this function. Follow the best practices listed in the attached documentation."

Implementing the Schema in Code

Once Claude generates your schema, copy it into your code file. Here's a good naming pattern to follow:

def get_current_datetime(date_format="%Y-%m-%d %H:%M:%S"):
    if not date_format:
        raise ValueError("date_format cannot be empty")
    return datetime.now().strftime(date_format)

get_current_datetime_schema = {
    "name": "get_current_datetime",
    "description": "Returns the current date and time formatted according to the specified format",
    "input_schema": {
        "type": "object",
        "properties": {
            "date_format": {
                "type": "string",
                "description": "A string specifying the format of the returned datetime. Uses Python's strftime format codes.",
                "default": "%Y-%m-%d %H:%M:%S"
            }
        },
        "required": []
    }
}

Use the pattern of function_name followed by function_name_schema to keep your schemas organized and easy to match with their corresponding functions.

Adding Type Safety

For better type checking, import and use the ToolParam type from the Anthropic library:

from anthropic.types import ToolParam

get_current_datetime_schema = ToolParam({
    "name": "get_current_datetime",
    "description": "Returns the current date and time formatted according to the specified format",
    # ... rest of schema
})

While not strictly necessary for functionality, this prevents type errors when you use the schema with Claude's API and makes your code more robust.

Handling Message Blocks

When working with Claude's tool functionality, you'll encounter a new type of response structure that's different from the simple text responses you've seen before. Instead of just getting back a single text block, Claude can now return multi-block messages that contain both text and tool usage information.

Making Tool-Enabled API Calls

To enable Claude to use tools, you need to include a tools parameter in your API call. Here's how to structure the request:

messages = []
messages.append({
    "role": "user",
    "content": "What is the exact time, formatted as HH:MM:SS?"
})

response = client.messages.create(
    model=model,
    max_tokens=1000,
    messages=messages,
    tools=[get_current_datetime_schema],
)

The tools parameter takes a list of JSON schemas that describe the available functions Claude can call.

Understanding Multi-Block Messages

When Claude decides to use a tool, it returns an assistant message with multiple blocks in the content list. This is a significant change from the simple text-only responses you've worked with before.

A multi-block message typically contains:

  • Text Block - Human-readable text explaining what Claude is doing (like "I can help you find out the current time. Let me find that information for you")
  • ToolUse Block - Instructions for your code about which tool to call and what parameters to use

The ToolUse block includes:

  • An ID for tracking the tool call
  • The name of the function to call (like "get_current_datetime")
  • Input parameters formatted as a dictionary
  • The type designation "tool_use"

Managing Conversation History with Multi-Block Messages

Remember that Claude doesn't store conversation history - you need to manage it manually. When working with tool responses, you must preserve the entire content structure, including all blocks.

Here's how to properly append a multi-block assistant message to your conversation history:

messages.append({
    "role": "assistant",
    "content": response.content
})

This preserves both the text block and the tool use block, which is crucial for maintaining the conversation context when you make subsequent API calls.

The Complete Tool Usage Flow

The tool usage process follows this pattern:

  1. Send user message with tool schema to Claude
  2. Receive assistant message with text block and tool use block
  3. Extract tool information and execute the actual function
  4. Send tool result back to Claude along with complete conversation history
  5. Receive final response from Claude

Each step requires careful handling of the message structure to ensure Claude has the full context it needs to provide accurate responses.

Updating Helper Functions

If you've been using helper functions like add_user_message() and add_assistant_message(), you'll need to update them to handle multi-block content. The current versions likely only support single text blocks, but now they need to accommodate the more complex content structures that include tool use blocks.

This multi-block message handling is essential for building robust applications that can seamlessly integrate Claude's tool capabilities while maintaining proper conversation flow.

Sending Tool Results

After Claude requests a tool call, you need to execute the function and send the results back. This completes the tool use workflow by providing Claude with the information it requested.

Running the Tool Function

When Claude responds with a tool use block, you extract the input parameters and call your function. Here's how to access the tool parameters:

response.content[1].input

This gives you a dictionary of the arguments Claude wants to pass to your function. Since your function expects keyword arguments rather than a dictionary, you use Python's unpacking syntax:

get_current_datetime(**response.content[1].input)

Tool Result Block

After running the tool function, you need to send the results back to Claude using a tool result block. This block goes inside a user message and tells Claude what happened when you executed the tool.

The tool result block has several important properties:

  • tool_use_id - Must match the id of the ToolUse block that this ToolResult corresponds to
  • content - Output from running your tool, serialized as a string
  • is_error - True if an error occurred

Handling Multiple Tool Calls

Claude can request multiple tool calls in a single response. For example, if a user asks "What's 10 + 10 and what's 30 + 30?", Claude might respond with two separate ToolUse blocks.

Each tool call gets a unique ID, and you must match these IDs when sending back results. This ensures Claude knows which result corresponds to which request, even if the results arrive in a different order.

Building the Follow-up Request

Your follow-up request to Claude must include the complete conversation history plus the new tool result. Here's the structure:

messages.append({
    "role": "user",
    "content": [{
        "type": "tool_result",
        "tool_use_id": response.content[1].id,
        "content": "15:04:22",
        "is_error": False
    }]
})

The complete message history now contains:

  1. Original user message
  2. Assistant message with tool use block
  3. User message with tool result block

Making the Final Request

When sending the follow-up request, you must still include the tool schema even though you're not expecting Claude to make another tool call. Claude needs the schema to understand the tool references in your conversation history.

client.messages.create(
    model=model,
    max_tokens=1000,
    messages=messages,
    tools=[get_current_datetime_schema]
)

Claude will then respond with a final message that incorporates the tool results into a natural response for the user. The tool use workflow is now complete - you've successfully enabled Claude to access real-time information through your custom function.

Common Tool Function Examples

Here are some essential tools you might implement:

  • Getting current date/time - For time-sensitive queries
  • Adding duration to dates - For scheduling and planning
  • Setting reminders - For task management
  • Fetching weather data - For location-based information
  • Database queries - For retrieving user-specific data
  • API calls - For integrating external services

Tool functions transform Claude from a conversational AI into a powerful assistant that can interact with real-world systems and data.