Skip to content
繁體中文
Messages

Tool Calling ​

Connect model requests to your application functions and return results for the next turn.

POST /v1/messages

Custom functions run in your application. The model supplies the function name and arguments; your code performs the lookup and returns the result.

Request and Authentication ​

http
POST https://api.tokatlas.ai/v1/messages
x-api-key: YOUR_API_KEY
anthropic-version: 2023-06-01
Content-Type: application/json
json
{
  "model": "claude-sonnet-4-6",
  "tools": [
    {
      "name": "lookup_stock",
      "description": "Look up available inventory for a product SKU.",
      "input_schema": {
        "type": "object",
        "properties": {
          "sku": {
            "type": "string",
            "description": "Product SKU, for example DEMO-001"
          }
        },
        "required": [
          "sku"
        ],
        "additionalProperties": false
      }
    }
  ],
  "max_tokens": 2048,
  "messages": [
    {
      "role": "user",
      "content": "Check stock for DEMO-001."
    }
  ]
}

Tool Call Format ​

Illustrative call item; IDs and arguments come from the actual response.

json
{
  "type": "tool_use",
  "id": "toolu_example",
  "name": "lookup_stock",
  "input": {
    "sku": "DEMO-001"
  }
}
  • Read every tool_use block in content. Return matching tool_result blocks in the next user message, preserving the entire assistant content. Put results before any user text.

  • tool_choice accepts {"type":"auto"}, {"type":"any"}, or {"type":"tool","name":"lookup_stock"}. Use automatic selection for the loop so the model can finish.

  • For streaming, assemble input_json_delta.partial_json by content-block index before parsing. Do not execute partial arguments.

First Request in Each Language ​

These requests obtain a tool-call request; they do not execute the tool. Use the complete workflow below to return results with the actual call IDs and continue the conversation.

See language setup. Set API_KEY and replace model, file URL, and ID placeholders first. Each version displays the raw response to the same request.

bash
curl --fail-with-body --silent --show-error --max-time 180 \
  --request POST \
  --url "https://api.tokatlas.ai/v1/messages" \
  --header "x-api-key: $API_KEY" \
  --header "anthropic-version: 2023-06-01" \
  --header "Content-Type: application/json" \
  --data '{
  "model": "claude-sonnet-4-6",
  "tools": [
    {
      "name": "lookup_stock",
      "description": "Look up available inventory for a product SKU.",
      "input_schema": {
        "type": "object",
        "properties": {
          "sku": {
            "type": "string",
            "description": "Product SKU, for example DEMO-001"
          }
        },
        "required": [
          "sku"
        ],
        "additionalProperties": false
      }
    }
  ],
  "stream": false,
  "messages": [
    {
      "role": "user",
      "content": "Check stock for DEMO-001."
    }
  ],
  "max_tokens": 2048
}'
python
import os
import requests

headers = {
    'x-api-key': os.environ["API_KEY"],
    'anthropic-version': '2023-06-01',
    'Content-Type': 'application/json',
}
payload = {'model': 'claude-sonnet-4-6',
 'tools': [{'name': 'lookup_stock',
            'description': 'Look up available inventory for a product SKU.',
            'input_schema': {'type': 'object',
                             'properties': {'sku': {'type': 'string',
                                                    'description': 'Product SKU, for '
                                                                   'example DEMO-001'}},
                             'required': ['sku'],
                             'additionalProperties': False}}],
 'stream': False,
 'messages': [{'role': 'user', 'content': 'Check stock for DEMO-001.'}],
 'max_tokens': 2048}
