---
title: "Vercel AI SDK v6の新機能｜ToolLoopAgent・needsApprovalの使い方とv7への移行【6.0.320対応】"
url: "https://www.issoh.co.jp/tech/details/10402/"
published: 2025-12-25
updated: 2026-10-31
categories: ["AIエージェント・MCP"]
publisher: "株式会社一創"
---

# Vercel AI SDK v6の新機能｜ToolLoopAgent・needsApprovalの使い方とv7への移行【6.0.320対応】

Vercel AI SDK v6（AI SDK 6）は2025年12月22日に正式版になった版で、ツールを呼びながら答えを出すエージェント用の `ToolLoopAgent` と、ツール実行の前に人の承認を挟む `needsApproval` が加わりました。2026年6月25日にAI SDK 7が出たため、いま `npm install ai` で入るのはv7です。**最新版（AI SDK 7）での書き方は[Vercel AI SDKの使い方｜AI SDK 7でNext.jsチャットとLangChain連携を実装](/tech/details/9551/)にまとめています。**

この記事はv6を使っている、またはv6のコードを読む必要がある人向けです。v6系の現行パッチ6.0.320（2026年10月30日公開）を入れ、コード例はTypeScript 7.0.2で型検査し、ToolLoopAgentと承認の流れはAPIキー不要のモックモデル（`MockLanguageModelV3`）で実行して出力を確かめました。

## まとめ｜v6を使うときに押さえる要点

- **v6はメンテナンス中の旧メジャー**。6.0.0のGAは2025-12-22、v7.0.0は2026-06-25。v6系にもパッチが出続けており、6.0.320が2026-10-30に公開されている。
- v6に留めるなら `npm install ai@ai-v6 @ai-sdk/react@ai-v6 @ai-sdk/openai@ai-v6` のように**全パッケージをdist-tagで揃える**。`ai@6` だけ固定して `@ai-sdk/react` をタグ無しで入れると、v7の `ai@7.0.159` が別に入って2つの版が混在する。
- `ToolLoopAgent` の停止条件の既定は `stepCountIs(20)`。一方 `generateText` は既定1ステップで、最初の応答がツール呼び出しだけなら `text` は空になる。
- 実行時の引数は `callOptionsSchema` ＋ `prepareCall`。日本語記事に出てくる `prepareCalls` は存在せず、型検査で TS2561 になる。
- 承認をクライアントから受け取る構成では `experimental_toolApprovalSecret` を設定する。設定しないと、偽造した承認メッセージでツールが実行される（6.0.320で実測）。

## AI SDK 6の位置づけとサポート状況

### リリース日とv5・v7との関係

