Claude API サーバーサイドツール実践ガイド — Web Search・Code Execution・Tool Searchで「検索→分析→回答」を1リクエストに凝縮する
Claude APIのtool_useを使ったことがあるなら、あの「ツール呼び出し → 結果を返す → また呼び出し」のクライアントサイドループに覚えがあるはずだ。だが2026年2月にGAとなったサーバーサイドツールは、そのループ自体が不要になる全く別のパラダイムだ。Web検索もPythonコード実行も1万ツールからの絞り込みも、Anthropicのサーバー側で自動完結する。
本記事では、3つのサーバーサイドツール(web_search / code_execution / tool_search)を体系的に整理し、Dynamic Filteringによるトークン削減、無料枠の活用法、そして「リサーチブリーフ自動生成」の実装例まで、APIアプリケーション開発者が今日から使える実践パターンを解説する。
サーバーサイドツールとは何か — クライアントサイドtool_useとの根本的な違い
従来のtool_use: 開発者がループを回す世界
従来のクライアントサイドtool_useでは、Claudeがツールを呼びたいときにstop_reason: "tool_use"でレスポンスが中断される。開発者はそのツール呼び出しを解析し、実際にツールを実行し、tool_resultとして結果を返し、再度APIを呼ぶ——というループを自前で実装する必要があった。
# クライアントサイドtool_use: 開発者がループを回す
import anthropic
client = anthropic.Anthropic()
messages = [{"role": "user", "content": "東京の天気を調べて"}]
while True:
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
tools=[{"name": "get_weather", "description": "天気を取得",
"input_schema": {"type": "object", "properties": {"city": {"type": "string"}}}}],
messages=messages
)
if response.stop_reason == "tool_use":
tool_block = next(b for b in response.content if b.type == "tool_use")
# 開発者が自分でツールを実行
result = call_weather_api(tool_block.input["city"])
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": [
{"type": "tool_result", "tool_use_id": tool_block.id, "content": result}
]})
else:
break # end_turnで完了
サーバーサイドツール: Anthropicが実行を完結させる世界
サーバーサイドツールでは、このループが丸ごと不要になる。toolsにサーバーサイドツールを指定するだけで、Anthropicのサーバー側でツール実行が完結し、結果が含まれた状態でレスポンスが返る。
# サーバーサイドツール: 1リクエストで完結
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
tools=[{"type": "web_search_20260209", "name": "web_search", "max_uses": 3}],
messages=[{"role": "user", "content": "Claude APIのサーバーサイドツールについて教えて"}]
)
# ループ不要。responseに検索結果を踏まえた回答が含まれている
IDプレフィックスで見分ける — toolu vs srvtoolu
レスポンスに含まれるツール呼び出しIDを見れば、どちらの実行形態か一目で判別できる。クライアントサイドはtoolu_プレフィックス、サーバーサイドはsrvtoolu_プレフィックスだ。レスポンス解析のロジックを組む際にこの区別は重要になる。
なお、長時間の処理ではstop_reason: "pause_turn"が返ることがある。この場合はレスポンスのcontentをそのまま次のリクエストのmessagesに追加して継続すればよい。
2026年2月17日のGA化により、anthropic-betaヘッダーは不要になった。ただしAmazon BedrockおよびGoogle Vertex AIではcode_executionが非対応である点に注意してほしい。
Web Search — Dynamic Filteringで精度とコストを両立する
基本の使い方: toolsにwebsearch20260209を追加するだけ
最新バージョンはweb_search_20260209だ。旧版web_search_20250305との最大の違いはDynamic Filtering対応にある。混同しやすいのでバージョンIDは正確に指定しよう。
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=2048,
tools=[{
"type": "web_search_20260209",
"name": "web_search",
"max_uses": 5,
"allowed_domains": ["docs.anthropic.com", "github.com"], # 検索スコープ制御
}],
messages=[{"role": "user", "content": "Claude APIのweb searchツールの料金体系を教えて"}]
)
# citationsの抽出
for block in response.content:
if block.type == "text":
print(block.text)
if hasattr(block, "citations") and block.citations:
for cite in block.citations:
print(f" 出典: {cite.url} - {cite.title}")
allowed_domainsやblocked_domainsで検索対象をドメイン単位で制御できる。RAGアプリで特定のドキュメントサイトだけを検索ソースにしたい場合に便利だ。
Dynamic Filtering: 内部code_executionで検索結果をフィルタリング
Dynamic Filteringはweb_search_20260209で追加された目玉機能だ。仕組みとしては、検索結果をサーバー内部のcode_execution(Python)で解析・フィルタリングしてから回答生成に渡す。これにより、不要な検索結果がコンテキストに入らなくなる。
ベンチマーク結果は印象的で、BrowseCompスコアがSonnetで33.3% → 46.6%(+13.3pt)、Opusで45.3% → 61.6%(+16.3pt)に向上し、入力トークンも24%削減されている。
ただし注意点がある。Opusモデルではより詳細な分析を行う傾向があり、出力トークンが増加してコスト増に転じる場合がある。Sonnetでの利用が最もコストパフォーマンスに優れる。
料金設計とcited_textのトークンカウント外の恩恵
Web Searchの料金は$10/1,000検索 + 標準トークン料金だ。ここで見落としがちなのが、レスポンスに含まれるcited_text、title、urlフィールドはトークンカウントの対象外という仕様だ。出典情報をリッチに返してもトークン料金に上乗せされない——正直、これを知ったときはかなり得した気分になった。
Code Execution — 無料で使えるサンドボックスPython環境
web_search併用で完全無料になる条件
code_execution_20260120が最新バージョンで、対応モデルはOpus 4.5+とSonnet 4.5+に限られる。
最大の注目ポイントは、web_search_20260209またはweb_fetch_20260209と併用するとcode_executionが完全無料になることだ。これはDynamic Filteringと同じインフラを共有しているためで、検索系ツールを使うアプリでは追加コストなしでPython実行環境を手に入れられる。
スタンドアロン利用の場合は月1,550時間の無料枠があり、超過分は$0.05/時間だ。
REPL永続化とProgrammatic Tool Callingの活用
同一ターン内ではREPLの状態が保持される。つまり最初のコード実行で定義した変数やインポートしたライブラリを、後続のコード実行で参照できる。データの段階的な加工に適した設計だ。
# web_search + code_execution: 検索→分析を1リクエストで
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=4096,
tools=[
{"type": "web_search_20260209", "name": "web_search", "max_uses": 5},
{"type": "code_execution_20260120", "name": "code_execution"},
],
messages=[{"role": "user",
"content": "主要LLMプロバイダのAPI料金を検索し、入出力トークン単価を比較表にまとめて"}]
)
# Claudeが自動的にWeb検索→データ抽出→Python分析→表形式出力を実行
# code_execution料金はweb_search併用により無料
allowed_callers設定を使えば、code_executionが自動トリガーされる条件をプログラマティックに制御することも可能だ。
Tool Search — 1万ツールから必要なものだけを選ばせる
regexとBM25: 2つのバリアントの使い分け
大量のツール定義をtoolsに渡すとコンテキスト消費が膨大になる。100個のツールを定義するだけで数万トークンを消費し、毎リクエストのコストに直結する。Tool Searchはこの問題をサーバーサイドで解決する。
2つのバリアントがある:
- regex版(
tool_search_tool_regex_20251119): ツール名・説明のパターンマッチ。高速だが柔軟性に劣る - BM25版(
tool_search_tool_bm25_20251119): 意味的な関連度スコアリング。多数ツールからの選択に強い
# BM25版: 100個のツールから必要なものだけを選択
tools = [generate_tool_definition(i) for i in range(100)] # 大量のツール定義
tools.append({
"type": "tool_search_tool_bm25_20251119",
"name": "tool_search",
})
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "ユーザーの注文履歴を取得して"}]
)
# Claudeがtool_searchで関連ツールを絞り込み、必要なものだけを呼び出す
トークン削減効果
実測で55Kトークン → 8.7Kトークン(85%削減)という数値が報告されている。最大10,000ツールまで対応しており、MCPサーバー統合やプラグインシステムなど、ツール数が動的に増えるアーキテクチャでの恩恵が大きい。
設計判断フロー — サーバーサイド vs クライアントサイド、どちらを選ぶか
判断フローチャート: 4つの分岐条件
すべてをサーバーサイドツールに置き換えるべきかというと、そうではない。以下の判断基準で使い分けるとよい。
- Web検索が必要か? →
web_search(サーバーサイド) - データ分析・計算が必要か? →
code_execution(サーバーサイド) - ツール定義が50個以上か? →
tool_search(サーバーサイド) - 自社DB・外部API・ファイルシステムとの連携が必要か? → カスタム
tool_use(クライアントサイド)
自社システムとの連携は、当然ながらAnthropicのサーバーからは実行できない。ここは引き続きクライアントサイドのtool_useが必要だ。
ハイブリッド構成: サーバーサイド + クライアントサイドの併用パターン
重要なのは、両者は同一リクエスト内で共存できることだ。
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=4096,
tools=[
# サーバーサイド: Web検索
{"type": "web_search_20260209", "name": "web_search", "max_uses": 3},
# サーバーサイド: コード実行
{"type": "code_execution_20260120", "name": "code_execution"},
# クライアントサイド: 自社DB検索
{"name": "search_products", "description": "自社商品DBを検索",
"input_schema": {"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"]}},
],
messages=[{"role": "user",
"content": "競合他社の最新価格をWeb検索し、うちの商品DBと比較して"}]
)
# web_searchはサーバーサイドで自動実行
# search_productsはstop_reason: tool_useで返るので開発者が処理
Claudeがサーバーサイドツールを先に実行し、その結果を踏まえてクライアントサイドのツール呼び出しを判断する——という流れが自然に成立する。ZDR(Zero Data Retention)環境ではallowed_callersの設定が追加で必要になる点だけ注意しておこう。
実装例: リサーチブリーフ自動生成エンドポイント
アーキテクチャ: 1リクエストで検索→分析→構造化出力
最後に、ここまでの知識を統合した実践的な実装例を示す。トピックを入力するとWeb検索 → 情報抽出 → Python分析 → 構造化レポートを1 APIコールで返すエンドポイントだ。
完全なコードとレスポンス例
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import anthropic
import json
app = FastAPI()
client = anthropic.Anthropic()
class ResearchRequest(BaseModel):
topic: str
max_searches: int = 5
class ResearchBrief(BaseModel):
summary: str
key_findings: list[str]
sources: list[dict]
SYSTEM_PROMPT = """あなたはリサーチアナリストです。与えられたトピックについて:
1. Web検索で最新情報を収集する
2. 収集した情報をPythonで整理・分析する
3. 以下のJSON形式で構造化レポートを出力する
出力形式:
{"summary": "要約(200字以内)", "key_findings": ["発見1", "発見2", ...], "data_analysis": "分析結果"}
"""
@app.post("/research", response_model=ResearchBrief)
async def generate_research_brief(req: ResearchRequest):
try:
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=8096,
system=SYSTEM_PROMPT,
tools=[
{"type": "web_search_20260209", "name": "web_search",
"max_uses": req.max_searches},
{"type": "code_execution_20260120", "name": "code_execution"},
],
messages=[{"role": "user",
"content": f"以下のトピックについてリサーチブリーフを作成してください: {req.topic}"}]
)
# pause_turn対応: 長時間実行時の継続処理
messages = [{"role": "user",
"content": f"以下のトピックについてリサーチブリーフを作成してください: {req.topic}"}]
while response.stop_reason == "pause_turn":
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": [{"type": "text", "text": "続けてください"}]})
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=8096,
system=SYSTEM_PROMPT,
tools=[
{"type": "web_search_20260209", "name": "web_search",
"max_uses": req.max_searches},
{"type": "code_execution_20260120", "name": "code_execution"},
],
messages=messages
)
# テキストブロックと出典の抽出
text_content = ""
sources = []
for block in response.content:
if block.type == "text":
text_content += block.text
if hasattr(block, "citations") and block.citations:
for cite in block.citations:
sources.append({
"url": cite.url,
"title": cite.title,
"cited_text": cite.cited_text
})
# JSON部分のパース
brief_data = json.loads(
text_content[text_content.index("{"):text_content.rindex("}") + 1]
)
return ResearchBrief(
summary=brief_data["summary"],
key_findings=brief_data["key_findings"],
sources=sources
)
except anthropic.APIError as e:
raise HTTPException(status_code=502, detail=f"Claude API error: {e.message}")
コスト面では、検索料金($10/1,000検索)+ 入出力トークン料金のみ。code_executionはweb_search併用により無料だ。本番運用では、max_tokensを余裕をもって設定すること、タイムアウトを長めに取ること(サーバーサイドツールの実行時間を含むため)を意識しておくとよい。
サーバーサイドツールは「APIの使い方が少し変わった」という話ではない。開発者がループを書いてツール結果を中継する設計パターンそのものが、検索・分析・大規模ツール選択という3つの頻出ユースケースで不要になった。特にweb_search + code_executionの併用は、コード実行が無料になるだけでなく、Dynamic Filteringによるトークン削減と精度向上を同時に得られる。
まだtool_useのクライアントサイドループだけでアプリを構築しているなら、まずはweb_searchの1行追加から試してほしい。検索 → 分析 → 回答が1リクエストに凝縮される体験は、APIアプリケーションの設計観を変えるはずだ。
