agent-sdk/custom-tools.md +833 −0 created
1> ## Documentation Index
2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
3> Use this file to discover all available pages before exploring further.
4
5# Claude にカスタムツールを提供する
6
7> Claude Agent SDK のインプロセス MCP サーバーでカスタムツールを定義し、Claude が関数を呼び出し、API にアクセスし、ドメイン固有の操作を実行できるようにします。
8
9カスタムツールは Agent SDK を拡張し、Claude が会話中に呼び出せる独自の関数を定義できるようにします。SDK のインプロセス MCP サーバーを使用すると、Claude にデータベース、外部 API、ドメイン固有のロジック、またはアプリケーションが必要とするその他の機能へのアクセスを提供できます。
10
11このガイドでは、入力スキーマとハンドラーを使用してツールを定義し、それらを MCP サーバーにバンドルし、`query` に渡し、Claude がアクセスできるツールを制御する方法について説明します。また、エラーハンドリング、ツール注釈、および画像などの非テキストコンテンツを返す方法についても説明します。
12
13## クイックリファレンス
14
15| 実行したい操作 | 方法 |
16| :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17| ツールを定義する | Python では [`@tool`](/ja/agent-sdk/python#tool)、TypeScript では [`tool()`](/ja/agent-sdk/typescript#tool) を使用して、名前、説明、スキーマ、ハンドラーを指定します。[カスタムツールを作成する](#create-a-custom-tool)を参照してください。 |
18| Claude にツールを登録する | `create_sdk_mcp_server` / `createSdkMcpServer` でラップし、`query()` の `mcpServers` に渡します。[カスタムツールを呼び出す](#call-a-custom-tool)を参照してください。 |
19| ツールを事前承認する | 許可されたツールに追加します。[許可されたツールを設定する](#configure-allowed-tools)を参照してください。 |
20| Claude のコンテキストから組み込みツールを削除する | 必要な組み込みのみをリストする `tools` 配列を渡します。[許可されたツールを設定する](#configure-allowed-tools)を参照してください。 |
21| Claude がツールを並列で呼び出せるようにする | 副作用のないツールに `readOnlyHint: true` を設定します。[ツール注釈を追加する](#add-tool-annotations)を参照してください。 |
22| ループを停止せずにエラーを処理する | throw の代わりに `isError: true` を返します。[エラーを処理する](#handle-errors)を参照してください。 |
23| 画像またはファイルを返す | コンテンツ配列で `image` または `resource` ブロックを使用します。[画像とリソースを返す](#return-images-and-resources)を参照してください。 |
24| マシン可読 JSON 結果を返す | 結果に `structuredContent` を設定します。[構造化データを返す](#return-structured-data)を参照してください。 |
25| 多くのツールにスケーリングする | [ツール検索](/ja/agent-sdk/tool-search)を使用して、オンデマンドでツールを読み込みます。 |
26
27## カスタムツールを作成する
28
29ツールは 4 つの部分で定義され、TypeScript の [`tool()`](/ja/agent-sdk/typescript#tool) ヘルパーまたは Python の [`@tool`](/ja/agent-sdk/python#tool) デコレーターに引数として渡されます。
30
31* **名前:** Claude がツールを呼び出すために使用する一意の識別子。
32* **説明:** ツールが何をするかを説明します。Claude はこれを読んで、ツールをいつ呼び出すかを決定します。
33* **入力スキーマ:** Claude が提供する必要がある引数。TypeScript では常に [Zod スキーマ](https://zod.dev/)であり、ハンドラーの `args` は自動的に型付けされます。Python では `{"latitude": float}` のような名前から型へのマッピングであり、SDK が JSON Schema に変換します。Python デコレーターは、列挙型、範囲、オプションフィールド、またはネストされたオブジェクトが必要な場合、完全な [JSON Schema](https://json-schema.org/understanding-json-schema/about) 辞書も受け入れます。
34* **ハンドラー:** Claude がツールを呼び出すときに実行される非同期関数。検証された引数を受け取り、以下を含むオブジェクトを返す必要があります。
35 * `content`(必須):結果ブロックの配列。各ブロックは `"text"`、`"image"`、または `"resource"` の `type` を持ちます。非テキストブロックについては、[画像とリソースを返す](#return-images-and-resources)を参照してください。
36 * `structuredContent`(オプション):結果をマシン可読データとして保持する JSON オブジェクト。`content` と共に返されます。[構造化データを返す](#return-structured-data)を参照してください。
37 * `isError`(オプション):ツール障害を通知するために `true` に設定し、Claude が対応できるようにします。[エラーを処理する](#handle-errors)を参照してください。
38
39ツールを定義した後、[`createSdkMcpServer`](/ja/agent-sdk/typescript#createsdkmcpserver)(TypeScript)または [`create_sdk_mcp_server`](/ja/agent-sdk/python#create_sdk_mcp_server)(Python)でサーバーにラップします。サーバーはアプリケーション内でインプロセスで実行され、別のプロセスとしては実行されません。
40
41### 天気ツールの例
42
43この例は `get_temperature` ツールを定義し、MCP サーバーにラップします。ツールのセットアップのみを行います。`query` に渡して実行するには、以下の [カスタムツールを呼び出す](#call-a-custom-tool)を参照してください。
44
45<CodeGroup>
46 ```python Python theme={null}
47 from typing import Any
48 import httpx
49 from claude_agent_sdk import tool, create_sdk_mcp_server
50
51
52 # ツールを定義:名前、説明、入力スキーマ、ハンドラー
53 @tool(
54 "get_temperature",
55 "Get the current temperature at a location",
56 {"latitude": float, "longitude": float},
57 )
58 async def get_temperature(args: dict[str, Any]) -> dict[str, Any]:
59 async with httpx.AsyncClient() as client:
60 response = await client.get(
61 "https://api.open-meteo.com/v1/forecast",
62 params={
63 "latitude": args["latitude"],
64 "longitude": args["longitude"],
65 "current": "temperature_2m",
66 "temperature_unit": "fahrenheit",
67 },
68 )
69 data = response.json()
70
71 # コンテンツ配列を返す - Claude はこれをツール結果として見ます
72 return {
73 "content": [
74 {
75 "type": "text",
76 "text": f"Temperature: {data['current']['temperature_2m']}°F",
77 }
78 ]
79 }
80
81
82 # ツールをインプロセス MCP サーバーにラップします
83 weather_server = create_sdk_mcp_server(
84 name="weather",
85 version="1.0.0",
86 tools=[get_temperature],
87 )
88 ```
89
90 ```typescript TypeScript theme={null}
91 import { tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
92 import { z } from "zod";
93
94 // ツールを定義:名前、説明、入力スキーマ、ハンドラー
95 const getTemperature = tool(
96 "get_temperature",
97 "Get the current temperature at a location",
98 {
99 latitude: z.number().describe("Latitude coordinate"), // .describe() は Claude が見るフィールド説明を追加します
100 longitude: z.number().describe("Longitude coordinate")
101 },
102 async (args) => {
103 // args はスキーマから型付けされます:{ latitude: number; longitude: number }
104 const response = await fetch(
105 `https://api.open-meteo.com/v1/forecast?latitude=${args.latitude}&longitude=${args.longitude}¤t=temperature_2m&temperature_unit=fahrenheit`
106 );
107 const data: any = await response.json();
108
109 // コンテンツ配列を返す - Claude はこれをツール結果として見ます
110 return {
111 content: [{ type: "text", text: `Temperature: ${data.current.temperature_2m}°F` }]
112 };
113 }
114 );
115
116 // ツールをインプロセス MCP サーバーにラップします
117 const weatherServer = createSdkMcpServer({
118 name: "weather",
119 version: "1.0.0",
120 tools: [getTemperature]
121 });
122 ```
123</CodeGroup>
124
125完全なパラメーター詳細については、[`tool()`](/ja/agent-sdk/typescript#tool) TypeScript リファレンスまたは [`@tool`](/ja/agent-sdk/python#tool) Python リファレンスを参照してください。JSON Schema 入力形式と戻り値の構造を含みます。
126
127<Tip>
128 パラメーターをオプションにするには:TypeScript では、Zod フィールドに `.default()` を追加します。Python では、辞書スキーマはすべてのキーを必須として扱うため、パラメーターをスキーマから除外し、説明文字列で言及し、ハンドラーで `args.get()` で読み取ります。以下の [`get_precipitation_chance` ツール](#add-more-tools)は両方のパターンを示しています。
129</Tip>
130
131### カスタムツールを呼び出す
132
133作成した MCP サーバーを `mcpServers` オプション経由で `query` に渡します。`mcpServers` のキーは各ツールの完全修飾名の `{server_name}` セグメントになります:`mcp__{server_name}__{tool_name}`。その名前を `allowedTools` にリストして、ツールが許可プロンプトなしで実行されるようにします。
134
135これらのスニペットは、[上記の例](#weather-tool-example)の `weatherServer` を再利用して、特定の場所の天気について Claude に尋ねます。
136
137<CodeGroup>
138 ```python Python theme={null}
139 import asyncio
140 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
141
142
143 async def main():
144 options = ClaudeAgentOptions(
145 mcp_servers={"weather": weather_server},
146 allowed_tools=["mcp__weather__get_temperature"],
147 )
148
149 async for message in query(
150 prompt="What's the temperature in San Francisco?",
151 options=options,
152 ):
153 # ResultMessage はすべてのツール呼び出しが完了した後の最終メッセージです
154 if isinstance(message, ResultMessage) and message.subtype == "success":
155 print(message.result)
156
157
158 asyncio.run(main())
159 ```
160
161 ```typescript TypeScript theme={null}
162 import { query } from "@anthropic-ai/claude-agent-sdk";
163
164 for await (const message of query({
165 prompt: "What's the temperature in San Francisco?",
166 options: {
167 mcpServers: { weather: weatherServer },
168 allowedTools: ["mcp__weather__get_temperature"]
169 }
170 })) {
171 // "result" はすべてのツール呼び出しが完了した後の最終メッセージです
172 if (message.type === "result" && message.subtype === "success") {
173 console.log(message.result);
174 }
175 }
176 ```
177</CodeGroup>
178
179### さらにツールを追加する
180
181サーバーは `tools` 配列にリストされた数だけのツールを保持します。複数のツールがサーバーにある場合、`allowedTools` で各ツールを個別にリストするか、ワイルドカード `mcp__weather__*` を使用してサーバーが公開するすべてのツールをカバーできます。
182
183以下の例は、[天気ツールの例](#weather-tool-example)の `weatherServer` に 2 番目のツール `get_precipitation_chance` を追加し、両方のツールを配列で再構築します。
184
185<CodeGroup>
186 ```python Python theme={null}
187 # 同じサーバーの 2 番目のツールを定義します
188 @tool(
189 "get_precipitation_chance",
190 "Get the hourly precipitation probability for a location. "
191 "Optionally pass 'hours' (1-24) to control how many hours to return.",
192 {"latitude": float, "longitude": float},
193 )
194 async def get_precipitation_chance(args: dict[str, Any]) -> dict[str, Any]:
195 # 'hours' はスキーマにありません - .get() で読み取ってオプションにします
196 hours = args.get("hours", 12)
197 async with httpx.AsyncClient() as client:
198 response = await client.get(
199 "https://api.open-meteo.com/v1/forecast",
200 params={
201 "latitude": args["latitude"],
202 "longitude": args["longitude"],
203 "hourly": "precipitation_probability",
204 "forecast_days": 1,
205 },
206 )
207 data = response.json()
208 chances = data["hourly"]["precipitation_probability"][:hours]
209
210 return {
211 "content": [
212 {
213 "type": "text",
214 "text": f"Next {hours} hours: {'%, '.join(map(str, chances))}%",
215 }
216 ]
217 }
218
219
220 # 両方のツールを配列で再構築します
221 weather_server = create_sdk_mcp_server(
222 name="weather",
223 version="1.0.0",
224 tools=[get_temperature, get_precipitation_chance],
225 )
226 ```
227
228 ```typescript TypeScript theme={null}
229 // 同じサーバーの 2 番目のツールを定義します
230 const getPrecipitationChance = tool(
231 "get_precipitation_chance",
232 "Get the hourly precipitation probability for a location",
233 {
234 latitude: z.number(),
235 longitude: z.number(),
236 hours: z
237 .number()
238 .int()
239 .min(1)
240 .max(24)
241 .default(12) // .default() はパラメーターをオプションにします
242 .describe("How many hours of forecast to return")
243 },
244 async (args) => {
245 const response = await fetch(
246 `https://api.open-meteo.com/v1/forecast?latitude=${args.latitude}&longitude=${args.longitude}&hourly=precipitation_probability&forecast_days=1`
247 );
248 const data: any = await response.json();
249 const chances = data.hourly.precipitation_probability.slice(0, args.hours);
250
251 return {
252 content: [{ type: "text", text: `Next ${args.hours} hours: ${chances.join("%, ")}%` }]
253 };
254 }
255 );
256
257 // 両方のツールを配列で再構築します
258 const weatherServer = createSdkMcpServer({
259 name: "weather",
260 version: "1.0.0",
261 tools: [getTemperature, getPrecipitationChance]
262 });
263 ```
264</CodeGroup>
265
266この配列内のすべてのツールは、毎ターン、コンテキストウィンドウスペースを消費します。数十のツールを定義している場合は、[ツール検索](/ja/agent-sdk/tool-search)を参照して、代わりにオンデマンドで読み込みます。
267
268### ツール注釈を追加する
269
270[ツール注釈](https://modelcontextprotocol.io/docs/concepts/tools#tool-annotations)は、ツールの動作を説明するオプションのメタデータです。TypeScript の `tool()` ヘルパーの 5 番目の引数として、または Python の `@tool` デコレーターの `annotations` キーワード引数として渡します。すべてのヒントフィールドはブール値です。
271
272| フィールド | デフォルト | 意味 |
273| :---------------- | :------ | :------------------------------------------------ |
274| `readOnlyHint` | `false` | ツールは環境を変更しません。ツールが他の読み取り専用ツールと並列で呼び出せるかどうかを制御します。 |
275| `destructiveHint` | `true` | ツールは破壊的な更新を実行する可能性があります。情報提供のみです。 |
276| `idempotentHint` | `false` | 同じ引数での繰り返し呼び出しは追加の効果がありません。情報提供のみです。 |
277| `openWorldHint` | `true` | ツールはプロセス外のシステムに到達します。情報提供のみです。 |
278
279注釈はメタデータであり、強制ではありません。`readOnlyHint: true` でマークされたツールは、ハンドラーがそれを行う場合、ディスクに書き込むことができます。注釈をハンドラーに正確に保ちます。
280
281この例は、[天気ツールの例](#weather-tool-example)の `get_temperature` ツールに `readOnlyHint` を追加します。
282
283<CodeGroup>
284 ```python Python theme={null}
285 from claude_agent_sdk import tool, ToolAnnotations
286
287
288 @tool(
289 "get_temperature",
290 "Get the current temperature at a location",
291 {"latitude": float, "longitude": float},
292 annotations=ToolAnnotations(
293 readOnlyHint=True
294 ), # Claude がこれを他の読み取り専用呼び出しとバッチ処理できるようにします
295 )
296 async def get_temperature(args):
297 return {"content": [{"type": "text", "text": "..."}]}
298 ```
299
300 ```typescript TypeScript theme={null}
301 tool(
302 "get_temperature",
303 "Get the current temperature at a location",
304 { latitude: z.number(), longitude: z.number() },
305 async (args) => ({ content: [{ type: "text", text: `...` }] }),
306 { annotations: { readOnlyHint: true } } // Claude がこれを他の読み取り専用呼び出しとバッチ処理できるようにします
307 );
308 ```
309</CodeGroup>
310
311[TypeScript](/ja/agent-sdk/typescript#toolannotations) または [Python](/ja/agent-sdk/python#toolannotations) リファレンスで `ToolAnnotations` を参照してください。
312
313## ツールアクセスを制御する
314
315[天気ツールの例](#weather-tool-example)はサーバーを登録し、`allowedTools` にツールをリストしました。このセクションでは、ツール名がどのように構成されるか、および複数のツールがある場合や組み込みを制限したい場合にアクセスをスコープする方法について説明します。
316
317### ツール名形式
318
319MCP ツールが Claude に公開されるとき、それらの名前は特定の形式に従います。
320
321* パターン:`mcp__{server_name}__{tool_name}`
322* 例:`weather` サーバーの `get_temperature` という名前のツールは `mcp__weather__get_temperature` になります
323
324### 許可されたツールを設定する
325
326`tools` オプションと許可/禁止リストは別のレイヤーで動作します。`tools` は Claude のコンテキストに表示される組み込みツールを制御します。許可および禁止ツールリストは、Claude がそれらを試みた後、呼び出しが承認または拒否されるかどうかを制御します。
327
328| オプション | レイヤー | 効果 |
329| :------------------------ | :--- | :----------------------------------------------------------------------------------------------- |
330| `tools: ["Read", "Grep"]` | 可用性 | リストされた組み込みのみが Claude のコンテキストにあります。リストされていない組み込みは削除されます。MCP ツールは影響を受けません。 |
331| `tools: []` | 可用性 | すべての組み込みが削除されます。Claude は MCP ツールのみを使用できます。 |
332| 許可されたツール | 許可 | リストされたツールは許可プロンプトなしで実行されます。リストされていないツールは利用可能なままです。呼び出しは [許可フロー](/ja/agent-sdk/permissions)を通ります。 |
333| 禁止されたツール | 許可 | リストされたツールへのすべての呼び出しが拒否されます。ツールは Claude のコンテキストに留まるため、Claude は呼び出しが拒否される前にそれを試みる可能性があります。 |
334
335Claude が使用できる組み込みを制限するには、禁止されたツールより `tools` を優先します。`tools` からツールを省略すると、Claude がそれを試みることがないようにコンテキストから削除されます。`disallowedTools`(Python:`disallowed_tools`)にリストすると、呼び出しはブロックされますが、ツールは表示されたままなので、Claude はそれを試みるターンを無駄にする可能性があります。完全な評価順序については、[許可を設定する](/ja/agent-sdk/permissions)を参照してください。
336
337## エラーを処理する
338
339ハンドラーがエラーを報告する方法は、エージェントループが続行するか停止するかを決定します。
340
341| 何が起こるか | 結果 |
342| :------------------------------------------------------------------ | :-------------------------------------------------------------- |
343| ハンドラーがキャッチされない例外をスロー | エージェントループが停止します。Claude はエラーを見ず、`query` 呼び出しが失敗します。 |
344| ハンドラーがエラーをキャッチして `isError: true`(TS)/ `"is_error": True`(Python)を返す | エージェントループが続行します。Claude はエラーをデータとして見て、再試行、別のツールを試す、または失敗を説明できます。 |
345
346以下の例は、スロー させる代わりに、ハンドラー内で 2 種類の障害をキャッチします。200 以外の HTTP ステータスは応答からキャッチされ、エラー結果として返されます。ネットワークエラーまたは無効な JSON は、周囲の `try/except`(Python)または `try/catch`(TypeScript)でキャッチされ、エラー結果としても返されます。どちらの場合も、ハンドラーは正常に返され、エージェントループが続行されます。
347
348<CodeGroup>
349 ```python Python theme={null}
350 import json
351 import httpx
352 from typing import Any
353
354
355 @tool(
356 "fetch_data",
357 "Fetch data from an API",
358 {"endpoint": str}, # シンプルなスキーマ
359 )
360 async def fetch_data(args: dict[str, Any]) -> dict[str, Any]:
361 try:
362 async with httpx.AsyncClient() as client:
363 response = await client.get(args["endpoint"])
364 if response.status_code != 200:
365 # Claude が対応できるようにツール結果として失敗を返します。
366 # is_error はこれを失敗した呼び出しとしてマークし、奇妙に見えるデータではなく。
367 return {
368 "content": [
369 {
370 "type": "text",
371 "text": f"API error: {response.status_code} {response.reason_phrase}",
372 }
373 ],
374 "is_error": True,
375 }
376
377 data = response.json()
378 return {"content": [{"type": "text", "text": json.dumps(data, indent=2)}]}
379 except Exception as e:
380 # ここでキャッチすることでエージェントループを生かしておきます。キャッチされない例外
381 # は query() 呼び出し全体を終了させます。
382 return {
383 "content": [{"type": "text", "text": f"Failed to fetch data: {str(e)}"}],
384 "is_error": True,
385 }
386 ```
387
388 ```typescript TypeScript theme={null}
389 tool(
390 "fetch_data",
391 "Fetch data from an API",
392 {
393 endpoint: z.string().url().describe("API endpoint URL")
394 },
395 async (args) => {
396 try {
397 const response = await fetch(args.endpoint);
398
399 if (!response.ok) {
400 // Claude が対応できるようにツール結果として失敗を返します。
401 // isError はこれを失敗した呼び出しとしてマークし、奇妙に見えるデータではなく。
402 return {
403 content: [
404 {
405 type: "text",
406 text: `API error: ${response.status} ${response.statusText}`
407 }
408 ],
409 isError: true
410 };
411 }
412
413 const data = await response.json();
414 return {
415 content: [
416 {
417 type: "text",
418 text: JSON.stringify(data, null, 2)
419 }
420 ]
421 };
422 } catch (error) {
423 // ここでキャッチすることでエージェントループを生かしておきます。キャッチされない throw
424 // は query() 呼び出し全体を終了させます。
425 return {
426 content: [
427 {
428 type: "text",
429 text: `Failed to fetch data: ${error instanceof Error ? error.message : String(error)}`
430 }
431 ],
432 isError: true
433 };
434 }
435 }
436 );
437 ```
438</CodeGroup>
439
440## 画像とリソースを返す
441
442ツール結果の `content` 配列は `text`、`image`、および `resource` ブロックを受け入れます。同じ応答でそれらを混ぜることができます。
443
444### 画像
445
446画像ブロックは画像バイトをインラインで、base64 としてエンコードされた状態で運びます。URL フィールドはありません。URL に存在する画像を返すには、ハンドラーで取得し、応答バイトを読み取り、返す前に base64 エンコードします。結果は視覚入力として処理されます。
447
448| フィールド | 型 | 注釈 |
449| :--------- | :-------- | :----------------------------------------------------------------- |
450| `type` | `"image"` | |
451| `data` | `string` | Base64 エンコードされたバイト。`data:image/...;base64,` プレフィックスなしの生の base64 のみ |
452| `mimeType` | `string` | 必須。例えば `image/png`、`image/jpeg`、`image/webp`、`image/gif` |
453
454<CodeGroup>
455 ```python Python theme={null}
456 import base64
457 import httpx
458
459
460 # URL から画像を取得して Claude に返すツールを定義します
461 @tool("fetch_image", "Fetch an image from a URL and return it to Claude", {"url": str})
462 async def fetch_image(args):
463 async with httpx.AsyncClient() as client: # 画像バイトを取得します
464 response = await client.get(args["url"])
465
466 return {
467 "content": [
468 {
469 "type": "image",
470 "data": base64.b64encode(response.content).decode(
471 "ascii"
472 ), # 生のバイトを base64 エンコードします
473 "mimeType": response.headers.get(
474 "content-type", "image/png"
475 ), # 応答から MIME タイプを読み取ります
476 }
477 ]
478 }
479 ```
480
481 ```typescript TypeScript theme={null}
482 tool(
483 "fetch_image",
484 "Fetch an image from a URL and return it to Claude",
485 {
486 url: z.string().url()
487 },
488 async (args) => {
489 const response = await fetch(args.url); // 画像バイトを取得します
490 const buffer = Buffer.from(await response.arrayBuffer()); // base64 エンコーディング用にバッファに読み込みます
491 const mimeType = response.headers.get("content-type") ?? "image/png";
492
493 return {
494 content: [
495 {
496 type: "image",
497 data: buffer.toString("base64"), // 生のバイトを base64 エンコードします
498 mimeType
499 }
500 ]
501 };
502 }
503 );
504 ```
505</CodeGroup>
506
507### リソース
508
509リソースブロックは URI で識別されるコンテンツを埋め込みます。URI は Claude が参照するためのラベルです。実際のコンテンツはブロックの `text` または `blob` フィールドに含まれます。これは、生成されたファイルや外部システムのレコードなど、後で名前で対処することが理にかなっているツールが生成するものを使用します。
510
511| フィールド | 型 | 注釈 |
512| :------------------ | :----------- | :--------------------------------- |
513| `type` | `"resource"` | |
514| `resource.uri` | `string` | コンテンツの識別子。任意の URI スキーム |
515| `resource.text` | `string` | テキストの場合のコンテンツ。`blob` ではなく、これを提供します |
516| `resource.blob` | `string` | バイナリの場合、base64 エンコードされたコンテンツ |
517| `resource.mimeType` | `string` | オプション |
518
519この例は、ツールハンドラー内から返されるリソースブロックを示しています。URI `file:///tmp/report.md` は Claude が後で参照できるラベルです。SDK はそのパスから読み取りません。
520
521<CodeGroup>
522 ```typescript TypeScript theme={null}
523 return {
524 content: [
525 {
526 type: "resource",
527 resource: {
528 uri: "file:///tmp/report.md", // Claude が参照するためのラベル。SDK が読み取るパスではありません
529 mimeType: "text/markdown",
530 text: "# Report\n..." // 実際のコンテンツ、インライン
531 }
532 }
533 ]
534 };
535 ```
536
537 ```python Python theme={null}
538 return {
539 "content": [
540 {
541 "type": "resource",
542 "resource": {
543 "uri": "file:///tmp/report.md", # Claude が参照するためのラベル。SDK が読み取るパスではありません
544 "mimeType": "text/markdown",
545 "text": "# Report\n...", # 実際のコンテンツ、インライン
546 },
547 }
548 ]
549 }
550 ```
551</CodeGroup>
552
553これらのブロック形状は MCP `CallToolResult` 型から来ています。完全な定義については、[MCP 仕様](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#tool-result)を参照してください。
554
555## 構造化データを返す
556
557`structuredContent` は結果のオプションの JSON オブジェクトで、`content` 配列とは別です。テキスト文字列または画像から解析する代わりに、Claude が正確なフィールドとして読み取ることができる生の値を返すために使用します。
558
559`structuredContent` が設定されると、Claude は JSON と `content` からの任意の画像またはリソースブロックを受け取ります。`content` のテキストブロックは転送されません。構造化データを複製すると想定されるためです。以下の例は、チャートを画像ブロックとしてレンダリングし、同じハンドラーから `structuredContent` でそれの背後にあるデータポイントを返します。
560
561```typescript TypeScript theme={null}
562return {
563 content: [
564 {
565 type: "image",
566 data: chartPngBuffer.toString("base64"),
567 mimeType: "image/png"
568 }
569 ],
570 structuredContent: {
571 series: "temperature_2m",
572 unit: "fahrenheit",
573 points: [62.1, 63.4, 65.0, 64.2]
574 }
575};
576```
577
578<Note>
579 Python `@tool` デコレーターはハンドラーの戻り辞書から `content` と `is_error` のみを転送します。Python から `structuredContent` を返すには、インプロセス SDK サーバーの代わりに [スタンドアロン MCP サーバー](/ja/agent-sdk/mcp)を実行します。
580</Note>
581
582## 例:単位変換ツール
583
584このツールは長さ、温度、重量の単位間で値を変換します。ユーザーは「100 キロメートルをマイルに変換」または「72°F は摂氏何度か」と尋ねることができ、Claude はリクエストから正しい単位タイプと単位を選択します。
585
5862 つのパターンを示しています。
587
588* **列挙型スキーマ:** `unit_type` は固定値のセットに制限されます。TypeScript では `z.enum()` を使用します。Python では、辞書スキーマは列挙型をサポートしないため、完全な JSON Schema 辞書が必要です。
589* **サポートされていない入力処理:** 変換ペアが見つからない場合、ハンドラーは `isError: true` を返すため、Claude はユーザーに何が間違っていたかを伝えることができ、失敗を通常の結果として扱いません。
590
591<CodeGroup>
592 ```python Python theme={null}
593 from typing import Any
594 from claude_agent_sdk import tool, create_sdk_mcp_server
595
596
597 # TypeScript の z.enum() は JSON Schema の "enum" 制約になります。
598 # 辞書スキーマに同等のものはないため、完全な JSON Schema が必要です。
599 @tool(
600 "convert_units",
601 "Convert a value from one unit to another",
602 {
603 "type": "object",
604 "properties": {
605 "unit_type": {
606 "type": "string",
607 "enum": ["length", "temperature", "weight"],
608 "description": "Category of unit",
609 },
610 "from_unit": {
611 "type": "string",
612 "description": "Unit to convert from, e.g. kilometers, fahrenheit, pounds",
613 },
614 "to_unit": {"type": "string", "description": "Unit to convert to"},
615 "value": {"type": "number", "description": "Value to convert"},
616 },
617 "required": ["unit_type", "from_unit", "to_unit", "value"],
618 },
619 )
620 async def convert_units(args: dict[str, Any]) -> dict[str, Any]:
621 conversions = {
622 "length": {
623 "kilometers_to_miles": lambda v: v * 0.621371,
624 "miles_to_kilometers": lambda v: v * 1.60934,
625 "meters_to_feet": lambda v: v * 3.28084,
626 "feet_to_meters": lambda v: v * 0.3048,
627 },
628 "temperature": {
629 "celsius_to_fahrenheit": lambda v: (v * 9) / 5 + 32,
630 "fahrenheit_to_celsius": lambda v: (v - 32) * 5 / 9,
631 "celsius_to_kelvin": lambda v: v + 273.15,
632 "kelvin_to_celsius": lambda v: v - 273.15,
633 },
634 "weight": {
635 "kilograms_to_pounds": lambda v: v * 2.20462,
636 "pounds_to_kilograms": lambda v: v * 0.453592,
637 "grams_to_ounces": lambda v: v * 0.035274,
638 "ounces_to_grams": lambda v: v * 28.3495,
639 },
640 }
641
642 key = f"{args['from_unit']}_to_{args['to_unit']}"
643 fn = conversions.get(args["unit_type"], {}).get(key)
644
645 if not fn:
646 return {
647 "content": [
648 {
649 "type": "text",
650 "text": f"Unsupported conversion: {args['from_unit']} to {args['to_unit']}",
651 }
652 ],
653 "is_error": True,
654 }
655
656 result = fn(args["value"])
657 return {
658 "content": [
659 {
660 "type": "text",
661 "text": f"{args['value']} {args['from_unit']} = {result:.4f} {args['to_unit']}",
662 }
663 ]
664 }
665
666
667 converter_server = create_sdk_mcp_server(
668 name="converter",
669 version="1.0.0",
670 tools=[convert_units],
671 )
672 ```
673
674 ```typescript TypeScript theme={null}
675 import { tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
676 import { z } from "zod";
677
678 const convert = tool(
679 "convert_units",
680 "Convert a value from one unit to another",
681 {
682 unit_type: z.enum(["length", "temperature", "weight"]).describe("Category of unit"),
683 from_unit: z
684 .string()
685 .describe("Unit to convert from, e.g. kilometers, fahrenheit, pounds"),
686 to_unit: z.string().describe("Unit to convert to"),
687 value: z.number().describe("Value to convert")
688 },
689 async (args) => {
690 type Conversions = Record<string, Record<string, (v: number) => number>>;
691
692 const conversions: Conversions = {
693 length: {
694 kilometers_to_miles: (v) => v * 0.621371,
695 miles_to_kilometers: (v) => v * 1.60934,
696 meters_to_feet: (v) => v * 3.28084,
697 feet_to_meters: (v) => v * 0.3048
698 },
699 temperature: {
700 celsius_to_fahrenheit: (v) => (v * 9) / 5 + 32,
701 fahrenheit_to_celsius: (v) => ((v - 32) * 5) / 9,
702 celsius_to_kelvin: (v) => v + 273.15,
703 kelvin_to_celsius: (v) => v - 273.15
704 },
705 weight: {
706 kilograms_to_pounds: (v) => v * 2.20462,
707 pounds_to_kilograms: (v) => v * 0.453592,
708 grams_to_ounces: (v) => v * 0.035274,
709 ounces_to_grams: (v) => v * 28.3495
710 }
711 };
712
713 const key = `${args.from_unit}_to_${args.to_unit}`;
714 const fn = conversions[args.unit_type]?.[key];
715
716 if (!fn) {
717 return {
718 content: [
719 {
720 type: "text",
721 text: `Unsupported conversion: ${args.from_unit} to ${args.to_unit}`
722 }
723 ],
724 isError: true
725 };
726 }
727
728 const result = fn(args.value);
729 return {
730 content: [
731 {
732 type: "text",
733 text: `${args.value} ${args.from_unit} = ${result.toFixed(4)} ${args.to_unit}`
734 }
735 ]
736 };
737 }
738 );
739
740 const converterServer = createSdkMcpServer({
741 name: "converter",
742 version: "1.0.0",
743 tools: [convert]
744 });
745 ```
746</CodeGroup>
747
748サーバーが定義されたら、天気の例と同じ方法で `query` に渡します。この例は、同じツールが異なる単位タイプを処理することを示すために、ループで 3 つの異なるプロンプトを送信します。各応答について、`AssistantMessage` オブジェクト(Claude がそのターン中に行ったツール呼び出しを含む)を検査し、最終的な `ResultMessage` テキストを出力する前に各 `ToolUseBlock` を出力します。これにより、Claude がツールを使用しているのか、独自の知識から答えているのかを確認できます。
749
750<CodeGroup>
751 ```python Python theme={null}
752 import asyncio
753 from claude_agent_sdk import (
754 query,
755 ClaudeAgentOptions,
756 ResultMessage,
757 AssistantMessage,
758 ToolUseBlock,
759 )
760
761
762 async def main():
763 options = ClaudeAgentOptions(
764 mcp_servers={"converter": converter_server},
765 allowed_tools=["mcp__converter__convert_units"],
766 )
767
768 prompts = [
769 "Convert 100 kilometers to miles.",
770 "What is 72°F in Celsius?",
771 "How many pounds is 5 kilograms?",
772 ]
773
774 for prompt in prompts:
775 async for message in query(prompt=prompt, options=options):
776 if isinstance(message, AssistantMessage):
777 for block in message.content:
778 if isinstance(block, ToolUseBlock):
779 print(f"[tool call] {block.name}({block.input})")
780 elif isinstance(message, ResultMessage) and message.subtype == "success":
781 print(f"Q: {prompt}\nA: {message.result}\n")
782
783
784 asyncio.run(main())
785 ```
786
787 ```typescript TypeScript theme={null}
788 import { query } from "@anthropic-ai/claude-agent-sdk";
789
790 const prompts = [
791 "Convert 100 kilometers to miles.",
792 "What is 72°F in Celsius?",
793 "How many pounds is 5 kilograms?"
794 ];
795
796 for (const prompt of prompts) {
797 for await (const message of query({
798 prompt,
799 options: {
800 mcpServers: { converter: converterServer },
801 allowedTools: ["mcp__converter__convert_units"]
802 }
803 })) {
804 if (message.type === "assistant") {
805 for (const block of message.message.content) {
806 if (block.type === "tool_use") {
807 console.log(`[tool call] ${block.name}`, block.input);
808 }
809 }
810 } else if (message.type === "result" && message.subtype === "success") {
811 console.log(`Q: ${prompt}\nA: ${message.result}\n`);
812 }
813 }
814 }
815 ```
816</CodeGroup>
817
818## 次のステップ
819
820カスタムツールは非同期関数を標準インターフェースにラップします。このページのパターンを同じサーバーで混ぜることができます。単一のサーバーは、データベースツール、API ゲートウェイツール、および画像レンダラーを並べて保持できます。
821
822ここから:
823
824* サーバーが数十のツールに成長する場合は、[ツール検索](/ja/agent-sdk/tool-search)を参照して、Claude がそれらを必要とするまで読み込みを遅延させます。
825* 独自のツールを構築する代わりに、外部 MCP サーバー(ファイルシステム、GitHub、Slack)に接続するには、[MCP サーバーを接続する](/ja/agent-sdk/mcp)を参照してください。
826* どのツールが自動的に実行されるか、承認が必要かを制御するには、[許可を設定する](/ja/agent-sdk/permissions)を参照してください。
827
828## 関連ドキュメント
829
830* [TypeScript SDK リファレンス](/ja/agent-sdk/typescript)
831* [Python SDK リファレンス](/ja/agent-sdk/python)
832* [MCP ドキュメント](https://modelcontextprotocol.io)
833* [SDK 概要](/ja/agent-sdk/overview)