response = requests.request(
    'POST', 'https://api.tokatlas.ai/v1/messages', headers=headers,
    json=payload,
    timeout=180,
)
response.raise_for_status()
print(response.text)
js
if (!process.env.API_KEY) throw new Error("Set API_KEY first.");
const response = await fetch("https://api.tokatlas.ai/v1/messages", {
  method: "POST",
  headers: {
    "x-api-key": process.env.API_KEY,
    "anthropic-version": "2023-06-01",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "model": "claude-sonnet-4-6",
  "tools": [
    {
      "name": "lookup_stock",
      "description": "Look up available inventory for a product SKU.",
      "input_schema": {
        "type": "object",
        "properties": {
          "sku": {
            "type": "string",
            "description": "Product SKU, for example DEMO-001"
          }
        },
        "required": [
          "sku"
        ],
        "additionalProperties": false
      }
    }
  ],
  "stream": false,
  "messages": [
    {
      "role": "user",
      "content": "Check stock for DEMO-001."
    }
  ],
  "max_tokens": 2048
}),
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
console.log(await response.text());
java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

public class Example {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("API_KEY");
        if (apiKey == null || apiKey.isBlank()) {
            throw new IllegalArgumentException("Set API_KEY first.");
        }
        String payload = String.join("\n",
            "{",
            "  \"model\": \"claude-sonnet-4-6\",",
            "  \"tools\": [",
            "    {",
            "      \"name\": \"lookup_stock\",",
            "      \"description\": \"Look up available inventory for a product SKU.\",",
            "      \"input_schema\": {",
            "        \"type\": \"object\",",
            "        \"properties\": {",
            "          \"sku\": {",
            "            \"type\": \"string\",",
            "            \"description\": \"Product SKU, for example DEMO-001\"",
            "          }",
            "        },",
            "        \"required\": [",
            "          \"sku\"",
            "        ],",
            "        \"additionalProperties\": false",
            "      }",
            "    }",
            "  ],",
            "  \"stream\": false,",
            "  \"messages\": [",
            "    {",
            "      \"role\": \"user\",",
            "      \"content\": \"Check stock for DEMO-001.\"",
            "    }",
            "  ],",
            "  \"max_tokens\": 2048",
            "}"
        );
        HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30)).build();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.tokatlas.ai/v1/messages"))
            .timeout(Duration.ofSeconds(180))
            .header("x-api-key", apiKey)
            .header("anthropic-version", "2023-06-01")
            .header("Content-Type", "application/json")
            .method("POST", HttpRequest.BodyPublishers.ofString(payload))
            .build();
        HttpResponse<String> response = client.send(
            request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new IllegalStateException("HTTP " + response.statusCode() + ": "
                + response.body());
        }
        System.out.println(response.body());
    }
}

Complete Python Example ​

Install requests (pip install requests) and set the API_KEY environment variable. The inventory result is demo data. Replace execute with your business service and select a model enabled for your account.

python
import json
import os
import requests

URL = "https://api.tokatlas.ai/v1/messages"
HEADERS = {'x-api-key': os.environ["API_KEY"], 'anthropic-version': '2023-06-01', 'Content-Type': 'application/json'}
TOOLS = [{'name': 'lookup_stock',
  'description': 'Look up available inventory for a product SKU.',
  'input_schema': {'type': 'object',
                   'properties': {'sku': {'type': 'string',
                                          'description': 'Product SKU, for example '
                                                         'DEMO-001'}},
                   'required': ['sku'],
                   'additionalProperties': False}}]


def post(payload):
    response = requests.post(URL, headers=HEADERS, json=payload, timeout=60)
    response.raise_for_status()
    body = response.json()
    # Accept the documented gateway envelope or a direct protocol response.
    data = body.get("data", body)
    if not isinstance(data, dict):
        raise RuntimeError("Unexpected API response")
    if data.get("error"):
        raise RuntimeError(data["error"])
    return data


def execute(name, args):
    if name != "lookup_stock":
        return {"error": "Unknown tool"}
    if not isinstance(args, dict) or set(args) != {"sku"}:
        return {"error": "Expected exactly one sku argument"}
    if not isinstance(args["sku"], str) or not args["sku"].strip():
        return {"error": "sku must be a non-empty string"}
    # Demo fixture only; replace with your inventory service.
    stock = {"DEMO-001": 18}
    if args["sku"] not in stock:
        return {"error": "SKU not found"}
    return {"sku": args["sku"], "available": stock[args["sku"]]}

messages = [{"role": "user", "content": "Check stock for DEMO-001."}]
for _ in range(6):
    reply = post({"model": "claude-sonnet-4-6", "max_tokens": 2048,
                  "tools": TOOLS, "messages": messages})
    if reply.get("stop_reason") == "max_tokens":
        raise RuntimeError("Truncated response; increase max_tokens")
    blocks = reply["content"]
    calls = [b for b in blocks if b["type"] == "tool_use"]
    if not calls:
        print("\n".join(b["text"] for b in blocks if b["type"] == "text"))
        break
    messages.append({"role": "assistant", "content": blocks})
    results = []
    for call in calls:
        result = execute(call["name"], call["input"])
        results.append({"type": "tool_result", "tool_use_id": call["id"],
                        "content": json.dumps(result), "is_error": "error" in result})
    messages.append({"role": "user", "content": results})
else:
    raise RuntimeError("Tool round limit reached")

Troubleshooting ​

SymptomCheck
Missing tool resultReturn one result for every call; retain the exact IDs.
Repeated callsAvoid forcing a tool on every round; cap the loop.
Invalid argumentsValidate names and parameters before dispatching.
Tool failureReturn a structured error instead of invented data.
Unsupported optionCheck the selected model and gateway route capabilities.