Vercel AI SDK 6の使い方を、TypeScriptの実装コードつきで2026年最新版として体系化した。Vercel AI SDK 6は、OpenAIやAnthropic、Googleなどのモデルを統一APIで扱い、エージェント・MCP連携・ツール承認・チャットUIまでをTypeScriptだけで実装するためのOSSツールキットだ。本記事ではToolLoopAgentによるエージェント構築から、@ai-sdk/mcpでのMCP連携、needsApprovalによるツール承認、DevToolsでのデバッグ、Next.jsチャットボット、v5からの移行までを公式blogとdocsの一次情報をもとに通しで解説する。

GitHubスター数は24.7Kを超え、メジャーバージョン6の正式リリースで「TypeScriptでAIエージェントを作るならまずこれ」という標準的な選択肢になりつつある。LangChain.jsのように抽象を厚く積むのではなく、薄い土台と型安全を武器にしている点が最大の特徴だ。

AIエージェントそのものの仕組み・種類・代表的フレームワークから押さえたい方は AIエージェントとは?仕組み・種類・代表的OSSフレームワークを初心者向けに解説【2026年版】 をご覧ください。

30秒で理解するVercel AI SDK 6

まず要点だけ先に押さえる。細部はこのあとのセクションで順に掘り下げる。

・Vercel AI SDK 6はTypeScript向けのAIツールキット。aiパッケージを中心に、OpenAI・Anthropic・Googleなどを統一APIで切り替えられる
・目玉はToolLoopAgent。プロンプト→ツール実行→結果追加→再実行のループを自動で回し、エージェント実装のボイラープレートを大きく減らす
・MCP(Model Context Protocol)が@ai-sdk/mcpとして安定版に。HTTP/stdioトランスポートとOAuth、Resources、Promptsに対応
needsApprovalでツール実行前の人間承認を、コードなしでUIに組み込める。危険なコマンドだけ承認をはさむ制御が標準化された
devToolsMiddleware()npx @ai-sdk/devtoolsで、入力・出力・トークン使用量・タイミングを可視化できる
・v5からの移行はnpx @ai-sdk/codemod v6で大半が自動。メジャー更新ながら破壊的変更は意図的に抑えられている

この記事のポイント
  • AI SDK 6の核心は「エージェント・MCP・承認・観測を一つのTypeScript APIに束ねた」こと。フロントのチャットUIまで地続きで書ける
  • エージェントはnew ToolLoopAgent({...})で作り、stopWhenで停止条件、prepareStepでステップごとの設定変更を制御する
  • MCPはcreateMCPClientclient.tools()で取得し、そのままagentに渡すだけ。承認はneedsApproval一行で有効化
  • v5資産はcodemodで大半が移行可能。LangChain.js・Microsoft Agent Frameworkとの棲み分けは「TypeScript完結度」で考えると分かりやすい

Vercel AI SDK 6とは(v5からの変化)

Vercel AI SDK 6とは、Next.jsの開発元であるVercelが提供する、AIアプリとエージェントをTypeScriptで構築するためのOSSライブラリだ。モデルプロバイダごとに異なるAPIの差異を吸収し、generateTextstreamTextといった統一インターフェースで、OpenAI・Anthropic・Googleなどを同じコードのまま差し替えられる。ライセンスはApache-2.0で、Vercelの契約がなくても単体で動く。

Vercelというプラットフォーム全体(Next.js最適化・AI Gateway・BotID)を俯瞰したい方は Vercelとは?Next.js最適化サーバーレスからAI SDK・BotID・AI Gatewayまで2026年最新ガイド を先に読むと位置づけが掴みやすい。

v5から6への一番大きな変化は、エージェント機能が「自分でループを書く」段階から「クラスとして提供される」段階へ進んだことだ。v5でもツール呼び出しは可能だったが、複数ステップのループ管理やメッセージ配列の積み上げは開発者の責任だった。6ではToolLoopAgentがその定型処理を引き受ける。

