Skip to main content
Claude’s tool use lets a model call functions you define. This guide shows how to wire Ceramic Search as a client tool, so Claude can retrieve real-time web results when generating a response.

Get your Ceramic API key

Create a free account to get started.

Generating model responses

When Claude needs to search, it returns a tool_use block with stop_reason: "tool_use". You execute the search, append the result as a tool_result in a user message, and call the API again for the final answer.

Set environment variables

export CERAMIC_API_KEY=your_ceramic_api_key
export ANTHROPIC_API_KEY=your_anthropic_api_key

Install dependencies

pip install anthropic ceramic_ai

Full example

import os
import anthropic
from ceramic_ai import Ceramic

claude = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
ceramic = Ceramic(api_key=os.getenv("CERAMIC_API_KEY"))

TOOL_DESCRIPTION = (
    "Search the web using Ceramic.\n"
    "Use for accurate current information — news, prices, recent events, documentation, general fact checking.\n"
    "Returns up to 10 ranked results with titles, URLs, and descriptions.\n"
    "Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.\n"
    "Query rules:\n"
    "- Queries must be 2-8 words\n"
    "- Include specific entities, topics, locations, and dates\n"
    "- Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).\n"
    "- Keep word order meaningful (`house cat` and `cat house` return different results)\n"
    "- Good keyword query examples:\n"
    "    - \"2026 Super Bowl halftime performer\"\n"
    "    - \"climate change effects global warming impact\"\n"
    "    - \"beginner investing strategies stocks bonds basics\"\n"
    "If the search returns no useful results, retry with a more specific keyword query."
)

ceramic_search_tool = {
    "name": "ceramic_search",
    "description": TOOL_DESCRIPTION,
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "keyword search query with 2–8 words",
            }
        },
        "required": ["query"],
    },
}

SYSTEM = "You have access to a web search tool. Use it to answer questions with up-to-date information."

messages = [{"role": "user", "content": "What are the latest California tenant protection laws?"}]

response = claude.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    system=SYSTEM,
    messages=messages,
    tools=[ceramic_search_tool],
)

while response.stop_reason == "tool_use":
    tool_use_blocks = [block for block in response.content if block.type == "tool_use"]

    # Append the full assistant message
    messages.append({"role": "assistant", "content": response.content})

    # Execute each tool call and collect results
    tool_results = []
    for tool_use in tool_use_blocks:
        if tool_use.name == "ceramic_search":
            results = ceramic.search(query=tool_use.input["query"])
            tool_results.append({
                "type": "tool_result",
                "tool_use_id": tool_use.id,
                "content": str(results),
            })

    messages.append({"role": "user", "content": tool_results})

    response = claude.messages.create(
        model="claude-opus-4-6",
        max_tokens=1024,
        system=SYSTEM,
        messages=messages,
        tools=[ceramic_search_tool],
    )

print(next((block.text for block in response.content if block.type == "text"), ""))

Run the example

python anthropic_tool_calling.py

Building agents

The Tool Runner handles the tool-calling loop automatically. Define ceramic_search with the @beta_tool decorator (Python) or betaZodTool (TypeScript) and the SDK executes it and continues the conversation until Claude returns a final answer.

Set environment variables

export CERAMIC_API_KEY=your_ceramic_api_key
export ANTHROPIC_API_KEY=your_anthropic_api_key

Install dependencies

pip install anthropic ceramic_ai

Full example

import os
import anthropic
from anthropic import beta_tool
from ceramic_ai import Ceramic

claude = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
ceramic = Ceramic(api_key=os.getenv("CERAMIC_API_KEY"))

@beta_tool
def ceramic_search(query: str) -> str:
    """Search the web using Ceramic.
    Use for accurate current information — news, prices, recent events, documentation, general fact checking.
    Returns up to 10 ranked results with titles, URLs, and descriptions.
    Ceramic matches exact keywords — it does not interpret natural language or synonyms automatically.
    Query rules:
    - Queries must be 2-8 words
    - Include specific entities, topics, locations, and dates
    - Do not include uninformative words such as articles (the, a, an). Avoid prepositions (on, about, in, for, of, at, by, with) unless they are within established phrases or names (United States of America, Into the Wild).
    - Keep word order meaningful (`house cat` and `cat house` return different results)
    - Good keyword query examples:
        - "2026 Super Bowl halftime performer"
        - "climate change effects global warming impact"
        - "beginner investing strategies stocks bonds basics"
    If the search returns no useful results, retry with a more specific keyword query.

    Args:
        query: keyword search query with 2–8 words
    """
    results = ceramic.search(query=query)
    return str(results)

runner = claude.beta.messages.tool_runner(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=[ceramic_search],
    messages=[{"role": "user", "content": "What are the latest California tenant protection laws?"}],
)
final_message = runner.until_done()
for block in final_message.content:
    if block.type == "text":
        print(block.text)

Run the example

python tool_runner.py