Claude Media
ClaudeのTypeScript SDKで実装するStreaming・Tool・MCPヘルパー

ClaudeのTypeScript SDKで実装するStreaming・Tool・MCPヘルパー

公式TypeScript SDKのStreamingヘルパー、Zod対応のToolヘルパー、MCPヘルパーの使い方をコード例で解説します。

TypeScript SDKの導入 — インストールと対応ランタイム

公式のTypeScript SDKはnpm install @anthropic-ai/sdkの一行で導入できます。TypeScript 5.0以上に対応し、実行環境はNode.js 20 LTS以降(非EOLに限る)・Deno 1.28.0以降・Bun 1.0以降・Cloudflare Workers・Vercel Edge Runtime・Jest 28以降("node"環境のみ、"jsdom"は非対応)・Nitro 2.6以降と幅広く動きます。React Nativeは今のところ非対応です。

ブラウザ実行はデフォルトで無効化されています。APIキーをクライアント側に持ち出すと第三者に抜き取られる危険があるためです。社内限定ツールや一時的なデバッグ用途など、キーの露出リスクを許容できる場面に限りdangerouslyAllowBrowser: trueで明示的に有効化します。

npm install @anthropic-ai/sdk

APIキーは環境変数ANTHROPIC_API_KEYから自動で読み込まれます。個人アカウントキーやサービスアカウントキーが複数のワークスペースにアクセスできる場合は、リクエストヘッダーanthropic-workspace-idでワークスペースを指定します。

const client = new Anthropic({
  apiKey: process.env["ANTHROPIC_API_KEY"] // デフォルト値なので省略可能
});
 
const message = await client.messages.create({
  max_tokens: 1024,
  messages: [{ role: "user", content: "Hello, Claude" }],
  model: "claude-opus-5"
});
 
for (const block of message.content) {
  if (block.type === "text") {
    console.log(block.text);
  }
}

ストリーミングとStreaming helpersで応答を逐次処理する

長い応答を待たずに処理を始めたいとき、SDKはServer-Sent Eventsによるストリーミングをサポートします。client.messages.create({ ..., stream: true })はイベントのasync iterableだけを返す軽量な経路で、最終メッセージをメモリ上に組み立てません。途中で打ち切りたければループをbreakするかstream.controller.abort()を呼びます。

イベント処理よりテキストだけ欲しい場合はclient.messages.stream()のStreaming helpersが便利です。on("text", ...)でテキスト断片を受け取りつつ、finalMessage()で完成したメッセージオブジェクトも取得できます。

const anthropic = new Anthropic();
 
const stream = anthropic.messages
  .stream({
    model: "claude-opus-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Say hello there!" }]
  })
  .on("text", (text) => {
    console.log(text);
  });
 
const message = await stream.finalMessage();
console.log(message);

イベント単位で処理を分岐したいだけならcreate({ stream: true })、テキスト取得と最終メッセージの両方が欲しいならmessages.stream()という使い分けです。

Tool helpersでZod / JSON Schemaからツールを定義する

ツール呼び出しのループを自分で書くと、Claudeの応答を受け取り→該当ツールを実行し→結果を返す、という往復を手動で管理することになります。TypeScript SDKのclient.beta.messages.toolRunner()はこの往復を自動化し、ZodスキーマまたはJSON Schemaで入力を定義するだけでループが完結します。

import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
import { z } from "zod";
 
const anthropic = new Anthropic();
 
const weatherTool = betaZodTool({
  name: "get_weather",
  inputSchema: z.object({
    location: z.string()
  }),
  description: "Get the current weather in a given location",
  run: (input) => {
    return `The weather in ${input.location} is foggy and 60°F`;
  }
});
 
const finalMessage = await anthropic.beta.messages.toolRunner({
  model: "claude-opus-5",
  max_tokens: 1000,
  messages: [{ role: "user", content: "What is the weather in San Francisco?" }],
  tools: [weatherTool]
});
 
console.log(finalMessage.content);

ツール実行中にエラーを返したいときは、プレーンなErrorではなくToolErrorを投げます。ToolErrorは画像などの構造化コンテンツを受け付けるため、「なぜ失敗したか」をスクリーンショット付きでモデルへ返すといった実装が可能です。プレーンなErrorを投げた場合はテキストのコンテンツブロックに自動変換されます。

import { ToolError } from "@anthropic-ai/sdk/lib/tools/BetaRunnableTool";
 
