ツールは LLM がアクションを実行したり外部システムとやり取りすることを可能にします。assistant-ui は、ツール操作をリアルタイムに作成、管理、視覚化するための包括的なツールキットを提供しています。
assistant-ui 入門 – ガイド : ツール
作成 : Masashi Okumura (@classcat.com)
作成日時 : 03/23/2026
バージョン : assistant-ui@0.0.84
* 本記事は assistant-ui.com/docs の以下のページを参考にしています :
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

assistant-ui 入門 – ガイド : ツール
ツールは、アシスタントに API 呼び出し、データベースクエリ等のようなアクションを提供します。
ツールは LLM がアクションを実行したり外部システムとやり取りすることを可能にします。assistant-ui は、ツール操作をリアルタイムに作成、管理、視覚化するための包括的なツールキットを提供しています。
概要
assistant-ui のツールは、LLM が特定のタスクを実行するために呼び出すことができる関数です。これらは、LLM の推論能力と以下のような現実世界のアクションの間のギャップを埋めます :
- API からデータを取得する
- 計算の実行
- データベースとのやり取り
- UI 要素の制御
- ワークフローの実行
推奨: Tools() API
Tools() API は assistant-ui でツールを登録するための推奨方法です。それは重複登録を防ぎ、すべてのランタイムとシームレスに連携する、一元管理可能なツール登録を提供します。
クイックスタート
すべてのツールを含む toolkit オブジェクトを作成し、useAui() を使用してそれを登録します :
import { useAui, Tools, type Toolkit } from "@assistant-ui/react";
import { z } from "zod";
// Define your toolkit
const myToolkit: Toolkit = {
getWeather: {
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 }) => {
const weather = await fetchWeatherAPI(location, unit);
return weather;
},
render: ({ args, result }) => {
if (!result) return <div>Fetching weather for {args.location}...</div>;
return (
<div className="weather-card">
<h3>{args.location}
<p>{result.temperature}° {args.unit}</p>
<p>{result.conditions}</p>
</div>
);
},
},
// Add more tools here
};
// Register tools in your runtime provider
function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
const runtime = useChatRuntime();
// Register all tools
const aui = useAui({
tools: Tools({ toolkit: myToolkit }),
});
return (
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
{children}
</AssistantRuntimeProvider>
);
}
利点
- 重複登録なし: ツールは一度だけ登録され、”tool already exists” エラーを回避します
- 定義の一元管理: すべてのツールは一箇所にまとめられ、管理とテストを容易にします
- 型安全: 適切な型推論を持つ完全な TypeScript サポート
- 柔軟性: すべてのランタイム (AI SDK, LangGraph, custom, 等) に対応
- 構成可能: ツールキットをファイル間に簡単に分割して統合できます
ツール定義
ツールキットの各ツールはこれらのプロパティを持つ ToolDefinition オブジェクトです :
type ToolDefinition =
| {
// Frontend tool: executes in the browser
type?: "frontend";
description?: string;
parameters: StandardSchemaV1 | JSONSchema7; // e.g. a Zod schema
execute: (args, context) => Promise;
render?: (props) => React.ReactNode;
}
| {
// Human tool: pauses for user input (render is required)
type: "human";
description?: string;
parameters: StandardSchemaV1 | JSONSchema7;
render: (props) => React.ReactNode;
}
| {
// Backend tool: execution happens server-side (no execute/parameters needed)
type: "backend";
render?: (props) => React.ReactNode;
};
大規模なツールキットのオーガナイズ
より大規模なアプリケーションについては、ツールを複数のファイル間で分割します :
// lib/tools/weather.tsx
export const weatherTools: Toolkit = {
getWeather: { /* ... */ },
getWeatherForecast: { /* ... */ },
};
// lib/tools/database.tsx
export const databaseTools: Toolkit = {
queryData: { /* ... */ },
insertData: { /* ... */ },
};
// lib/toolkit.tsx
import { weatherTools } from "./tools/weather";
import { databaseTools } from "./tools/database";
export const appToolkit: Toolkit = {
...weatherTools,
...databaseTools,
};
// App.tsx
import { appToolkit } from "./lib/toolkit";
function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
const runtime = useChatRuntime();
const aui = useAui({
tools: Tools({ toolkit: appToolkit }),
});
return (
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
{children}
</AssistantRuntimeProvider>
);
}
UI-Only ツール
実行が他の場所で発生するツール (e.g., バックエンド MCP ツール) については、execute 関数を省略します :
const uiOnlyToolkit: Toolkit = {
webSearch: {
description: "Search the web",
parameters: z.object({
query: z.string(),
}),
// No execute - handled by backend
render: ({ args, result }) => {
return (
<div>
<h3>Search: {args.query}</h3>
{result?.results.map((item) => (
<div key={item.id}>
<a href={item.url}>{item.title}</a>
</div>
))}
</div>
);
},
},
};
ツール実行コンテキスト
ツールは実行中に追加コンテキストを受け取ります :
execute: async (args, context) => {
// context.abortSignal - AbortSignal for cancellation
// context.toolCallId - Unique identifier for this invocation
// context.human - Function to request human input
// Example: Respect cancellation
const response = await fetch(url, { signal: context.abortSignal });
// Example: Request user confirmation
const userResponse = await context.human({
message: "Are you sure?",
});
};
Human-in-the-Loop
ツールは実行を一時停止してユーザ入力や承認を要求できます :
const confirmationToolkit: Toolkit = {
sendEmail: {
description: "Send an email with confirmation",
parameters: z.object({
to: z.string(),
subject: z.string(),
body: z.string(),
}),
execute: async ({ to, subject, body }, { human }) => {
// Request user confirmation before sending
const confirmed = await human({
type: "confirmation",
action: "send-email",
details: { to, subject },
});
if (!confirmed) {
return { status: "cancelled" };
}
await sendEmail({ to, subject, body });
return { status: "sent" };
},
render: ({ args, result, interrupt, resume }) => {
// Show confirmation dialog when waiting for user input
if (interrupt) {
return (
<div>
<h3>Confirm Email</h3>
<p>Send to: {interrupt.payload.details.to}</p>
<p>Subject: {interrupt.payload.details.subject}</p>
<button onClick={() => resume(true)}>Confirm</button>
<button onClick={() => resume(false)}>Cancel</button>
</div>
);
}
// Show result
if (result) {
return <div>Status: {result.status}</div>;
}
return <div>Preparing email...</div>;
},
},
};
ツール・パラダイム
フロントエンドツール
ブラウザ内で実行されるツール :
const frontendToolkit: Toolkit = {
screenshot: {
description: "Capture a screenshot of the current page",
parameters: z.object({
selector: z.string().optional(),
}),
execute: async ({ selector }) => {
const element = selector ? document.querySelector(selector) : document.body;
const screenshot = await captureElement(element);
return { dataUrl: screenshot };
},
},
};
バックエンドツール
サーバ側で実行されるツール :
// Backend route (AI SDK)
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
messages: await convertToModelMessages(messages),
tools: {
queryDatabase: {
description: "Query the application database",
inputSchema: zodSchema(
z.object({
query: z.string(),
table: z.string(),
}),
),
execute: async ({ query, table }) => {
const results = await db.query(query, { table });
return results;
},
},
},
});
return result.toUIMessageStreamResponse();
}
frontendTools を使用したクライアント定義ツール
Vercel AI SDK アダプターは、クライアント定義ツールの自動シリアル化を実装しています。Tools() API 経由で登録されたツールは、自動的に API リクエストに含まれます :
// Frontend: Define tools with Tools() API
const clientToolkit: Toolkit = {
calculate: {
description: "Perform calculations",
parameters: z.object({
expression: z.string(),
}),
execute: async ({ expression }) => {
return eval(expression); // Use proper parser in production
},
},
};
function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
const runtime = useChatRuntime();
const aui = useAui({
tools: Tools({ toolkit: clientToolkit }),
});
return (
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
{children}
</AssistantRuntimeProvider>
);
}
// Backend: Use frontendTools to receive client tools
import { frontendTools } from "@assistant-ui/react-ai-sdk";
export async function POST(req: Request) {
const { messages, tools } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
messages: await convertToModelMessages(messages),
tools: {
...frontendTools(tools), // Client-defined tools
// Additional server-side tools
queryDatabase: {
description: "Query the database",
inputSchema: zodSchema(z.object({ query: z.string() })),
execute: async ({ query }) => {
return await db.query(query);
},
},
},
});
return result.toUIMessageStreamResponse();
}
MCP (Model Context Protocol) ツール
AI SDK の実験的な MCP サポートを使用した MCP サーバと統合 :
import { experimental_createMCPClient, streamText } from "ai";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
export async function POST(req: Request) {
const client = await experimental_createMCPClient({
transport: new StdioClientTransport({
command: "npx",
args: ["@modelcontextprotocol/server-github"],
}),
});
try {
const tools = await client.tools();
const result = streamText({
model: openai("gpt-4o"),
tools,
messages: await convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
} finally {
await client.close();
}
}
ベストプラクティス
- Tools() API の使用: legacy のコンポーネント/フックベースの登録よりも、Tools() API を常に優先します
- 定義の一元管理: 管理を容易にするため、すべてのツールをツールキットファイル内に保持します
- 明確な説明: 各ツールをいつ使用するべきか LLM の理解を助けるために、ツール説明をわかりやすく記述します
- パラメータ検証: Zod スキーマを使用して型安全性を確保します
- エラー処理: ユーザフレンドリーなメッセージでエラーを適切に処理します
- 状態のロード: ツール実行中に視覚的なフィードバックを提供する
- セキュリティ: 権限を検証し、入力をサニタイズする
- パフォーマンス: キャンセル可能な操作には abort シグナルを使用します
- テスト: ツールを単独で、そして完全なアシスタントフローでテストします
以上