もう一つの転換点はMCPの安定化だ。v5では実験的だったMCPサポートが、6では独立した@ai-sdk/mcpパッケージとして安定版になり、HTTPトランスポート上のOAuth認証フローや、サーバー側のResources・Promptsまで正式に扱えるようになった。これにより「外部ツール群をMCPサーバーとして立て、SDK側はクライアントとして繋ぐ」構成が現実的になった。

AI SDK 6の主な仕様(2026年6月時点)
・ライセンス: Apache-2.0(OSS)
・GitHubスター: 24.7K超
・主要パッケージ: ai(コア)/ @ai-sdk/react / @ai-sdk/anthropic / @ai-sdk/openai / @ai-sdk/google / @ai-sdk/mcp
・対応UI: React / Svelte / Vue / Angular
・対応ランタイム: Node.js / Cloudflare Workers / Deno / 各種エッジ
・v3 Language Model Specification準拠

このほか6では、generateObjectgenerateTextが統合され、ツール呼び出しのループの末尾で構造化出力(Output.object()Output.array()Output.choice()など)を返せるようになった。rerank()関数によるリランキング、generateImage()の画像編集対応、Anthropic・OpenAI・Google・xAIそれぞれのプロバイダ固有ツール(Memory Tool・Shell Tool・Google Maps Tool等)も追加されている。GitHub Copilotのネイティブ化のように、ツール側が「呼び出される前提」で設計に組み込まれていく流れと地続きだ(参考: GitHub Copilotがネイティブアプリ化、デフォルトモデルもPolarisへ——Build 2026の主戦場)。

アーキテクチャ|Core・UI・Toolsの構成

Vercel AI SDK 6を理解するうえで、内部が「3つの層」に分かれていると捉えると挙動の見通しが良くなる。サーバー側でモデルを叩くCore層、ブラウザ側でストリームを受けるUI層、そして外部能力を足すTools/MCP層だ。これらが一本のストリームで繋がる。

Core層はaiパッケージが担い、generateTextstreamTextgenerateObjectToolLoopAgentといったサーバー実行のエントリポイントを提供する。ここがプロバイダ抽象の本体で、モデル文字列を差し替えるだけで送り先を変えられる。UI層は@ai-sdk/reactuseChatなどで、Core層が吐くUIメッセージストリームを受け取り、message.partsを種類ごとに描き分ける。

Tools/MCP層は、tool()で定義したローカルツールと、@ai-sdk/mcpで繋いだMCPサーバー由来のツールの両方を指す。どちらも最終的には同じ「ツール」としてエージェントのループに供給される。次の図がデータの流れだ。