const screenshotTool = betaZodTool({
  name: "take_screenshot",
  inputSchema: z.object({ url: z.string() }),
  run: async (input) => {
    if (!isValidUrl(input.url)) {
      throw new ToolError(`Invalid URL: ${input.url}`);
    }
    const result = await takeScreenshot(input.url);
    if (result.error) {
      throw new ToolError([
        { type: "text", text: `Failed to load page: ${result.error}` },
        { type: "image", source: { type: "base64", data: result.screenshot, media_type: "image/png" } }
      ]);
    }
    return { type: "image", source: { type: "base64", data: result.screenshot, media_type: "image/png" } };
  }
});

MCP helpersでMCPサーバーとの往復を短くする

TypeScript SDKにはModel Context Protocol(MCP)サーバーと連携するための専用ヘルパーが用意されています。mcpToolsmcpMessagesmcpResourceToContentmcpResourceToFileの4関数が、MCP側の型をClaude API側の型へ変換し、定型的な変換コードを書かずに済ませます。

APIにはmcp_serversパラメータもあり、URLでアクセスできるリモートサーバーへClaudeを直接つなげます。ツールだけで足りるならこちらが手軽です。ローカルのMCPサーバーやプロンプト・リソースまで扱いたい、あるいは接続の細部を自分で制御したい場合はMCP helpersを使います。判断基準は単純で、接続先がURL経由のリモートサーバーかつツール呼び出しだけで完結するならmcp_servers、stdio接続のローカルサーバーを使う・プロンプトやリソースも取り込みたい・レスポンスの変換処理に手を加えたいのいずれかに該当すればMCP helpersを選びます。

import {
  mcpTools,
  mcpMessages,
  mcpResourceToContent,
  mcpResourceToFile
} from "@anthropic-ai/sdk/helpers/beta/mcp";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
 
const anthropic = new Anthropic();
const transport = new StdioClientTransport({ command: "mcp-server", args: [] });
const mcpClient = new Client({ name: "my-client", version: "1.0.0" });
await mcpClient.connect(transport);
 
// MCPのプロンプトをそのままMessages APIへ渡す
const { messages } = await mcpClient.getPrompt({ name: "my-prompt" });
await anthropic.beta.messages.create({
  model: "claude-opus-5",
  max_tokens: 1024,
  messages: mcpMessages(messages)
});
 
// MCPのツール一覧をtoolRunnerにそのまま渡す
const { tools } = await mcpClient.listTools();
const finalMessage = await anthropic.beta.messages.toolRunner({
  model: "claude-opus-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Use the available tools" }],
  tools: mcpTools(tools, mcpClient)
});

MCPのリソースはmcpResourceToContentでメッセージのコンテンツに、mcpResourceToFileでファイルアップロードの入力にそれぞれ変換できます。変換できない値(未対応のコンテンツ種別・MIMEタイプ、http/https以外のリソースリンクなど)を渡すとUnsupportedMCPValueErrorが送出されるので、try/catchで拾って原因を特定します。

Message Batchesとリトライ・タイムアウトの設定

client.messages.batches名前空間でMessage Batchesにも対応しています。基本の流れはbatches.create()でリクエストの配列を送り、processing_status"ended"になったらbatches.results()で結果を1件ずつ取り出すというものです。

const batch = await client.messages.batches.create({
  requests: [
    { custom_id: "my-first-request", params: { model: "claude-opus-5", max_tokens: 1024, messages: [{ role: "user", content: "Hello, world" }] } }
  ]
});
 
const results = await client.messages.batches.results(batch.id);
for await (const entry of results) {
  if (entry.result.type === "succeeded") {
    console.log(entry.result.message.content);
  }
}

接続失敗やAPIからの4xx/5xx応答はAPIErrorのサブクラスとして投げられます。接続エラー・408・409・429・500番台は指数バックオフ付きでデフォルト2回まで自動リトライされ、maxRetriesオプションでクライアント全体またはリクエスト単位に上書きできます。たとえばバッチ処理の下書き作成など失敗しても実害の小さいリクエストだけリトライ回数を減らし、決済確定のような一発勝負のリクエストだけ増やす、という使い分けがリクエスト単位の上書きで可能です。タイムアウトはデフォルト10分ですが、非ストリーミングかつmax_tokensが大きい場合は(60 * 60 * maxTokens) / 128000秒(最大60分)まで動的に延びます。長時間かかる非ストリーミングリクエストは、この見積もりで10分を超えると判断された時点でSDK側がエラーを出します。ストリーミングに切り替えるか、timeoutオプションを明示的に上書きすると回避できます。

ステータスエラー型
400エラー型BadRequestError
401エラー型AuthenticationError
403エラー型PermissionDeniedError
404エラー型NotFoundError
409エラー型ConflictError
422エラー型UnprocessableEntityError
429エラー型RateLimitError
500以上エラー型InternalServerError
N/Aエラー型APIConnectionError(接続自体が確立できない場合)