npmレジストリの公開日時で見ると、5.0.0が2025-07-31、6.0.0が2025-12-22、7.0.0が2026-06-25です。v6がメジャー版になった理由について、公式の[AI SDK 6 Betaの告知](https://ai-sdk.dev/v5/docs/announcing-ai-sdk-6-beta)は、プロバイダとの接続仕様をv3 Language Model Specificationへ上げたためと説明しています。[正式版のリリースブログ](https://vercel.com/blog/ai-sdk-6)も「AI SDK 5と違い、多くの利用者にとって大きな破壊的変更は想定していない」としており、v5からの移行はv4→v5ほど重くありません。

| メジャー版    | x.0.0の公開日  | 2026-10-31時点の最終版 | npmのdist-tag | Node.js要件   |
| -------- | ---------- | ---------------- | ------------ | ----------- |
| AI SDK 5 | 2025-07-31 | 5.0.283          | `ai-v5`      | 18以上        |
| AI SDK 6 | 2025-12-22 | 6.0.320          | `ai-v6`      | 18以上        |
| AI SDK 7 | 2026-06-25 | 7.0.159          | `latest`     | 22以上（ESM専用） |

### v6で追加された機能の一覧

リリースブログの章立てから、v6で入った機能を整理すると次のとおりです。安定性の列は、名前に `experimental_` が付くか、ドキュメントにexperimentalの注記があるかで判定しています。

| 機能          | 主なAPI・パッケージ                       | 安定性          |
| ----------- | --------------------------------- | ------------ |
| エージェント      | `ToolLoopAgent`・`Agent`インターフェース   | 安定           |
| 実行時の引数      | `callOptionsSchema`・`prepareCall` | 安定           |
| ツール実行の承認    | `needsApproval`                   | 安定           |
| 承認の署名検証     | `experimental_toolApprovalSecret` | experimental |
| ツール併用の構造化出力 | `Output.object` など                | 安定           |
| MCPクライアント   | `@ai-sdk/mcp` の `createMCPClient` | 安定           |
| DevTools    | `@ai-sdk/devtools`                | experimental |
| リランキング      | `rerank`                          | 安定           |
| 画像編集        | `generateImage` の参照画像             | 安定           |
| LangChain連携 | `@ai-sdk/langchain`（作り直し）         | 安定           |

ツール定義では、厳密なスキーマ検証をツールごとに有効にする `strict` と、入力例を渡す `inputExamples`（ネイティブ対応はAnthropicのみ）が入りました。モデルへ返す内容をツールの戻り値と分ける `toModelOutput` はv5からありますが、v6では引数が `{ output }` のオブジェクトに変わりました。

## v6のインストールとバージョン固定

### dist-tag ai-v6による関連パッケージの統一

AI SDKは本体の `ai` と、UIフック・プロバイダ・DevToolsなどが別パッケージです。v6に留めるときは、すべてを `ai-v6` タグで指定します。

```
npm install --save-exact ai@ai-v6 @ai-sdk/react@ai-v6 @ai-sdk/openai@ai-v6 zod
```

空のプロジェクトで `npm install ai@6 @ai-sdk/react react@19` と、本体だけ版を指定して試すと、`npm ls ai` の結果は次のようになりました。`@ai-sdk/react` のタグ無しは4.0.163（v7用）で、依存として `ai@7.0.159` を持ち込みます。

```
+-- @ai-sdk/react@4.0.163
| `-- ai@7.0.159
`-- ai@6.0.320
```

この状態ではサーバー側はv6、`useChat` 側はv7の型とストリーム形式で動きます。`ai-v6` タグで揃えた場合は `@ai-sdk/react@3.0.323` と `@ai-sdk/openai@3.0.135` が入り、`ai@6.0.320` は1つにまとまりました（deduped）。

### 移行ガイドのai@latestに注意

v6同梱の移行ガイド（v5→v6）は、更新コマンドの例として `pnpm install ai@latest @ai-sdk/react@latest @ai-sdk/openai@latest` を載せています。v6公開時はこれでv6が入りましたが、2026年6月25日以降はv7が入ります。v5からv6へ上げる途中でこのコマンドを写すと、v6を飛ばしてv7になります。codemodも同様に、v5→v6だけを当てたいときは `npx @ai-sdk/codemod v6` を使います（`upgrade` はv4以降の全codemodを当てます）。

## ToolLoopAgentによるツール呼び出しループの実装

`ToolLoopAgent` は、モデル・指示・ツールを一度設定し、モデルの応答にツール呼び出しがある限り「ツール実行→結果をモデルへ返す」を繰り返すクラスです。v5の `Experimental_Agent` の後継で、v6.0.320でも互換のため `Experimental_Agent` はexportされています。

```
import { ToolLoopAgent, tool, stepCountIs } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';

const weather = tool({
  description: '都市の気温を返す',
  inputSchema: z.object({ city: z.string() }),
  execute: async ({ city }) => ({ city, tempC: 21 }), // 動作説明用の固定値。実際の気温は取得しない
});

export const weatherAgent = new ToolLoopAgent({
  model: openai('gpt-5.6'),
  instructions: '気温を調べてから日本語で答える',
  tools: { weather },
  stopWhen: stepCountIs(5),
});

const result = await weatherAgent.generate({ prompt: '東京の気温は？' });
console.log(result.text, result.steps.length);
```

モックモデルで「1回目はツール呼び出し、2回目は文章」を返すようにして実行すると、エージェントは2ステップで「東京は21度です」を返しました。同じモデルとツールを `stopWhen` 無しの `generateText` に渡すと1ステップで止まり、`text` は空文字でした。停止条件の既定値は、`ToolLoopAgent` が `stepCountIs(20)`、`generateText`・`streamText` が1ステップです。v6のツール解説ページには `stepCountIs(count)` の説明に「default: stepCountIs(20)」と書かれていますが、これはエージェントの既定値で、`generateText` には当てはまりません。

### callOptionsSchemaとprepareCallによる呼び出し単位の指示変更

ユーザー名や契約プランなど、呼び出しのたびに変わる値は `callOptionsSchema` で型を決め、`prepareCall` で指示やモデル設定に反映します。

```
const supportAgent = new ToolLoopAgent({
  model: openai('gpt-5.6'),
  callOptionsSchema: z.object({
    userName: z.string(),
    plan: z.enum(['free', 'pro']),
  }),
  prepareCall: ({ options, ...rest }) => ({
    ...rest,
    instructions: `${options.userName}さん（${options.plan}プラン）に回答する`,
  }),
});

await supportAgent.generate({
  prompt: '請求書の再発行方法は？',
  options: { userName: '佐藤', plan: 'pro' },
});
```

プロパティ名は単数形の `prepareCall` です。v6リリース直後の日本語解説には `prepareCalls` と書いたものがあり、そのまま写すと `error TS2561: ... 'prepareCalls' does not exist in type 'ToolLoopAgentSettings<...>'. Did you mean to write 'prepareCall'?` になります。

また6.0.171（2026-04-30）より前は、`callOptionsSchema` が型だけで実行時には検証されていませんでした（CHANGELOGの「enforce callOptionsSchema at runtime」）。6.0.320で `plan: 'enterprise'` を渡すと `AI_TypeValidationError` で止まり、モデルは呼ばれません。スキーマで入力を制限している前提のコードなら、6.0.171以上に上げてください。

### Output.objectによるツール結果の構造化出力

v5までは、ツールを使うループは `generateText`、型付きのJSONを返す専用APIは `generateObject` でしたが、`generateText` にも実験的な `experimental_output` がありました。v6では `generateText` に `output` を渡すと、ツールを呼んだ後の最終応答をスキーマで検証して受け取れます。`generateObject`・`streamObject` はv6で非推奨になりました。

```
import { generateText, Output, stepCountIs } from 'ai';

const { output } = await generateText({
  model: openai('gpt-5.6'),
  tools: { weather },
  stopWhen: stepCountIs(5),
  output: Output.object({
    schema: z.object({ city: z.string(), tempC: z.number(), advice: z.string() }),
  }),
  prompt: '大阪の気温と服装のアドバイス',
});
console.log(output.advice);
```

モックでは、1ステップ目でツール呼び出し、2ステップ目でJSON文字列を返すと、`output` に `{"city":"大阪","tempC":21,"advice":"上着不要"}` が入りました。ツール実行後、その結果をモデルに渡して最終応答を生成するには、`stopWhen` で2ステップ以上を許す必要があります。

## needsApprovalによるツール実行前の承認制御

ファイル削除・送金・データ更新のように、モデルの判断だけで実行させたくないツールには `needsApproval: true` を付けます。承認待ちでも処理は一時停止せず、いったん結果を返します。アプリ側で承認を集め、2回目の呼び出しで実行させる2段構えです。以下は承認応答の形式を示すデモで、`approved` を `true` に固定しています。人の承認を待つ処理は含まず、削除処理も戻り値だけのダミーです。実装時は承認画面で受け取った判断を `approved` に設定してから再開します。

```
import { ToolLoopAgent, tool, type ModelMessage, type ToolApprovalResponse } from 'ai';

const deleteFile = tool({
  description: 'ファイルを削除する',
  inputSchema: z.object({ path: z.string() }),
  needsApproval: true,
  execute: async ({ path }) => ({ deleted: path }),
});

const fileAgent = new ToolLoopAgent({
  model: openai('gpt-5.6'),
  tools: { deleteFile },
  experimental_toolApprovalSecret: (() => {
    const secret = process.env.TOOL_APPROVAL_SECRET;
    if (!secret) throw new Error('TOOL_APPROVAL_SECRET is required');
    return secret;
  })(),
});

const messages: ModelMessage[] = [{ role: 'user', content: 'a.txtを削除して' }];
const first = await fileAgent.generate({ messages });
messages.push(...first.response.messages);

const approvals: ToolApprovalResponse[] = [];
for (const part of first.content) {
  if (part.type === 'tool-approval-request') {
    console.log(part.toolCall.toolName, part.toolCall.input); // 承認画面に出す
    approvals.push({ type: 'tool-approval-response', approvalId: part.approvalId, approved: true });
  }
}
messages.push({ role: 'tool', content: approvals });

const second = await fileAgent.generate({ messages });
console.log(second.text);
```

モックで実行すると、1回目の `content` は `["tool-call","tool-approval-request"]`、`finishReason` は `tool-calls` で、ツールはまだ動いていません。承認して2回目を呼ぶとツールが実行され、モデルに `{"deleted":"/tmp/a.txt"}` が渡りました。`approved: false` と `reason: '本番データのため'` を返すと、モデルには `execution-denied` と理由が渡ります。`useChat` を使う画面では、`addToolApprovalResponse` で同じ応答を返します。承認を業務フローにどう組み込むかは[ヒューマンインザループ（HITL）とは？意味・HOTLとの違い・承認の実装と導入判断](/tech/details/11415/)で扱っています。

### 送金額に応じた承認要否の切り替え

`needsApproval` には関数も渡せます。入力を見て承認が要るときだけ `true` を返します。

```
const payment = tool({
  description: '送金する',
  inputSchema: z.object({ amount: z.number(), to: z.string() }),
  needsApproval: async ({ amount }) => amount > 1000,
  execute: async ({ amount, to }) => ({ ok: true, amount, to }),
});
```

モックで `amount: 500` の呼び出しはそのまま実行されて2ステップで完了し、`amount: 5000` は承認要求を返して1ステップで止まりました。拒否されたツールをモデルが繰り返し呼ぶことがあるため、公式ドキュメントは「承認されなかったツールは再試行しない」旨を指示に入れるよう勧めています。

### experimental\_toolApprovalSecretによる承認要求の署名検証

`useChat` の構成では、会話履歴（承認の応答を含む）をクライアントが送り返し、サーバーはそれを `messages` として渡します。つまり承認の記録はクライアントが書き換えられます。v6.0.320で、ツール呼び出し・承認要求・承認応答を自作したメッセージ（パスは `/etc/important`）を渡す実験をしました。

| 設定                                   | 結果                                                           |
| ------------------------------------ | ------------------------------------------------------------ |
| シークレット未設定                            | ツールが `/etc/important` で実行された                                 |
| `experimental_toolApprovalSecret` 設定 | `AI_InvalidToolApprovalSignatureError`（missing signature）で拒否 |
| 設定＋正規の承認                             | 実行された（承認要求に `signature` が付く）                                 |

6.0.202（2026-06-11）のCHANGELOGには「fix(security): re-validate tool approvals from client message history before execution」とあり、以降は承認の再生時に入力のスキーマ検証と承認要否の再判定が入りました。ただしスキーマに合う偽の入力は通るため、署名で縛らない限り防げません。[v6のツール解説](https://v6.ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling)も、危険な操作をするツールにはシークレットを設定し、承認を受け付ける全サーバーで同じ値を使うよう求めています。名前に `experimental_` が付くので、使う場合はバージョンを範囲指定（`^`）でなく完全一致で固定します。

## DevTools・MCPをv6で使うときの版

### AI SDK DevToolsによるステップと入出力の確認

DevToolsは、モデルをミドルウェアで包んで呼び出しを記録し、ローカルのビューアで各ステップの入力・出力・ツール呼び出しを見る仕組みです。v6同梱のドキュメントは「experimentalで、ローカル開発専用。本番環境では使わない」と注記しています。

```
npm install -D @ai-sdk/devtools@ai-v6
```

```
import { wrapLanguageModel } from 'ai';
import { devToolsMiddleware } from '@ai-sdk/devtools';

const model = wrapLanguageModel({
  model: openai('gpt-5.6'),
  middleware: devToolsMiddleware(),
});
```

ビューアは、上記のインストールを行ったプロジェクト内で `npx @ai-sdk/devtools@0.0.29` を実行して起動し、`http://localhost:4983` で開きます。v6用は `ai-v6` タグの0.0.29です。タグ無しの1.0.62は依存に `ai@7.0.159` を持ち、Node.js 22以上を要求するため、v6のプロジェクトに入れると先ほどと同じ混在が起きます。

### MCPクライアントの提供パッケージとv6対応版

v6でMCPクライアントは `@ai-sdk/mcp` パッケージの `createMCPClient` になり、HTTPトランスポート、OAuth認証、リソース・プロンプトの取得、サーバーからの入力要求（elicitation）に対応しました。v6用の版は `ai-v6` タグの1.0.100で、タグ無しは2.0.88（Node.js 22以上）です。TypeScriptでMCPサーバーを自作する側の注意点は[MCP TypeScript SDKの脆弱性：2026年10月の7件の対象版と更新後に要る設定変更](/tech/details/19021/)を参照してください。

## v6のパッチで変わった挙動

v6系は「6.0.x」のまま300回以上パッチが出ており、その中で既定の挙動が変わったものがあります。`^6.0.0` で指定していても、lockファイルが古ければ修正前の版で動いています。`npm ls ai` で実際の版を確認し、下の修正を含む6.0.203以上（できれば6.0.x の最終版）へ上げてください。

| 版（公開日）              | 変更                                        |
| ------------------- | ----------------------------------------- |
| 6.0.170（2026-04-29） | messages内のsystemメッセージに警告                  |
| 6.0.171（2026-04-30） | `callOptionsSchema` を実行時に検証               |
| 6.0.188（2026-05-21） | `ToolLoopAgent` に `allowSystemInMessages` |
| 6.0.202（2026-06-11） | 承認の再生時に入力と承認要否を再検証                        |
| 6.0.203（2026-06-12） | UIストリームのエラー詳細を既定で秘匿                       |
| 6.0.203（2026-06-12） | ダウンロード時のSSRF対策を強化                         |

6.0.203のエラー秘匿は、`toUIMessageStream()` の `onError` を省略したとき、それまでサーバー例外の文字列（内部ホスト名やパスを含みうる）をそのままクライアントへ流していた問題の修正です。以後は「An error occurred.」だけが送られます。画面に個別のエラー案内が必要な場合は、`onError` で公開してよい文言だけを返し、内部ホスト名・パス・例外の詳細はサーバーログに残します。

systemメッセージの扱いは版で異なります。6.0.320で `messages` に `role: 'system'` を入れると、`generateText` も `ToolLoopAgent` も処理は続き、「プロンプトインジェクションの危険がある」という警告が出るだけでした。v7では同じ入力が `AI_InvalidPromptError` で止まります。v6のうちに、システムプロンプトを `system`（エージェントでは `instructions`）へ移しておくと、v7へ上げたときに止まりません。

## v7への移行判断と主な差分

### v7で名前が変わるAPI

v7の移行ガイドに載っている変更のうち、v6のコードで出番が多いものを抜き出しました。多くは改称で、`npx @ai-sdk/codemod v7` がまとめて書き換えます。

| v6の書き方                               | v7の書き方                          | v7での扱い     |
| ------------------------------------ | ------------------------------- | ---------- |
| `stepCountIs(n)`                     | `isStepCount(n)`                | 旧名は別名で残る   |
| `system`                             | `instructions`                  | 旧名は非推奨     |
| `onFinish`・`onStepFinish`            | `onEnd`・`onStepEnd`             | codemodで改称 |
| `experimental_telemetry`             | `telemetry`                     | codemodで改称 |
| `result.fullStream`                  | `result.stream`                 | codemodで改称 |
| ツールの `needsApproval`                 | 呼び出し側の `toolApproval`           | 非推奨        |
| `result.toUIMessageStreamResponse()` | `createUIMessageStreamResponse` | 非推奨        |
| messages内のsystem                     | 同じ                              | 警告からエラーへ   |
| Node.js 18以上・CJS可                    | Node.js 22以上・ESM専用              | 要件変更       |

v7の型定義では、ツール定義の `needsApproval` に「Tool approval is handled on a generateText / streamText level now」という@deprecatedの注記が付いています。承認の設計はツール単位から呼び出し単位へ移るため、承認画面を作り込んだアプリほど移行の手間がかかります。v7での具体的な書き方は[Vercel AI SDKの使い方｜AI SDK 7でNext.jsチャットとLangChain連携を実装](/tech/details/9551/)に載せています。

### v6に留める場面と移行する場面

新規開発はv7で始めてください。v6で書く場合は、将来の移行で承認・終了コールバック・システムプロンプトに使うAPIを見直す必要があります。ただし、v7でも非推奨の互換APIとして残るものがあり、すべての即時書き換えが必須ではありません。

既存のv6アプリは、Node.js 22以上とESMの要件を満たし、必要なv7機能や移行後の保守負担を評価できた段階で移行します。逆に、Node.js 20以下のサーバーで動いている、CommonJSの `require('ai')` が残っている、`needsApproval` を前提にした承認画面がある、のどれかに当てはまるなら、まずv6系の最終パッチへ上げて `ai-v6` タグで固定し、ランタイムとモジュール形式を先に整えるほうが安全です。v6系にもパッチは出続けています。移行の優先度は、利用中の版に影響する脆弱性とv6への修正提供状況を確認したうえで、v7固有の機能が必要かどうかも含めて判断します。エージェントの設計から相談したい場合は、弊社の[AIエージェント開発](https://www.issoh.co.jp/service/ai/agent/)でも承っています。

## よくある質問

### Vercel AI SDK v6はいつリリースされましたか？

6.0.0は2025年12月22日にnpmで公開されました。2026年6月25日にv7.0.0が出たため最新版ではありませんが、v6系のパッチは続いており、2026年10月30日に6.0.320が公開されています。

### ToolLoopAgentとExperimental\_Agentの違いは何ですか？

`ToolLoopAgent` はv5の `Experimental_Agent` を正式化したクラスで、指示は `instructions`、停止条件の既定は `stepCountIs(20)` です。6.0.320でも `Experimental_Agent` は互換のためexportされていますが、新しく書くコードでは `ToolLoopAgent` を使います。

### prepareCallsというAPIはありますか？

ありません。正しくは `callOptionsSchema` と `prepareCall`（単数形）の組み合わせです。`prepareCalls` と書くとTypeScriptが TS2561 を出し、型検査をしないJavaScriptでは設定が無視されます。

### AI SDK DevToolsは本番環境で使えますか？

使えません。公式ドキュメントがexperimentalかつローカル開発専用と明記しています。v6で使うときは `@ai-sdk/devtools@ai-v6`（0.0.29）を開発依存に入れ、本番のビルドではミドルウェアを外します。

### v6からv7へはどう移行しますか？

Node.js 22以上とESMの環境を用意し、`npx @ai-sdk/codemod v7` で改称をまとめて当てます。codemodで直らないのは承認（`needsApproval` から `toolApproval` へ）とレスポンス生成（`createUIMessageStreamResponse` へ）の書き換えで、これは手で直します。

## 関連記事

- [Vercel AI SDKの使い方｜AI SDK 7でNext.jsチャットとLangChain連携を実装](/tech/details/9551/)
- [ヒューマンインザループ（HITL）とは？意味・HOTLとの違い・承認の実装と導入判断](/tech/details/11415/)
- [AIエージェントの作り方：ツール定義からループ・承認・トレースまでコードで組む](/tech/details/17571/)
- [Smooth Text Streamingとは？AI SDK smoothStreamの使い方と日本語の分割設定](/tech/details/8440/)
- [TanStack AIとは？使い方・対応プロバイダーとVercel AI SDKとの違い](/tech/details/10501/)

---

出典: [Vercel AI SDK v6の新機能｜ToolLoopAgent・needsApprovalの使い方とv7への移行【6.0.320対応】](<https://www.issoh.co.jp/tech/details/10402/>)（株式会社一創）
