mcpField
T
tools

tools

github.com/assistant-ui開発296.3k 回インストール1 日前に更新
リポジトリ

tools について

assistant-ui tools は、モデルが呼び出すツールとその UI を作る開発者向けのツールキットシステムです。コンパイラーが `"use generative"` ファイルをサーバー用とクライアント用に分割し、人による操作や MCP ツールキットにも対応します。

主な機能

  1. 01サーバー用・クライアント用ビルドの生成
  2. 02バックエンド、フロントエンド、人間、Provider のツール種別
  3. 03ツール呼び出しとストリーミング状態の描画
  4. 04人間の入力と承認ゲート
  5. 05MCP ツールキットとアプリへの対応

活用シーン

  1. 01チャットモデル向けのツール定義
  2. 02チャット UI でのツール呼び出し表示
  3. 03人間の入力や承認による実行の一時停止

MCP 対応

ツールキットは `AISDKToolkit` と `defineMcpToolkit` を通じて MCP サーバーを開けます。また、MCP App の `ui://` ウィジェットの描画方法も扱います。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/assistant-ui/skills --skill tools

インストール後、Claude Code で/toolsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
human-in-loop.md
legacy-component-apis.md
mcp-apps.md
mcp-server.md
multi-agent.md
tool-ui.md
toolkits.md
webmcp.md

詳細

assistant-ui ツール

最新の API については、常に assistant-ui.com/llms.txt を参照してください。

ツールとは、モデルが呼び出せる名前付きの機能です。assistant-ui では、ツールをツールキット内で宣言します。ツールキットは、キーがモデルから見えるツール名であり、値がスキーマ、エグゼキューター、レンダラーを保持するマップです。サポートされている作成方法は、ビルドプラグインによってコンパイルされる "use generative" ファイルです。これは 1 つのファイルをサーバービルド(スキーマとバックエンドエグゼキューター)とクライアントビルド(スキーマとレンダラーとブラウザーエグゼキューター)に分割します。

リファレンス

作成モデル

1. ビルドプラグインを追加する

コンパイラーがなければ、このディレクティブは何も行いません。

import { withAui } from "@assistant-ui/next";

export default withAui({
  /* your Next config */
});

Vite と TanStack Start では、代わりに @assistant-ui/vite の aui() を plugins に追加します。Expo と bare React Native では、@assistant-ui/metro の withAui で Metro 設定をラップします。3 つとも aui オプションオブジェクトを受け取り、その詳細は toolkits.md に記載されています。

2. ツールキットを書く
"use generative";

import { defineToolkit } from "@assistant-ui/react";
import { z } from "zod";

export default defineToolkit({
  get_weather: {
    description: "Get current weather for a location.",
    parameters: z.object({
      location: z.string().describe("City name or zip code"),
      unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
    }),
    execute: async ({ location, unit }) => {
      "use client";
      return fetchWeatherAPI(location, unit);
    },
    render: ({ args, result }) =>
      result ? (
        <div>
          {result.temperature} {args.unit}
        </div>
      ) : (
        <div>Fetching weather for {args.location}</div>
      ),
  },
});
3. クライアントにマウントする
"use client";

import { AssistantRuntimeProvider, AuiConfig, Tools } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/ai-sdk";
import toolkit from "./toolkit";

export function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
  const runtime = useChatRuntime();
  const config = AuiConfig({ tools: Tools({ toolkit }) });
  return (
    <AssistantRuntimeProvider runtime={runtime} config={config}>
      {children}
    </AssistantRuntimeProvider>
  );
}

代わりにツールキットをツリーの一部にスコープするには、そのサブツリーを <AuiProvider extends={aui} config={config}> でラップし、const aui = useAui() を指定します。useChatRuntime() はデフォルトで /api/chat を対象にします。

4. モデルに公開する

同じインポートが、ルートハンドラー内ではサーバービルドに解決されます。

import { AISDKToolkit } from "@assistant-ui/ai-sdk";
import { streamText, convertToModelMessages } from "ai";
import { openai } from "@ai-sdk/openai";
import toolkit from "../../toolkit";

const aiToolkit = new AISDKToolkit({ toolkit });

export async function POST(req: Request) {
  const { messages, system, tools } = await req.json();

  const result = streamText({
    model: openai("gpt-5.6-luna"),
    system,
    messages: await convertToModelMessages(messages),
    tools: await aiToolkit.tools({ frontend: tools }),
  });

  return result.toUIMessageStreamResponse();
}