デフォルトではSDKがanthropic-versionヘッダーを2023-06-01に固定して送信します。独自の値で上書きすることも技術的には可能ですが、レスポンス型の解釈がSDKの想定とずれる可能性があるため推奨されません。

Auto-paginationとBeta機能へのアクセス

一覧系エンドポイントはページネーションされています。for await ... of構文を使えば、次ページの有無を自分で判定せずに全件を辿れます。

async function fetchAllMessageBatches() {
  const allMessageBatches = [];
  for await (const messageBatch of client.messages.batches.list({ limit: 20 })) {
    allMessageBatches.push(messageBatch);
  }
  return allMessageBatches;
}

1ページずつ手動で制御したい場合はpage.hasNextPage()page.getNextPage()を使います。全件を一括で辿らず、1ページ分を処理してユーザーの操作を待ってから次ページを取得する画面や、途中で打ち切り条件を判定してから続きを取るバッチ処理など、for awaitが向かない場面で使います。

ベータ機能はほとんどがclient.beta配下から使えます。有効化にはbetasフィールドへベータヘッダーの識別子を渡します。たとえばcontext editingを使う場合は次のようになります。

const client = new Anthropic();
const response = await client.beta.messages.create({
  model: "claude-opus-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Hello, Claude" }],
  betas: ["context-management-2025-06-27"]
});

ベータ機能は一般提供前の早期フィードバック用なので、利用可能な機能一覧は随時変わります。特定の機能が使えるかどうかは、実装前に公式のbuild with Claude overviewで確認するのが安全です。

Bedrock・Agent Platform・Foundryのクライアントを使い分ける

Amazon Bedrock・Google CloudのAgent Platform・Claude Platform on AWS・Microsoft Foundryへは、それぞれ専用パッケージのクライアントで接続します。Agent Platformは@anthropic-ai/vertex-sdkAnthropicVertex、Bedrockは@anthropic-ai/bedrock-sdkAnthropicBedrockMantle(新規プロジェクト向け)とAnthropicBedrock(既存のInvokeModel API利用アプリ向け)、Claude Platform on AWSは@anthropic-ai/aws-sdkAnthropicAws(ベータ、workspaceIdをコンストラクタか環境変数ANTHROPIC_AWS_WORKSPACE_IDで指定)、Foundryは@anthropic-ai/foundry-sdkAnthropicFoundryです。

パッケージは基本的にSemVerに従いますが、静的型だけへの変更・ドキュメント化されていない内部実装への変更・大多数の利用者に影響しない変更はマイナーバージョンとしてリリースされることがあります。破壊的変更の判定基準を把握しておくと、アップグレード時の差分確認が楽になります。

よくあるつまずき

  • ブラウザ実行の有効化忘れではなく誤有効化: dangerouslyAllowBrowserをプロダクション環境で有効にすると、APIキーがクライアントコードに露出したままデプロイされます
  • create({ stream: true })messages.stream()の混同: 前者はイベントのみのasync iterableでメモリ効率重視、後者はテキスト蓄積と最終メッセージ取得のための高レベルヘルパーです。用途に合わない方を選ぶと、不要なメモリ消費か扱いにくいイベント処理のどちらかを抱えます
  • mcp_serversパラメータとMCP helpersの取り違え: リモートMCPサーバーをURLでつなぐだけならmcp_serversで十分です。ローカルサーバーやリソース変換が必要な場面でMCP helpersを使わずに自前実装すると、変換コードが余分に増えます
  • 大きなmax_tokensを非ストリーミングで指定: 予想時間が10分を超えるとSDKがエラーを出す仕組みがあるため、長時間リクエストはstream: trueかタイムアウト上書きで対応します

まとめ

TypeScript SDKはnpm install @anthropic-ai/sdkで導入でき、TypeScript 5.0以上とNode.js 20 LTS以降を主な前提とします。応答を逐次処理したいだけならcreate({ stream: true })、テキスト取得と最終メッセージの両方が欲しいならStreaming helpers、ツール呼び出しの往復を自作したくなければZod / JSON Schema対応のTool helpers、というのが基本の使い分けです。MCPサーバーと連携する場面では専用のMCP helpersが変換コードを大きく減らします。

Ruby SDKとの実装上の違いを比較したい場合はClaudeのRuby SDKでツール実行ループとページネーションを実装する、料金・モデル選択を含むAPI全体の見取り図はAnthropic API完全ガイド、送信前にトークン数を見積もりたい場合はcount_tokensで送信前にトークン数を数える方法もあわせて参照してください。

この記事を共有:XはてブLinkedIn