出典: Anthropic Academy / 2026-07-04
日本語ナレッジ(本文の自然な全訳)
あなたの既存のワークフローは、多くの異なるテクノロジーに依存しています。プロジェクト管理ソフト、データベース、ファイルなど。Claude はこれらを自分でチェックすることはできません。代わりに、外部のデータやアクションへのアクセスを与える「ツール(tools)」に頼ります。
ツールとは何か
簡単に言えば、ツールとは、あなたが定義して Claude に公開する関数です。それが何をするか、どんな入力を取るかを記述し、いつ呼び出すかは Claude が判断します。
ここで腹落ちさせておくべき重要点:Claude はツールを実行しません。実行するのはあなたのコードです。 流れはこうです。
- Claude がツール呼び出しをリクエストする。
- あなたのコードがその関数を実行する。
- 結果が Claude に戻り、Claude は続行する。
ツールの定義のしかた
ツールは 3 つのパートを持つ JSON スキーマです。名前(name)、説明(description)、入力スキーマ(input schema)。これらをリクエストボディの tools 配列として Claude に渡します。
description は、そのツールを呼ぶべきかどうかを判断するために Claude が読むものです。 曖昧な説明を書くと、ツール使用が下手になります。これがエージェントが誤作動したり、使えるツールを掴めなかったりする第 1 位の原因です。具体的に書きましょう。
ツール定義はこんな形です。
{
"name": "lookup_building_code",
"description": "Look up a specific building code section by its identifier. Returns the full text of that code section.",
"input_schema": {
"type": "object",
"properties": {
"section": {
"type": "string",
"description": "The building code section to look up"
}
},
"required": ["section"]
}
}
これを使うと何が起こるでしょうか。たとえばエージェントにコンプライアンスレポートを送ったとします。最初のターンで、Claude は stop_reason: "tool_use" で返ってきます。これが私たちのシグナルです。
私たちのループは、Claude がリクエストしたパラメータで lookup_building_code を呼び出し、その結果を tool result として戻します。tool result とは、ツール呼び出しの id に紐づいた tool_result ブロックを含む user メッセージです。
そして Claude は続行します。その時点から、Claude が必要なものを揃えるまで、ツールを呼んで結果を返すことを繰り返せます。
複数のツール:Claude に選ばせる
ツール 1 つでも役に立ちますが、面白いのは Claude に複数のツールを与え、どれをどの順で使うかを Claude が選ぶのを見ることです。
こんなシナリオを想像してください。デンバーへの 3 日間の旅行の荷造りをしていて、今日の天気と、これから数日の予報の両方が知りたい。そこで、1 つではなく 2 つのツールを宣言します。
const tools = [
{
name: "get_weather",
description: "Get today's current weather for a city.",
input_schema: {
type: "object",
properties: {
city: { type: "string", description: "The city to check" }
},
required: ["city"]
}
},
{
name: "get_forecast",
description: "Get the weather forecast for the next few days for a city.",
input_schema: {
type: "object",
properties: {
city: { type: "string", description: "The city to check" }
},
required: ["city"]
}
}
];
ループはこれまで見てきたエージェントループと同一です。新しい部分は、ツール名で分岐する runTool 関数(switch 文)だけ。このコードブロックが、実際にあなたのコードが走る場所です。
function runTool(name, input) {
switch (name) {
case "get_weather":
return getWeather(input.city);
case "get_forecast":
return getForecast(input.city);
}
}
while (true) {
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages,
tools,
});
if (response.stop_reason !== "tool_use") {
// Claude is done — this is the final answer
break;
}
messages.push({ role: "assistant", content: response.content });
const toolResults = response.content
.filter((block) => block.type === "tool_use")
.map((block) => ({
type: "tool_result",
tool_use_id: block.id,
content: runTool(block.name, block.input),
}));
messages.push({ role: "user", content: toolResults });
}
これがパターンの全体です。3 つ目のツールが欲しい? 配列に足して、switch に case を足す、それで完了です。
これを実行すると、Claude が get_weather を呼び、それから get_forecast を呼ぶのが見えます。同じターンで両方呼ぶこともあれば、続けて 1 つずつ呼ぶこともあります。そして答えます。「重ね着を持っていけ、今日は雪がちらつくが週の後半にかけて暖かくなる」と。
ここで Claude がどう選んだかに注目してください。description を読み、あなたのプロンプトを「今日の天気」と「これから数日」に対応づけ、それぞれに適したツールを選びました。だからこそ、あなたのツールの description は本当に重要なのです。
ツールランナー:ボイラープレートを省く
いま書いたコードに、あなたはすでに 2 つの危険信号(red flags)に気づいているはずです。
- たった 2 つの単純なルックアップにしては、コードが多すぎる。
- 実際のコードベースでは、持っている関数ごとに JSON スキーマを手書きしたくはない。コードを二度書くようなものだ。
そこで登場するのがツールランナー(tool runner)です。これは TypeScript・Python・Ruby 向けの Claude SDK に同梱されています。ランナーはあなたの実際の関数を受け取り、型とドキュメントを読んでスキーマを組み立て、tool use / tool result のループ全体を内部で処理します。
あなたのコードはこう縮みます。ツールを記述し、プロンプトを送り、結果を待つ。同じ 2 ツールの天気デモをツールランナー経由で配線したものがこれです。
// The same two lookups we ran by hand — just plain TypeScript functions
function getWeather(city: string) {
// ...existing lookup
}
function getForecast(city: string) {
// ...existing lookup
}
const runner = client.beta.messages.toolRunner({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [
{
role: "user",
content:
"I'm packing for a three-day trip to Denver. What's the weather today and over the next few days?",
},
],
tools: [getWeather, getForecast],
});
// Returns the final assistant message after all the tool ping-pong has settled
const finalMessage = await runner.untilDone();
同じシナリオが、わずかなコード量で済みます。
- while ループも、stop reason の分岐も、ツール結果を手動で messages に戻す作業もない — ランナーがそのすべてを処理する。
- JSON スキーマがない。だから二度書きにならない。
- 2 つの関数は、少し前に手で回したのと同じルックアップで、ただのプレーンな TypeScript。
runner.untilDone() は、すべてが落ち着いた後に最終的なアシスタントメッセージを返す。
実行すると、同じ答えが得られます。
本物のツールは既存コードをラップする
実際には、あなたのツールはハードコードされた天気データではありません。アプリケーションにすでにある本物の関数をラップします。
コンプライアンスレビューエージェントを考えてみましょう。そのツールは、コードベースにすでに存在する lookup_building_code と search_building_code 関数の薄いラッパーです。ツールランナーを使えば、それらの関数を直接渡すだけで、エージェントは書き込むすべての指摘(finding)で特定の建築基準セクションを引用します — スキーマの記述は一切不要です。
まとめ
- ツールは Claude にあなたのシステムへのアクセスを与える。ツールとは、あなたが定義して公開する関数。Claude がいつ呼ぶかを判断し、あなたのコードが実行する。
- ツールは name・description・input schema を持つ JSON スキーマで、リクエストの
tools 配列として渡す。
- 具体的な description を書く。曖昧な description はエージェントが誤作動する第 1 位の原因。
stop_reason: "tool_use" が、ツールを実行して結果を tool result として返すべき合図。
- 複数ツールでは、ツール名で分岐する。ツールを足すとは、配列に足して case を足すこと。
- SDK のツールランナー(TypeScript・Python・Ruby)は、あなたの実際の関数からスキーマを作り、ループ全体を処理する — あるいは自分でループを回してもよい。
- あなたが実行するか、ループを委譲するか。そのスペクトルの遠端では、マネージドエージェントがエージェント全体を Anthropic に委譲する。
英語原文
Your existing workflows rely on a lot of different technologies — project management software, databases, files. Claude can't just check these things itself. Instead, it relies on tools, which give Claude access to external data and actions.
Simply put, a tool is a function you define and expose to Claude. You describe what it does and what inputs it takes, and Claude decides when to call it.
Here's the key thing to internalize: Claude doesn't execute the tool — your code does. The flow looks like this:
- Claude requests a tool call.
- Your code executes the function.
- The result goes back to Claude, and it keeps going.
Tools are JSON schemas with three parts: a name, a description, and an input schema. You pass them to Claude in the request body as a tools array.
The description is what Claude reads to decide whether to call the tool. If you write a vague description, you get bad tool use. This is the number one reason agents misfire or don't grab the tools that are available to them. Be specific.
Here's what a tool definition looks like:
{
"name": "lookup_building_code",
"description": "Look up a specific building code section by its identifier. Returns the full text of that code section.",
"input_schema": {
"type": "object",
"properties": {
"section": {
"type": "string",
"description": "The building code section to look up"
}
},
"required": ["section"]
}
}
So what happens when we use this? Say we send an agent a compliance report. On the first turn, Claude comes back with stop_reason: "tool_use" — that's our signal. Here's what that response looks like:
Our loop calls lookup_building_code with the parameter Claude requested, then feeds the result back as a tool result — a user message containing a tool_result block tied to the tool call's id:
And Claude keeps going. At that point, we can keep calling tools and returning results to Claude until it has what it needs.
One tool is useful, but the interesting part is giving Claude multiple tools and watching it pick which one to use, in what order.
Picture this scenario: you're packing for a three-day trip to Denver, and you want both today's weather and the forecast for the next few days. So we declare two tools instead of one:
const tools = [
{
name: "get_weather",
description: "Get today's current weather for a city.",
input_schema: {
type: "object",
properties: {
city: { type: "string", description: "The city to check" }
},
required: ["city"]
}
},
{
name: "get_forecast",
description: "Get the weather forecast for the next few days for a city.",
input_schema: {
type: "object",
properties: {
city: { type: "string", description: "The city to check" }
},
required: ["city"]
}
}
];
The loop is identical to the agent loops we've already seen. The only new piece is a runTool function that dispatches on the tool name with a switch statement — this block of code is just where your code actually runs:
function runTool(name, input) {
switch (name) {
case "get_weather":
return getWeather(input.city);
case "get_forecast":
return getForecast(input.city);
}
}
while (true) {
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages,
tools,
});
if (response.stop_reason !== "tool_use") {
// Claude is done — this is the final answer
break;
}
messages.push({ role: "assistant", content: response.content });
const toolResults = response.content
.filter((block) => block.type === "tool_use")
.map((block) => ({
type: "tool_result",
tool_use_id: block.id,
content: runTool(block.name, block.input),
}));
messages.push({ role: "user", content: toolResults });
}
And that's the whole pattern. Want a third tool? Add it to the array, add a case to the switch, and you're done.
Run this, and you'll see Claude call get_weather and then get_forecast — sometimes in the same turn, sometimes one after the other. Then it answers: pack layers, expect snow flurries today, warming through the week.
Now notice how Claude chose. It read the descriptions, mapped your prompt to "today's weather" and "the next few days," and picked the right tool for each. That's why your tool descriptions really matter.
You've probably already spotted two red flags with what we just wrote:
- That's a lot of code for two simple lookups.
- In a real codebase, you don't want to handwrite JSON schemas for every function you have. It's like writing your code twice.
That's where the tool runner comes in. It ships in the Claude SDK for TypeScript, Python, and Ruby. The runner takes your actual functions, reads the types and docs to build the schema for you, and handles the entire tool use / tool result loop internally.
Your code shrinks down to: describe the tool, send the prompt, wait for the result. Here's the same two-tool weather demo wired through the tool runner:
// The same two lookups we ran by hand — just plain TypeScript functions
function getWeather(city: string) {
// ...existing lookup
}
function getForecast(city: string) {
// ...existing lookup
}
const runner = client.beta.messages.toolRunner({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [
{
role: "user",
content:
"I'm packing for a three-day trip to Denver. What's the weather today and over the next few days?",
},
],
tools: [getWeather, getForecast],
});
// Returns the final assistant message after all the tool ping-pong has settled
const finalMessage = await runner.untilDone();
Same scenario, a fraction of the code:
- No while loop, no stop reason switch, no manually pushing tool results back into messages — the runner handles all of that.
- No JSON schemas, so you don't write things twice.
- The two functions are the same lookups we ran by hand a minute ago, just plain TypeScript.
- runner.untilDone() returns the final assistant message once everything has settled.
Run it, and you get the same answer.
In real life, your tools wouldn't be hardcoded weather data. They'd wrap actual functions you already have in your application.
Take a compliance review agent: its tools are thin wrappers around lookup_building_code and search_building_code functions that already exist in the codebase. With the tool runner, you pass those functions in directly, and the agent cites specific code sections in every finding it writes — no schema writing required:
Recap
- Tools give Claude access to your systems. A tool is a function you define and expose; Claude decides when to call it, and your code executes it.
- Tools are JSON schemas with a name, a description, and an input schema, passed in the request as a tools array.
- Write specific descriptions. Vague descriptions are the number one reason agents misfire.
- stop_reason: "tool_use" is your signal to run the tool and feed the result back as a tool result.
- For multiple tools, dispatch on the tool name. Adding a tool means adding to the array and adding a case.
- The SDK's tool runner (TypeScript, Python, Ruby) builds schemas from your actual functions and handles the whole loop — or you can run the loop yourself.
- You execute, or you delegate the loop. At the far end of that spectrum, managed agents delegate the whole agent to Anthropic.