AISDKToolkit.tools() は、すべてのツールキットツールをモデルに登録し、サーバービルドがエグゼキューターを持つ場合はバックエンドの execute を接続し、クライアントがリクエスト本文でアップロードしたフロントエンドツールをマージし、ツールキットが展開するあらゆる MCP サーバーを開きます。サーバーの execute は、同じ名前でアップロードされたエントリーよりも優先されます。

ツールの種類

種類は execute から推論され、type として書き戻されます。"use generative" ファイル内で type を自分で記述することはありません。

記述する execute推論される種類サーバービルドで保持されるものクライアントビルドで保持されるもの
単純な async () => ...backendserver-only でガードされた、スキーマと executeスキーマと render
async () => { "use client"; ... }frontendスキーマのみスキーマと execute、さらに render または renderText
humanTool()humanスキーマのみスキーマと render
stubTool()frontend、executor は実行時に提供されるスキーマのみスキーマと render または renderText
providerTool({ ... })providerスキーマと provider 設定スキーマと provider 設定
externalTool()backend、別の場所で定義される省略type: "backend" と render または renderText

コンパイラはビルド時に、すべてのツールが execute を宣言すること、frontend ツールが render または renderText を宣言すること、human ツールが render を宣言することを強制します。humanTool() と stubTool() にはランタイム実装がなく、到達すると例外をスローします。これはそのファイルが一度もコンパイルされなかったことを意味します。externalTool() も同様にコンパイル時のマーカーです。

ツール呼び出しのレンダリング

render はライブの呼び出しを ToolCallMessagePartProps として受け取ります。

フィールド型備考
argsTArgs解析済みの引数。ストリーミング中は部分的
argsTextstring生の、場合によっては部分的な JSON
resultTResult | undefined呼び出しに結果が存在する場合に存在
isErrorboolean | undefined結果が失敗を表すかどうか
statusToolCallMessagePartStatusrunning、complete、reason を伴う incomplete、または reason: "tool-calls" | "interrupt" を伴う requires-action
toolName、toolCallIdstringモデルから見える名前と、この呼び出しの安定した id
timingToolCallTiming | undefined追跡される場合の、実時間での開始と完了
interrupt{ type: "human"; payload: unknown } | undefinedfrontend executor からの一時停止された human() リクエスト
approvalobject | undefinedサーバー側のゲート: id、approved?、options?、optionId?、resolution?
addResult(result) => voidUI から human ツールを完了させる
resume(payload: unknown) => voidinterrupt に応答する
respondToApproval(response: ToolApprovalResponse) => Promise<void>承認ゲートに応答する

コンポーネントの代わりに 1 行のステータスを使うには、renderText に running と complete の値を設定します。それぞれ文字列か、({ args, result }) を引数とする関数です。UI を折りたたまれたツールグループの外に保つには、エントリで display: "standalone" を設定します。レンダラーを持たないツールは ToolFallback 要素にフォールバックします。

承認ゲート

一部のランタイムはサーバー上で一時停止し、ツールが実行される前にクライアントが応答しなければならない承認リクエストを発行します。AI SDK v7 ランタイムは、呼び出しレベルの toolApproval オプションにリストされたすべてのツールに対してこれを発行します。

import { useState } from "react";
import { defineToolkit, type ToolApprovalResponse } from "@assistant-ui/react";

const toolkit = defineToolkit({
  deploy: {
    type: "backend",
    render: ({ args, approval, respondToApproval, result }) => {
      const [error, setError] = useState<string | null>(null);

      const answer = async (response: ToolApprovalResponse) => {
        setError(null);
        try {
          await respondToApproval(response);
        } catch (failure) {
          setError(failure instanceof Error ? failure.message : String(failure));
        }
      };

      if (approval?.approved === undefined) {
        if (approval?.isAutomatic) return <p>Auto approved by policy</p>;
        return (
          <div>
            <p>Approve deploy to {args.target}?</p>
            <button onClick={() => void answer({ approved: true })}>Approve</button>
            <button onClick={() => void answer({ approved: false, reason: "user denied" })}>
              Deny
            </button>
            {error && <p role="alert">{error}</p>}
          </div>
        );
      }

      if (approval?.approved === false) {
        return <p>Denied{approval.reason ? `: ${approval.reason}` : ""}</p>;
      }
      return result === undefined ? <p>Approved, running</p> : <p>Deployed</p>;
    },
  },
});

