How to Build an AI Agent with Tool Calling
Building an AI agent with tool calling means giving a language model access to functions it can invoke — search, database lookups, calculators, API calls — and letting it decide when and how to use them to complete a task. The model doesn't execute code itself; it outputs a structured request describing which tool to call and with what arguments, your application runs that function, and you feed the result back so the model can continue reasoning or respond to the user.
This article walks through the actual mechanics: how tool calling works under the hood, how to structure the agent loop, common failure modes, and how to get it running quickly using an API.
What "Tool Calling" Actually Means
When you send a request to a modern LLM API, you can include a list of tool definitions — each one a name, a description, and a JSON schema for its parameters. The model reads the conversation, decides whether a tool is needed, and if so, returns a structured object instead of (or alongside) plain text, specifying the tool name and arguments.
Your code is responsible for:
- Parsing that tool call request
- Executing the actual function (hitting a database, calling a weather API, running a calculation)
- Sending the result back to the model as a new message
- Letting the model decide the next step — call another tool, or respond to the user
This request/execute/respond cycle is the entire mechanism behind "agents." There's no magic — it's a loop with structured JSON in the middle.
Step 1: Define Your Tools with Clear Schemas
The single biggest factor in reliable tool calling is schema quality. Vague descriptions produce vague (or wrong) tool calls.
{
"name": "get_order_status",
"description": "Look up the current status of a customer order by order ID. Returns shipping status, estimated delivery, and tracking number.",
"input_schema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID, formatted like ORD-12345"
}
},
"required": ["order_id"]
}
}
Keep descriptions specific about what the tool does, what it returns, and any constraints (formats, ranges, units). Models rely entirely on this text to decide when to call the tool — there's no other signal.
Step 2: Build the Agent Loop
A minimal agent loop looks like this in JavaScript:
async function runAgent(userMessage, tools) {
let messages = [{ role: "user", content: userMessage }];
while (true) {
const response = await callModel(messages, tools);
if (response.stop_reason === "tool_use") {
const toolCall = response.content.find(c => c.type === "tool_use");
const result = await executeTool(toolCall.name, toolCall.input);
messages.push({ role: "assistant", content: response.content });
messages.push({
role: "user",
content: [{
type: "tool_result",
tool_use_id: toolCall.id,
content: JSON.stringify(result)
}]
});
continue;
}
return response.content;
}
}
The loop keeps running until the model stops requesting tools and returns a final text answer. Most agents cap this at some maximum number of iterations (5–10 is typical) to avoid infinite loops from a misbehaving prompt or flaky tool.
Step 3: Handle Errors Inside the Loop, Not Around It
A common mistake is treating tool execution failures as application-level errors that crash the loop. Instead, feed the error back to the model as a tool result:
try {
const result = await executeTool(toolCall.name, toolCall.input);
return { tool_use_id: toolCall.id, content: JSON.stringify(result) };
} catch (err) {
return {
tool_use_id: toolCall.id,
content: JSON.stringify({ error: err.message }),
is_error: true
};
}
Models are generally good at recovering — retrying with different arguments, trying a different tool, or explaining to the user why something failed — as long as they actually see the error instead of the conversation just breaking.
Step 4: Limit What Tools Can Do
Tool calling gives the model real-world side effects. Before wiring up anything that writes data, sends emails, or spends money, add guardrails:
- Validate arguments server-side even if the schema looks right — models occasionally hallucinate values.
- Require confirmation for destructive actions (deletes, payments, sends) rather than letting the model execute them silently.
- Scope credentials — the function executing the tool should only have the permissions it actually needs.
- Log every tool call with its arguments and result for debugging and auditing.
Step 5: Pick the Right Model API
Tool calling quality varies significantly between models — some are much better at picking the correct tool, filling in schemas correctly, and knowing when not to call a tool. If your agent depends on Claude's tool-use behavior specifically, you need a way to call it from your backend or app without dealing with raw provider credentials, billing tiers, or key rotation yourself.
This is where SubToAPI fits: it turns your existing Claude access into a standard HTTPS API with application-scoped keys (sub_live_...), so each service or team member gets its own key instead of sharing one credential. Tool use, streaming, and usage metadata all work through the same interface — see the tool use docs for the exact request format, or the quickstart to get a key running in a few minutes.
A basic call with tools defined looks like this:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 1024,
"tools": [{
"name": "get_order_status",
"description": "Look up order status by order ID",
"input_schema": {
"type": "object",
"properties": { "order_id": { "type": "string" } },
"required": ["order_id"]
}
}],
"messages": [{ "role": "user", "content": "Where is order ORD-12345?" }]
}'
For teams building multiple agents, seat-based plans (Solo €9, Team €19/seat, Scale €49/seat) mean you don't need to renegotiate access every time someone new joins the project — see pricing for details, and there's a free trial at signup.
Step 6: Test with Adversarial Inputs
Before shipping, test your agent against:
- Ambiguous requests where multiple tools could apply
- Missing required information (does it ask, or guess?)
- Tool failures (timeouts, empty results, malformed data)
- Multi-step tasks requiring 3+ sequential tool calls
Agents that work well on the happy path often break down on these edge cases, and that's where most production bugs live.
Questions
Do I need a special "agent framework" to build tool calling? No. Tool calling is a request/response loop you can build with plain code, as shown above. Frameworks add convenience for complex multi-agent orchestration, but a single well-scoped agent rarely needs one.
How many tools can I give a model at once? Technically dozens, but accuracy drops as the list grows and tools become similar. Keep it under 10–15 well-differentiated tools per agent; split broader tasks into separate agents if needed.
Can the model call multiple tools in one turn? Some models support returning several tool calls in a single response, which you then execute (in parallel or sequence) before responding. Check the specific API's messages docs for supported behavior.