flowchart TD U["ブラウザ
useChat / message.parts"] -->|sendMessage| API["API Route
Next.js / Edge"] API --> Core["Core層
ToolLoopAgent / streamText"] Core -->|model 文字列| Prov["Provider
OpenAI / Anthropic / Google"] Core --> Tools["Tools層
tool() ローカル定義"] Core --> MCP["MCP層
@ai-sdk/mcp client.tools()"] MCP --> Srv["MCPサーバー
HTTP / stdio + OAuth"] Tools -->|needsApproval| Approve["ツール承認
approval-requested"] Approve -->|addToolApprovalResponse| U Core -->|UIMessageStream| API API -->|toUIMessageStreamResponse| U Core -.->|devToolsMiddleware| Dev["DevTools
localhost:4983"]

重要なのは、ローカルのtool()もMCP由来のツールも、承認フローも、すべて同じCore層のループに合流する点だ。だから「最初はローカルツールで作り、あとからMCPサーバーへ切り出す」「特定のツールにだけ承認をはさむ」といった改修が、UI側のコードをほとんど変えずに進められる。

ToolLoopAgent でエージェントを作る

ここからが実装の本丸だ。AI SDK 6でエージェントを作る推奨パターンはToolLoopAgentクラスを使う方法になる。まずは天気を調べてから摂氏に変換する、2ツール構成の最小エージェントを見る。

import { ToolLoopAgent, tool } from 'ai';
import { z } from 'zod';

const weatherAgent = new ToolLoopAgent({
  model: 'anthropic/claude-sonnet-4.5',
  tools: {
    weather: tool({
      description: 'Get the weather in a location (in Fahrenheit)',
      inputSchema: z.object({
        location: z.string().describe('The location to get the weather for'),
      }),
      execute: async ({ location }) => ({
        location,
        temperature: 72 + Math.floor(Math.random() * 21) - 10,
      }),
    }),
    convertFahrenheitToCelsius: tool({
      description: 'Convert temperature from Fahrenheit to Celsius',
      inputSchema: z.object({
        temperature: z.number().describe('Temperature in Fahrenheit'),
      }),
      execute: async ({ temperature }) => {
        const celsius = Math.round((temperature - 32) * (5 / 9));
        return { celsius };
      },
    }),
  },
});

ツールは3要素で定義する。descriptionがモデルへの用途説明、inputSchemaがZodで書く引数スキーマ(各フィールドに.describe()を付けるとモデルの精度が上がる)、executeが実際の処理を行う非同期関数だ。あとは実行するだけでよい。

const result = await weatherAgent.generate({
  prompt: 'What is the weather in San Francisco in celsius?',
});

console.log(result.text);   // エージェントの最終回答
console.log(result.steps);  // 途中で踏んだステップ

generate()を呼ぶと、ToolLoopAgentは内部で「モデル呼び出し→ツール実行→結果をメッセージに追加→再びモデル呼び出し」を、デフォルトで最大20ステップまで自動で繰り返す。result.stepsを見れば、どのツールをどの順で呼んだかを追跡できる。ストリーミングしたい場合はgenerate()の代わりにstream()を使う。

ループの止め方はstopWhenで制御する。組み込みの停止条件としてstepCountIs(count)(指定ステップ数で停止)・hasToolCall(toolName)(特定ツールが呼ばれたら停止)・isLoopFinished()(自然終了まで回す)が用意されている。デフォルトは安全弁としてのstepCountIs(20)だ。

import { ToolLoopAgent, stepCountIs } from 'ai';

const agent = new ToolLoopAgent({
  model: 'anthropic/claude-sonnet-4.5',
  tools: { /* ... */ },
  stopWhen: stepCountIs(50),
  // ステップごとに設定を差し替える
  prepareStep: async ({ stepNumber, steps }) => {
    if (stepNumber <= 2) {
      return { activeTools: ['search'], toolChoice: 'required' };
    }
    if (stepNumber <= 5) {
      return { activeTools: ['analyze'] };
    }
    return { activeTools: ['summarize'], toolChoice: 'required' };
  },
});

prepareStepは各ステップの直前に走るコールバックで、stepNumbermessagesstepsmodelを受け取れる。これを使うと「序盤は検索ツールだけ強制し、中盤は分析、終盤は要約に絞る」といったフェーズ制御や、messagesが長くなりすぎたら先頭と直近だけ残してコンテキストを圧縮する、といった調整ができる。自前で停止条件を書きたい場合は、stepsを見て真偽を返す関数をstopWhenに渡せばよい。

ToolLoopAgent と Agent インターフェース
AI SDK 6では「Agent」はクラスではなくインターフェースとして定義され直した。ToolLoopAgentはその標準実装で、サードパーティがDurableAgent(再開可能ワークフロー)のような独自実装を差し込める設計になっている。callOptionsSchemaprepareCallを使えば、リクエストごとにRAG文書を注入したりモデルを選んだりする型安全な引数渡しも可能だ。

MCP連携の実装

外部のツール群をMCPサーバーとして切り出し、SDK側からクライアントとして繋ぐのが6の定番構成だ。@ai-sdk/mcpcreateMCPClientでクライアントを作り、client.tools()で取得したツールをそのままエージェントやstreamTextに渡す。

MCPサーバーの仕組み・代表的なサーバー・自作手順そのものは MCPサーバーとは|仕組み・代表的なサーバー一覧・自作手順を2026年最新で解説 で詳しく扱っている。本セクションは「SDK側からどう繋ぐか」に絞る。

import { createMCPClient } from '@ai-sdk/mcp';
import { streamText } from 'ai';

const mcpClient = await createMCPClient({
  transport: {
    type: 'http',
    url: 'https://your-server.com/mcp',
    headers: { Authorization: 'Bearer my-api-key' },
    redirect: 'error', // SSRF対策
  },
});

const tools = await mcpClient.tools();

const result = await streamText({
  model: 'anthropic/claude-sonnet-4.5',
  tools,
  prompt: 'What is the weather in Brooklyn, New York?',
  onFinish: async () => {
    await mcpClient.close(); // リソースリーク防止に必須
  },
});

本番では上記のようにHTTPトランスポートを使う。@modelcontextprotocol/sdkStreamableHTTPClientTransportを直接渡す書き方もあり、OAuth認証が必要な場合はauthProviderを設定すればフローを自動で処理してくれる。ローカル開発ではStdioClientTransportでローカルプロセスのMCPサーバーに繋ぐ。

ツールは自動スキーマ探索(client.tools()で全件取得)のほか、型安全のために明示スキーマを定義することもできる。出力スキーマまで指定すれば、結果が構造化された型として返る。

import { z } from 'zod';

const tools = await mcpClient.tools({
  schemas: {
    'get-weather': {
      inputSchema: z.object({ location: z.string() }),
      outputSchema: z.object({
        temperature: z.number(),
        conditions: z.string(),
        humidity: z.number(),
      }),
    },
  },
});

6で安定化したMCPは、ツール呼び出しだけでなくResources(ファイルやDBレコードの公開・読み取り)、Prompts(引数つきの再利用可能テンプレート)、Elicitation(サーバーがユーザーへ追加入力を要求する仕組み)まで扱える。client.listResources()client.readResource({ uri })でリソースを読み、client.onElicitationRequest()でサーバー起点の入力要求に応答するハンドラを登録する形だ。

MCPクライアントは必ず閉じる
ストリーミング時はonFinish内で、非ストリーミング時はtry/finallyawait mcpClient.close()を呼ぶ。閉じ忘れると接続が残ってリソースリークの原因になる。HTTPトランスポートではredirect: 'error'を付けてSSRF(サーバーサイドリクエストフォージェリ)を防ぐのも忘れずに。

tool承認とDevTools

エージェントに任意コマンド実行やファイル削除のような「取り返しのつかない操作」を持たせるなら、実行前の人間承認は欠かせない。6ではこれがneedsApprovalフラグ一つで実現できる。

サーバー側のツール定義にneedsApproval: trueを付けると、モデルがそのツールを呼んだ瞬間にexecuteはすぐ走らず、approval-requested状態のツールパートがクライアントへ送られる。ユーザーが承認して初めて実行される。needsApprovalには真偽値だけでなく、入力に応じて判定する非同期関数も渡せる。

import { tool } from 'ai';
import { z } from 'zod';

const runShell = tool({
  description: 'Run a shell command',
  inputSchema: z.object({ command: z.string() }),
  // ls は自動実行、rm -rf は承認をはさむ
  needsApproval: async ({ command }) =>
    /\brm\b|\bsudo\b|>/.test(command),
  execute: async ({ command }) => runInSandbox(command),
});

クライアント側はuseChataddToolApprovalResponseで承認/拒否を返す。message.partsの中で対象ツールのパートを見つけ、stateapproval-requestedなら承認ボタンを描く。

'use client';

import { useChat } from '@ai-sdk/react';
import {
  DefaultChatTransport,
  lastAssistantMessageIsCompleteWithApprovalResponses,
} from 'ai';

export default function Chat() {
  const { messages, sendMessage, addToolApprovalResponse } = useChat({
    transport: new DefaultChatTransport({ api: '/api/chat' }),
    sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
  });

  return (
    <>
      {messages.map(m =>
        m.parts.map((part, i) => {
          if (part.type === 'tool-getWeatherInformation') {
            switch (part.state) {
              case 'approval-requested':
                return (
                  <div key={part.toolCallId}>
                    Get weather for {part.input.city}?
                    <button
                      onClick={() =>
                        addToolApprovalResponse({
                          id: part.approval.id,
                          approved: true,
                        })
                      }
                    >
                      Approve
                    </button>
                    <button
                      onClick={() =>
                        addToolApprovalResponse({
                          id: part.approval.id,
                          approved: false,
                        })
                      }
                    >
                      Deny
                    </button>
                  </div>
                );
              case 'output-available':
                return <div key={part.toolCallId}>{part.output}</div>;
              case 'output-denied':
                return <div key={part.toolCallId}>拒否されました</div>;
            }
          }
        }),
      )}
    </>
  );
}

lastAssistantMessageIsCompleteWithApprovalResponsessendAutomaticallyWhenに渡しておくと、最後のステップの承認がすべて揃った時点で自動的に次のメッセージが送られる。状態はapproval-requested(保留)・output-available(承認後の結果)・output-denied(拒否)の3つを描き分ければよい。

デバッグにはDevToolsが効く。モデルをdevToolsMiddleware()でラップしておき、npx @ai-sdk/devtoolsでビューアを起動するとhttp://localhost:4983で、入力パラメータ・出力内容・トークン使用量・所要時間・プロバイダの生データまで覗ける。エージェントが「なぜそのツールを呼んだのか」「どこでトークンを食っているのか」を追うときの第一手になる。

Next.jsチャットボット実装例

ここまでの部品を、Next.js App Routerの実用的なチャットボットに組み上げる。サーバーのAPI Routeはストリームを返すだけ、クライアントはuseChatで受けるだけ、というのが基本形だ。

// app/api/chat/route.ts
import { streamText, convertToModelMessages, type UIMessage } from 'ai';

export async function POST(req: Request) {
  const { messages }: { messages: UIMessage[] } = await req.json();

  const result = streamText({
    model: 'anthropic/claude-sonnet-4.5',
    messages: await convertToModelMessages(messages),
  });

  return result.toUIMessageStreamResponse();
}

クライアントはuseChatからmessagessendMessageを受け取り、message.partsをtypeで分岐させながら描画する。テキストパートだけでなく、ツールパートや推論パートもこのpartsの配列に並ぶので、種類ごとにUIを書き分けられる。

// app/page.tsx
'use client';

import { useChat } from '@ai-sdk/react';
import { useState } from 'react';

export default function Chat() {
  const [input, setInput] = useState('');
  const { messages, sendMessage } = useChat();

  return (
    <div className="flex flex-col w-full max-w-md py-24 mx-auto">
      {messages.map(message => (
        <div key={message.id} className="whitespace-pre-wrap">
          {message.role === 'user' ? 'User: ' : 'AI: '}
          {message.parts.map((part, i) => {
            if (part.type === 'text') {
              return <div key={`${message.id}-${i}`}>{part.text}</div>;
            }
          })}
        </div>
      ))}

      <form
        onSubmit={e => {
          e.preventDefault();
          sendMessage({ text: input });
          setInput('');
        }}
      >
        <input
          className="fixed bottom-0 w-full max-w-md p-2 mb-8 border rounded"
          value={input}
          placeholder="Say something..."
          onChange={e => setInput(e.currentTarget.value)}
        />
      </form>
    </div>
  );
}

エージェントをAPI Routeに繋ぐ場合は、streamTextの代わりにToolLoopAgentのstream()を使い、createAgentUIStreamResponse()でレスポンス化する。InferAgentUIMessage型を使えば、エージェントのツール定義からUIメッセージの型を推論でき、クライアント側のパート分岐を型安全に書ける。これが「サーバーのツール定義を変えると、フロントの型もずれる」という事故を防いでくれる。

v5からの移行ガイド

v5を使っているプロジェクトの移行は、多くの場合そこまで重くない。Vercelはコードモッド(自動コード変換)を用意していて、まずはこれを当てるのが出発点になる。

# v5 → v6 への変換(v6だけを当てる)
npx @ai-sdk/codemod v6

# v4から積み上がっている場合は全バージョン分を順に当てる
npx @ai-sdk/codemod upgrade

コードモッドが自動で書き換えてくれるのは、import経路の移動、useChatフックの形状変更、tool()ヘルパーのシグネチャ変更などだ。一方で、ルーティング層、独自のツール結果レンダリング、込み入ったprovider-optionsの使い方は手作業が残ると理解しておくとよい。主な破壊的変更は次の通りだ。

・プロバイダのtextEmbeddingModeltextEmbeddingメソッドがembeddingModelembeddingに改名
isToolUIPartisStaticToolUIPartへ改名(静的ツールパート判定であることを明確化)。従来のisToolOrDynamicToolUIPartが新しいisToolUIPartになった
unknownのfinish reasonが廃止され、otherとして返るようになった
ai/testモジュールのV2モッククラスが削除。テストはV3モッククラスへ移行する
・MCP関連は@ai-sdk/mcpパッケージへ集約

メジャー更新だが「大改装」ではない
6はv3 Language Model Specificationが土台になっているが、Vercel自身が「大きな破壊的変更は意図的に抑えた」としている。v5を素直に使っていたコードほど移行は早い。手順としては、codemodを当てる→ビルドと型チェックを通す→主要なチャット/エージェント経路を実際に動かして確認する→コミット、の順が安全だ。いきなり全面置換せず、まずは1つの経路で挙動を確かめてから広げるとよい。

Microsoft Agent Framework / LangChain.js との比較表

TypeScriptでエージェントを組むときの主要な選択肢を並べる。どれも一長一短で、「TypeScriptでどこまで完結させたいか」「既存エコシステムの厚みが要るか」で選ぶと整理しやすい。

項目 Vercel AI SDK 6 LangChain.js Microsoft Agent Framework OpenAI Agents SDK (JS)
主な言語 TypeScriptネイティブ TS/JS(Python起点) Python / TypeScript両対応 TypeScript(v0.10系)
位置づけ 軽量・型安全な土台+UIストリーミング 最大級エコシステム・部品豊富 Azure連携・観測性重視 OpenAIモデル中心に最適化
プロバイダ OpenAI/Anthropic/Google他(Gateway可) 多数(最も幅広い) Azure OpenAI/OpenAI中心 OpenAI中心
エージェント ToolLoopAgent+stopWhen/prepareStep LangGraphで状態機械的に構成 ワークフロー+エージェント Agents/Handoffs/Guardrails
MCP @ai-sdk/mcpで安定対応 対応(統合あり) 対応 対応
観測性 DevTools+middleware LangSmith OpenTelemetry標準 トレーシング標準
フロントUI useChat等を標準提供 別途実装 別途実装 別途実装
強み フロントまで地続き・薄い抽象 RAG/マルチエージェントの部品量 エンタープライズ観測性 OpenAI機能の追従の速さ

ざっくり言えば、フロントエンドのチャットUIまで含めてTypeScriptで一気通貫に書きたいならVercel AI SDK、複雑なRAGやマルチエージェントの既製部品を組み合わせたいならLangChain.js、AzureとOpenTelemetryを軸にした企業システムに載せるならMicrosoft Agent Framework、OpenAIの最新機能を最速で使いたいならOpenAI Agents SDK、という棲み分けになる。Cloudflare Agentsのようにエッジ常駐のステートフルエージェントに振った選択肢もあり、デプロイ先の制約から逆算する手もある。

エンタープライズ用途

業務でエージェントを運用するとなると、モデル切替・ロギング・コスト管理の3点が現実的な論点になる。AI SDK 6はこのいずれにも標準的な打ち手を持つ。

モデル切替は、コード中のモデル文字列を差し替えるだけで完結する。さらにVercel AI Gateway経由にすれば、複数プロバイダを1つのキーで束ね、あるプロバイダが落ちたときに別のモデルへフェイルオーバーする構成が組める。prepareStepでステップごとに「軽いタスクは安いモデル、難所だけ高性能モデル」と動的に切り替えれば、品質とコストのバランスも取れる。

ロギングと観測性はdevToolsMiddleware()をはじめとするミドルウェア機構が起点になる。モデル呼び出しをラップして入出力・トークン・所要時間を記録し、本番ではこれを自前のロガーや監視基盤に流す。6ではusageが詳細化され、inputTokenDetailscacheReadTokenscacheWriteTokens等)やoutputTokenDetailsreasoningTokens等)まで取れるようになったため、キャッシュ効果や推論トークンの内訳を見ながらコストを最適化できる。