approval.approved には 3 つの状態があります。undefined はゲートが開いていることを意味し、respondToApproval が合法となる唯一の状態です。true は決定が許可として記録され、サーバーが結果を生成していることを意味します。false は拒否として記録されたことを意味し、ランタイムはエラー結果を記録して approval.reason を公開します。approval.isAutomatic は、ユーザーではなくサーバー側のポリシーが決定を下した場合に true になるため、ボタンではなくバッジをレンダリングしてください。

respondToApproval は、ランタイムが応答を受け入れると解決し、記録できなかった場合(たとえばゲートの期限切れや回答の拒否)に拒否されるプロミスを返します。拒否された応答でもリクエストを再試行できるよう、コントロールを無効化する前にこれを await してください。toolApprovalAcceptsText(approval) は、リクエストが単独で、またはオプションと並んで自由形式の回答を受け付けるかどうかを報告するため、レンダラーはテキストフィールドを提供すべきかどうかを判断できます。オプション、質問、解決の全サーフェスについては human-in-loop.md を参照してください。

ヒューマンツール

ヒューマンツールにはエグゼキューターがありません。レンダラーが結果を提供するまで実行は一時停止します。

select_date: {
  description: "Ask the user to select a date.",
  parameters: z.object({ prompt: z.string() }),
  execute: humanTool(),
  render: ({ args, result, addResult }) => {
    if (result) return <p>Selected {result.date}</p>;
    return <DatePicker prompt={args.prompt} onChange={(date) => addResult({ date })} />;
  },
},

addResult は正確に 1 回だけ呼び出してください。ユーザーがツール結果そのものを提供する場合はヒューマンツールを、バックエンドがアクションを所有し許可だけを必要とする場合は承認ゲートを使用してください。

よくある落とし穴

humanTool() または stubTool() が実行時にスローする

  • そのファイルがコンパイラーによって処理されていません。ビルドプラグインを追加し、"use generative" をファイルの先頭行として維持してください。

ツール UI がまったくレンダリングされない

  • ツールキットのキーは、MCP の prefix を含め、モデルから見えるツール名と完全に一致している必要があります。
  • ツールキットがマウントされている必要があります。const config = AuiConfig({ tools: Tools({ toolkit }) }) をプロバイダーの config として渡してください。モジュールスコープまたは useMemo から安定したツールキットを渡してください。

モデルがフロントエンドツールやヒューマンツールを認識しない

  • クライアントビルドは、バックエンドが同じファイルのサーバービルドをインポートしたと想定するため、それらのスキーマのアップロードをスキップします。自分のバックエンドがない場合は、aui: { backendless: true } を指定してコンパイルしてください。

フロントエンドツールの結果がモデルに届かない

  • ランタイムを ai の sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls で設定し、承認ゲートには lastAssistantMessageIsCompleteWithApprovalResponses を設定してください。

ラウンドトリップされた結果で toModelOutput が無視される

  • ツールレジストリを streamText だけでなく convertToModelMessages(messages, { tools }) にも渡してください。

ツール名に関するビルド警告

  • 名前の重複は、2 つのスプレッドフラグメントが同じキーを定義し、オブジェクトスプレッドが後のものを保持することを意味します。どちらか一方のエントリの名前を変更してください。makeTool() できないツールは、不透明なファクトリー呼び出しに由来します。インラインオブジェクトとして記述するか、コンパイラーから見える defineToolkit(...) または defineMcpToolkit(...) フラグメントをスプレッドしてください。

respondToApproval が拒否される

  • これは approval.approved が undefined である間だけ合法です。display: "text" も allowFreeform も宣言していないリクエストへの text 回答はスローされ、不明な optionId も同様です。

MCP 接続がたまる

  • AISDKToolkit をモジュールスコープに保持して、クライアントがリクエストをまたいでプールされるようにし、onFinish から aiToolkit.close() を呼び出してください。

関連スキル

  • elements -- スタイル付きの ToolFallback と ToolGroup のファイル、およびカタログの残りの部分
  • generative-ui -- あなたが提供する語彙からモデルが構成する UI
  • react-mcp -- エンドユーザーがブラウザで追加し認証する MCP サーバー
  • runtime -- ツールキットが組み込まれる useChatRuntime と AI SDK のルート
  • copilots -- インタラクタブル、スタブツールに代わるモデル編集可能なアプリ状態
  • update -- 古いツールコードをツールキットへ移行する

コメント