Describe the JSON shape of a Claude Opus 4.7 assistant message that calls a tool
Walk through what an assistant message looks like at the JSON level when Claude Opus 4.7 calls a tool during a turn, and what the next user turn must contain. Be specific about field names and the block typed content array.
Assistant content is a typed-block array mixing text and tool_use blocks; the next user turn replies with tool_result blocks linked by tool_use_id. No separate tool role.
Picture a conversation where each speaker can hand over more than one slip of paper per turn. The assistant might pass a slip with words on it and another slip that says please run this calculation, with a unique sticker number on the calculation slip. On the next turn, the user hands back a slip with the calculation result and the same sticker number, so it is obvious which request the result answers. Nothing is glued together as one long sentence. Each kind of slip has its own format, and the sticker number is what keeps the call and the result linked across turns. That is exactly how the message shape works under the hood.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Tool calling is the most common reason teams fine-tune in 2026, and the schema details are where teams quietly ship broken behaviour. Anthropic's messages API has a specific, opinionated shape for tool calls that differs from the OpenAI chat completions convention, and conflating the two is a frequent source of training-time bugs that pass tests but fail under production load.
The core idea is that assistant content is not a string. It is a list of typed blocks, and the API treats each block as a first-class structural unit. A tool call is one specific block type, with its own required fields and its own protocol for how the result returns on the next turn. Getting the shape right at training time means the model learns to emit valid block-typed output at inference time. Getting it wrong means the model learns to emit flat text that looks like JSON, which no runtime can dispatch.
The rest of this section walks the assistant turn structure, the tool_use block fields, the tool_result reply protocol, the id linkage that enables parallel tool calls, and the specific failure modes that hit fine-tuning datasets when teams accidentally flatten the structure.
The block-typed content array
Every message in the Anthropic messages API has two top-level fields: role (one of 'user', 'assistant', or 'system') and content. The content field is the source of all the schema-level interest. For simple text messages it can be a string, but for any structured interaction including tool calls, it must be an array of typed blocks.
The relevant block types for tool calls are text, tool_use, and tool_result. A text block looks like {"type": "text", "text": "..."} and carries natural-language content. A tool_use block, valid only inside an assistant turn, looks like {"type": "tool_use", "id": "...", "name": "...", "input": {...}}. A tool_result block, valid only inside a user turn, looks like {"type": "tool_result", "tool_use_id": "...", "content": ...}.
The array structure is what lets a single turn carry multiple blocks. The assistant can emit a text block of reasoning followed by a tool_use block, or several tool_use blocks for parallel calls, or a text block then a tool_use block then another text block. The order matters: the runtime preserves it, and the model's training data should preserve it too. Flattening the array into a single string at any stage of data preparation destroys the type information and the boundary between blocks, which is the supervision signal that teaches the model to emit valid structured output.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
from anthropic import Anthropic
client = Anthropic()
resp = client.messages.create(
model="claude-opus-4-7-20260101",
max_tokens=1024,
tools=[{
"name": "get_weather",
"description": "Lookup current weather for a city",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}],
messages=[
{"role": "user", "content": "Weather in Tokyo?"},
],
)
# resp.content is the block-typed array.
# For a tool call, you will see something like:
# [TextBlock(text="Let me check..."),
# ToolUseBlock(id="toolu_01...", name="get_weather",
# input={"city": "Tokyo"})]
for block in resp.content:
if block.type == "tool_use":
result = run_tool(block.name, block.input)
# Reply with a tool_result block under role 'user'
followup = client.messages.create(
model="claude-opus-4-7-20260101",
max_tokens=1024,
messages=[
{"role": "user", "content": "Weather in Tokyo?"},
{"role": "assistant", "content": resp.content},
{"role": "user", "content": [
{"type": "tool_result",
"tool_use_id": block.id,
"content": result},
]},
],
)| Aspect | Anthropic messages API | OpenAI chat completions |
|---|---|---|
| Assistant content | Typed-block array (text, tool_use) | String plus tool_calls field |
| Tool-call location | Inline tool_use block in content | Separate tool_calls field on message |
| Result role | 'user' with tool_result block | 'tool' role with content string |
| Call-to-result linkage | tool_use_id matches tool_use.id | tool_call_id matches tool_calls[].id |
| Parallel calls | Multiple tool_use blocks in one turn | Multiple entries in tool_calls array |
Real products, models, and research that use this idea.
- Claude Opus 4.7 production deployments at Anthropic customers use this exact block-typed shape for tool-augmented agents calling search, calculator, and code-execution tools.
- Anthropic's official SDKs (TypeScript, Python) construct typed-block content automatically when you pass tools to the messages.create call.
What an interviewer would ask next. Try answering before peeking at the approach.
QHow does the message shape support parallel tool calls in one assistant turn?
The assistant emits multiple tool_use blocks in one content array, each with its own id. The runtime executes all calls and returns a user message whose content array carries one tool_result per call, linked by tool_use_id. The model can then reason over all results in the next assistant turn.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Treating assistant content as a single string and putting a tool call into JSON inside that string. The API expects a list of typed blocks; flat strings drop the structured schema and break the model's tool-call grounding.
60 second bullets to scan on the way to the call.
The typed-block structure of assistant content
The three required fields on a tool_use block
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.