How to Build an AI Agent with Claude: A Practical Guide
Building an AI agent with Claude means giving the model a loop: it receives a task, decides whether to call a tool, executes that tool, reads the result, and repeats until the task is done. This is different from a simple chatbot, which just takes a prompt and returns a reply. An agent needs state, tool definitions, and a control loop that keeps calling Claude until it produces a final answer instead of another tool request.
This guide walks through the actual architecture: how to structure the loop, define tools, manage context, and handle errors, with runnable code. It applies whether you're calling Anthropic's API directly or going through a proxy like SubToAPI.
The Core Agent Loop
At minimum, an agent needs four things:
- A system prompt that defines the agent's role and constraints
- A set of tool definitions (functions the model can call)
- A loop that sends messages, checks for tool calls, executes them, and sends results back
- A stopping condition — either the model returns a final text answer, or you hit a max iteration count
Here's a minimal loop in JavaScript:
async function runAgent(userMessage, tools, toolHandlers) {
const messages = [{ role: "user", content: userMessage }];
const maxSteps = 8;
for (let step = 0; step < maxSteps; step++) {
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
system: "You are a research agent. Use tools to gather facts before answering.",
messages,
tools,
}),
});
const data = await response.json();
messages.push({ role: "assistant", content: data.content });
const toolUse = data.content.find((block) => block.type === "tool_use");
if (!toolUse) {
return data.content.find((block) => block.type === "text")?.text;
}
const result = await toolHandlers[toolUse.name](toolUse.input);
messages.push({
role: "user",
content: [
{ type: "tool_result", tool_use_id: toolUse.id, content: JSON.stringify(result) },
],
});
}
return "Max steps reached without a final answer.";
}
This is the whole pattern. Everything else — better error handling, parallel tool calls, memory, guardrails — builds on top of this loop.
Defining Tools Correctly
Claude decides which tool to call based on the tool's name, description, and JSON schema. Vague descriptions produce vague tool selection. Be explicit about what the tool does and when it should be used:
const tools = [
{
name: "search_docs",
description:
"Search internal documentation for a given query. Use this when the user asks about product features, pricing, or setup instructions. Do not use for general knowledge questions.",
input_schema: {
type: "object",
properties: {
query: { type: "string", description: "The search query" },
limit: { type: "integer", description: "Max results to return", default: 5 },
},
required: ["query"],
},
},
];
A common mistake is giving the agent too many overlapping tools. If two tools could plausibly handle the same request, Claude will sometimes pick the wrong one or bounce between them. Keep tool boundaries clear, and if you need many capabilities, group them into a smaller number of well-scoped tools rather than one tool per API endpoint.
Managing Context and Memory
Agents that run multi-step tasks accumulate a lot of message history — tool calls, tool results, intermediate reasoning. Two things matter here:
- Trim aggressively. Tool results (especially raw API responses or file contents) can be large. Summarize or truncate before appending them to the message history, or you'll hit context limits fast on longer runs.
- Separate short-term and long-term memory. The message array is short-term memory for the current task. For anything that needs to persist across sessions (user preferences, prior decisions), store it outside the conversation — a database row the agent can query via a tool — rather than trying to keep it in context indefinitely.
A practical pattern: after every few tool calls, have the agent (or a cheaper model) summarize progress so far and replace the raw tool results with the summary. This keeps token usage predictable on long-running agents.
Error Handling and Guardrails
Real agents fail in predictable ways: a tool throws an error, the model calls a tool with malformed input, or the loop never terminates because the model keeps requesting more information. Handle these explicitly:
- Catch tool execution errors and feed them back to Claude as a tool result rather than crashing the loop — the model can often recover by trying a different approach.
- Validate tool input against your schema before execution, especially for tools that touch a database or external API.
- Cap iterations with a hard
maxStepslimit, and consider a token budget cap alongside it, since a loop can run for many cheap steps and still be expensive in aggregate. - Log every step (tool called, input, output) so you can debug why an agent took a particular path.
Choosing a Model for Agent Work
Tool selection and multi-step reasoning benefit from a stronger model. Haiku is fast and cheap but can struggle with complex tool chains; Sonnet is the usual default for agents that need to reason across several steps; Opus is worth it for agents making high-stakes decisions where mistakes are costly. Many production agents mix models: Sonnet or Opus for planning and tool selection, Haiku for cheap sub-tasks like summarization.
Deployment Considerations
Once the agent logic works locally, you need a stable way to call the API in production: key management, usage tracking per user or per agent, and streaming for long-running responses so the frontend isn't waiting on a single blocking call. If you're building this for a team or a product with multiple users, SubToAPI gives you an HTTPS endpoint with application API keys, streaming, and usage metadata per key, so you can run agents for different customers or team members without building that infrastructure yourself. See the quickstart for setup and the tool use docs for the exact request format.
FAQ
Do I need a framework like LangChain to build a Claude agent?
No. The core loop is maybe 40 lines of code, as shown above. Frameworks add value for complex multi-agent orchestration, but for a single agent with a handful of tools, a plain loop is easier to debug and modify.
How do I stop an agent from looping forever?
Set a hard maximum on iterations (typically 5–15 depending on task complexity) and optionally a token budget. If the loop hits the limit without a final answer, return the best partial result along with a clear message that the task wasn't completed.
Can an agent call multiple tools at once?
Yes — Claude can return multiple tool_use blocks in a single response. Execute them in parallel where they don't depend on each other's output, then send all results back in the same tool_result batch before the next model call.