コスト管理では、この詳細なトークン計測に加え、stopWhenにカスタム条件を渡して「累積トークンが閾値を超えたら止める」といった予算ガードを実装できる。toModelOutput関数を使えば、大きなツール結果をモデルに渡す出力と実データとで分離し、不要なトークン消費を抑えられる。

エンタープライズ運用のチェックリスト
・モデルはGateway+環境変数で切替可能にし、コードを触らずに差し替えられるようにする
devToolsMiddleware相当のロギングを本番でも有効化し、トークン内訳を監視する
・破壊的ツールには必ずneedsApprovalを付け、承認ログを残す
stopWhenで予算・ステップ上限のガードを入れ、暴走を防ぐ
・MCPサーバーはHTTP+OAuth+redirect: 'error'で固める

よくある落とし穴

実装で詰まりやすいポイントを先回りで挙げる。

ストリーミングのレスポンス化を間違える——API Routeではresult.toUIMessageStreamResponse()を返す。ただのテキストとして返すとクライアントのmessage.partsが組み立たない
MCPクライアントを閉じ忘れる——onFinishまたはtry/finallyで必ずclose()する。接続が残り続けるとリソースリークになる
needsApprovalの状態をUIで描き分けていない——approval-requestedを処理しないと、承認待ちで止まったように見える。3状態(保留・承認後・拒否)を描く
stopWhenを外して無限ループ化——デフォルトのstepCountIs(20)を消すと、停止条件次第ではループが止まらない。上限は必ず残す
execute無しツールの意味を取り違える——executeを持たないツールはエージェントを止めるシグナルとして機能する。完了通知用のdoneツールなどに使う
移行時にcodemodを過信する——import書き換えは自動でも、ルーティングや独自レンダリングは手作業。ビルドと実機確認をはさむ
Zodスキーマの説明文を省略——.describe()が無いとモデルが引数を取り違えやすい。各フィールドに用途を書く

これらはいずれも公式docsの注意事項や挙動から導けるもので、再現できなければ手元のバージョン(aiパッケージのメジャーが6か)をまず確認するとよい。

参照ソース

AI SDK 6 — Vercel公式ブログ(v6の新機能・ToolLoopAgent・MCP・ツール承認の一次情報)
vercel/ai — GitHubリポジトリ(スター数・パッケージ構成・リリース)
AI SDK 公式ドキュメント(Agents・Loop Control・MCP Tools・Migration Guide)
Next.js: Human-in-the-Loop — AI SDK Cookbook(ツール承認のクライアント実装)
Migrate AI SDK 5.x to 6.0 — 移行ガイド(破壊的変更とcodemod)