← Blog

How to Implement Tool Use in Claude: A Practical Guide

2026-09-15 · 5 min read · SubToAPI Team

Implementing tool use in Claude means giving the model a set of function definitions it can request to call, then writing the code that executes those functions and feeds the results back into the conversation. Claude never runs your code itself — it only decides when and with what arguments a tool should be called, and your application handles the actual execution.

This guide walks through the full loop: defining tools, sending them in a request, parsing the model's response, executing the tool, and returning results so Claude can produce a final answer.

Step 1: Define your tools

Each tool needs a name, a description, and a JSON Schema describing its inputs. The description matters more than most people expect — Claude uses it to decide whether and when to call the tool, so vague descriptions lead to missed or incorrect calls.

{
  "name": "get_weather",
  "description": "Get the current weather for a given city. Use this whenever the user asks about weather, temperature, or forecast conditions.",
  "input_schema": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "City name, e.g. 'Berlin' or 'Tokyo'"
      },
      "unit": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"]
      }
    },
    "required": ["city"]
  }
}

Keep schemas tight. Avoid optional fields Claude doesn't need, and don't reuse one generic tool for five unrelated tasks — the model calls tools more reliably when each one has a clear, single purpose.

Step 2: Send the request with tools

Include the tools array in your request alongside the normal messages payload. If you want Claude to always use a specific tool, you can force it with tool_choice; otherwise leave it to auto.

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 1024,
    "tools": [{ "name": "get_weather", ... }],
    "messages": [
      { "role": "user", "content": "What is the weather in Lisbon?" }
    ]
  }'

If your team is building on top of an existing Claude subscription rather than a raw Anthropic API key, the same request shape works through SubToAPI — you send it to https://api.subtoapi.app/v1/messages with Authorization: Bearer $SUBTOAPI_KEY instead. The tool-calling mechanics are identical; see /docs/tools for the exact request format.

Step 3: Handle the tool_use stop reason

When Claude decides to call a tool, the response's stop_reason will be tool_use, and the content array will include a block like this:

{
  "type": "tool_use",
  "id": "toolu_01A2b3C4d5",
  "name": "get_weather",
  "input": { "city": "Lisbon", "unit": "celsius" }
}

Your code needs to check stop_reason, find the tool_use block(s), and run the corresponding function. A common mistake is assuming there's only ever one tool call per turn — Claude can request multiple tools in parallel, so iterate over all tool_use blocks in the content array.

const response = await callClaude(messages, tools);

if (response.stop_reason === "tool_use") {
  const toolResults = [];

  for (const block of response.content) {
    if (block.type === "tool_use") {
      const result = await executeTool(block.name, block.input);
      toolResults.push({
        type: "tool_result",
        tool_use_id: block.id,
        content: JSON.stringify(result)
      });
    }
  }
}

Step 4: Return the tool result

Append Claude's original response as an assistant message, then send a new user message containing the tool_result blocks matched to the correct tool_use_id. This is the step most implementations get wrong — the tool_result must be tied to the exact id Claude generated, not just sent as plain text.

messages.push({ role: "assistant", content: response.content });
messages.push({ role: "user", content: toolResults });

const finalResponse = await callClaude(messages, tools);

Claude then reads the tool output and produces a natural-language answer, or — if the task requires it — calls another tool. This is why tool use is implemented as a loop, not a single request/response pair.

Step 5: Loop until stop_reason is not tool_use

Wrap steps 3–4 in a loop that keeps calling the model and executing tools until Claude returns end_turn or another terminal stop reason. Always set a max iteration count — a misconfigured tool or an ambiguous schema can cause the model to call the same tool repeatedly.

let iterations = 0;
while (response.stop_reason === "tool_use" && iterations < 10) {
  // execute tools, append results, call again
  iterations++;
}

Handling errors gracefully

If a tool call fails — invalid input, an API timeout, a permission error — don't throw and abandon the loop. Return a tool_result with is_error: true and a short explanation. Claude will usually retry with corrected arguments or explain the failure to the user instead of silently breaking the conversation.

{
  "type": "tool_result",
  "tool_use_id": "toolu_01A2b3C4d5",
  "content": "City not found: 'Lisbob'",
  "is_error": true
}

Streaming and tool use together

If you're streaming responses, tool_use blocks arrive incrementally as input_json_delta events that you need to accumulate before the input is valid JSON. Don't try to parse partial JSON mid-stream — wait for the content_block_stop event for that block. See /docs/streaming for the event sequence if you're combining streaming with tool calls.

Testing your implementation

If you're managing this across a team — multiple developers, multiple environments, shared API keys with usage tracking — a service layer like SubToAPI can simplify the operational side: scoped sub_live_... keys per environment, streaming, and the same tool-calling interface, with usage visible in one dashboard instead of per-developer credentials. Start with /docs/quickstart if you want to see the full request/response cycle end to end, or check /pricing for team plans.

Questions

Do I need a special API plan to use tool calling? No — tool use is part of the standard Messages API and works on any plan that supports the Messages endpoint, including SubToAPI's Solo, Team, and Scale tiers.

Can Claude call multiple tools in one turn? Yes. A single response can contain several tool_use blocks; your code should loop over all of them and return a matching tool_result for each tool_use_id.

What happens if I don't return a tool_result for every tool_use block? The next request will fail or Claude will treat the conversation as incomplete — every tool_use_id sent by the model must have a corresponding tool_result before you continue the conversation.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →