How to Build an AI Agent with Claude Tools
An AI agent built with Claude tools is a program that lets the model decide when to call external functions — a database query, a web search, a calculator, an internal API — and then uses the results to continue reasoning toward a final answer. You're not writing a rigid if/else chatbot; you're giving Claude a set of capabilities and letting it pick which ones to use, in what order, based on the user's request.
This guide walks through the actual mechanics: defining tools, handling the responses Claude sends back, running the agent loop until a task is done, and the production details (retries, streaming, auth) that separate a demo from something you'd put in front of real users.
What "Tool Use" Actually Means
Claude's tool use (sometimes called function calling) works like this:
- You send a request with a
toolsarray describing each function: its name, description, and JSON Schema for inputs. - Claude decides whether answering the user requires a tool. If so, it returns a
tool_usecontent block instead of (or alongside) text. - Your code executes that function with the arguments Claude provided.
- You send the result back to Claude as a
tool_resultblock in the next message. - Claude reads the result and either calls another tool or returns a final answer.
That loop — request, tool call, execute, return result, repeat — is the entire architecture of a Claude-based agent. There's no hidden magic; the "agent" behavior comes from looping this cycle until the model stops requesting tools.
Defining a Tool
A tool definition is just a schema. Here's a minimal example for a weather lookup:
{
"name": "get_weather",
"description": "Get the current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}
The description fields matter more than people expect. Claude uses them to decide whether to call the tool at all, so vague descriptions lead to missed or incorrect calls. Be specific about what the function does and what each parameter means.
Running the Agent Loop
Here's a working loop structure in JavaScript. This example uses SubToAPI's endpoint, which mirrors the standard Messages API shape so you can swap in your own key without rewriting logic:
const tools = [
{
name: "get_weather",
description: "Get the current weather for a city",
input_schema: {
type: "object",
properties: {
city: { type: "string" },
unit: { type: "string", enum: ["celsius", "fahrenheit"] }
},
required: ["city"]
}
}
];
async function callClaude(messages) {
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 1024,
tools,
messages
})
});
return res.json();
}
function executeTool(name, input) {
if (name === "get_weather") {
// call your real weather API here
return { temperature: 18, condition: "cloudy" };
}
throw new Error(`Unknown tool: ${name}`);
}
async function runAgent(userMessage) {
let messages = [{ role: "user", content: userMessage }];
while (true) {
const response = await callClaude(messages);
messages.push({ role: "assistant", content: response.content });
const toolUses = response.content.filter(b => b.type === "tool_use");
if (toolUses.length === 0) {
return response.content.find(b => b.type === "text")?.text;
}
const toolResults = toolUses.map(use => ({
type: "tool_result",
tool_use_id: use.id,
content: JSON.stringify(executeTool(use.name, use.input))
}));
messages.push({ role: "user", content: toolResults });
}
}
This loop terminates naturally when Claude stops requesting tools and returns plain text. For agents that chain many steps — research, code execution, file edits — the same pattern holds, just with more tools and more iterations.
Common Pitfalls
Over-describing tools. If every tool sounds like it could handle the request, Claude will guess wrong more often. Keep tool boundaries narrow and names unambiguous.
Forgetting tool_use_id. Every tool_result must reference the exact id from the matching tool_use block. Mismatches cause the API to reject the follow-up request.
No loop limit. Always cap iterations (e.g., 10) to prevent a misbehaving agent from looping indefinitely and burning tokens on a stuck task.
Parallel tool calls. Claude can return multiple tool_use blocks in one response when it needs several pieces of information at once. Execute them concurrently and return all results together in a single tool_result message — don't reply one at a time.
Streaming agents. If you want partial output while tools are still running, combine streaming with the tool loop: stream text deltas as they arrive, but buffer until a full tool_use block completes before executing it. See the streaming docs for the event format.
Taking It to Production
A prototype agent calling the Anthropic API directly is fine for testing. Once you have multiple team members, multiple apps, or a need to track usage per project, a few things get painful fast: rotating a single shared key, seeing which app burned through your quota, and giving teammates access without handing out your root credentials.
This is the gap SubToAPI fills. You keep your existing Claude access, and SubToAPI issues scoped sub_live_... application keys so each agent or project authenticates separately, with its own usage metadata visible in one dashboard. The request/response format matches the Messages API directly, so an agent loop like the one above needs zero changes beyond swapping the base URL and key — check the quickstart and the messages reference for the exact payload shapes, and the tools doc for schema details specific to tool-calling requests.
Plans start at €9/month for solo builders, with team and scale tiers for multi-seat setups — see /pricing for details, or just /signup to try it with a free trial.
questions
Do I need a special framework to build an agent with Claude tools? No. Tool use is a feature of the Messages API itself — you define tools in the request and handle tool_use/tool_result blocks in a loop. Frameworks like LangChain can help with complex orchestration, but a basic agent is just a few dozen lines of code against the raw API.
How many tools can one agent have? There's no hard limit, but more tools means more chance of Claude picking the wrong one. Most reliable agents use somewhere between 3 and 15 well-described tools. If you need more, consider grouping related tools or using a router step first.
Can an agent call tools recursively, like running code and reacting to errors? Yes — this is a standard pattern. The agent loop keeps running as long as Claude returns tool_use blocks, so you can have it execute code, read the error from tool_result, and try a fixed version in the next iteration, as long as you cap total iterations.