Think は、各ターンで組み込みのワークスペースファイルツールを提供します。加えて、カスタムツール、コード実行、動的拡張の連携ポイントがあります。
各ターンで、Think は複数ソースのツールをマージします。名前が衝突した場合は、後のソースが先のソースを上書きします。
- ワークスペースツール —
read、write、edit、list、find、grep、delete、bash(組み込み) getTools()— 独自のサーバー側ツール- 拡張ツール — 読み込んだ拡張のツール(拡張名でプレフィックス)
- セッションツール —
set_context、load_context、search_context(configureSessionから) - スキルツール —
activate_skill、read_skill_resource、run_skill_script(getSkills()から。 Agent Skills を参照) - MCP ツール —
includeMcpToolsがtrueのとき、接続中の MCP サーバーから - クライアントツール — ブラウザから(クライアントツール を参照)
ツールは、そのターンを実行しているエージェントに属します。親子のオーケストレーションでは、chat() に単発ツールを渡すのではなく Agents as tools を使います。
すべての Think エージェントは this.workspace を持ちます。Durable Object の SQLite を裏にした仮想ファイルシステムです。ワークスペースツールは設定なしで、モデルから自動で使えます。
| ツール | 説明 |
|---|---|
read |
行番号付きでテキストを読みます。画像と PDF はマルチモーダルモデルへ渡せます |
write |
ファイルへ内容を書き込みます(親ディレクトリを作成します) |
edit |
既存ファイルに検索置換の編集を適用します(ファジーマッチ対応) |
list |
パス内のファイルとディレクトリを一覧します |
find |
glob パターンに合うファイルを探します |
grep |
正規表現または固定文字列でファイル内容を検索します |
delete |
ファイルまたはディレクトリを削除します |
bash |
ワークスペースファイルに対して、サンドボックス内の Bash スクリプトを実行します |
bash ツールはデフォルトで有効です。ワークスペースファイルを just-bash 仮想ファイルシステムにマウントし、ネットワークなしで実行し、作成・更新・削除したファイルと空ディレクトリをワークスペースへ書き戻します。複数のファイル操作を組み合わせるシェル風ワークフローに使います。単純な読み取り、書き込み、編集には、より狭いツールを使います。
ツール呼び出しを抑えるため、Bash ツールはデフォルトで最大 1,000 件のワークスペースファイルをスナップショットし、1 MB 超のファイルはスキップします。スキップしたファイルはツール結果に報告され、書き戻し時は保護扱いになります。マウントされていない内容をスクリプトが誤って上書き・削除しないようにするためです。maxWorkspaceFiles、maxWorkspaceFileBytes、maxOutputBytes、timeout、network は workspaceBash で調整できます。
保守的なデプロイでは、デフォルトの Bash ツールを無効にします。
export class MyAgent extends Think {
workspaceBash = false;
getModel() {
/* ... */
}
}export class MyAgent extends Think<Env> {
workspaceBash = false;
getModel() {
/* ... */
}
}デフォルトでは、ワークスペースはすべてを SQLite に保存します。大きなファイルでは、workspace をオーバーライドして R2 への退避を追加します。
import { Think } from "@cloudflare/think";
import { Workspace } from "@cloudflare/shell";
export class MyAgent extends Think {
workspace = new Workspace({
sql: this.ctx.storage.sql,
r2: this.env.R2,
name: () => this.name,
});
getModel() {
/* ... */
}
}import { Think } from "@cloudflare/think";
import { Workspace } from "@cloudflare/shell";
export class MyAgent extends Think<Env> {
override workspace = new Workspace({
sql: this.ctx.storage.sql,
r2: this.env.R2,
name: () => this.name,
});
getModel() {
/* ... */
}
}これには R2 バケットバインディングが必要です。
{
"$schema": "./node_modules/wrangler/config-schema.json",
"r2_buckets": [
{
"binding": "R2",
"bucket_name": "agent-files"
}
]
}[[r2_buckets]]
binding = "R2"
bucket_name = "agent-files"getTools() をオーバーライドして独自ツールを追加します。標準の AI SDK tool() 定義で、Zod や Valibot などのライブラリからスキーマを付けます。
import { Think } from "@cloudflare/think";
import { tool } from "ai";
import { z } from "zod";
export class MyAgent extends Think {
getModel() {
/* ... */
}
getTools() {
return {
getWeather: tool({
description: "Get the current weather for a city",
inputSchema: z.object({
city: z.string().describe("City name"),
}),
execute: async ({ city }) => {
const res = await fetch(
`https://api.weather.com/v1/current?q=${city}&key=${this.env.WEATHER_KEY}`,
);
return res.json();
},
}),
};
}
}import { Think } from "@cloudflare/think";
import { tool } from "ai";
import type { ToolSet } from "ai";
import { z } from "zod";
export class MyAgent extends Think<Env> {
getModel() {
/* ... */
}
getTools(): ToolSet {
return {
getWeather: tool({
description: "Get the current weather for a city",
inputSchema: z.object({
city: z.string().describe("City name"),
}),
execute: async ({ city }) => {
const res = await fetch(
`https://api.weather.com/v1/current?q=${city}&key=${this.env.WEATHER_KEY}`,
);
return res.json();
},
}),
};
}
}カスタムツールはワークスペースツールと自動でマージされます。カスタムツールとワークスペースツールが同名なら、カスタムツールが勝ちます。
needsApproval オプションで、実行前にユーザー承認を必須にできます。
getTools(): ToolSet {
return {
deleteFile: tool({
description: "Delete a file from the system",
inputSchema: z.object({ path: z.string() }),
needsApproval: async ({ path }) => path.startsWith("/important/"),
execute: async ({ path }) => {
await this.workspace.rm(path);
return { deleted: path };
},
}),
};
}needsApproval が true を返すと、ツール呼び出しは承認のためクライアントへ送られます。クライアントが CF_AGENT_TOOL_APPROVAL で応答するまで、会話は一時停止します。
beforeTurn フックは、特定のターン向けにツールを制限または追加できます。
beforeTurn(ctx: TurnContext) {
return {
activeTools: ["read", "write", "getWeather"],
tools: { emergencyTool: this.createEmergencyTool() },
};
}activeTools は、モデルが呼べるツールを制限します。tools は、このターンだけ追加のツールを足します(既存ツールの上にマージされます)。
Think は Agent 基底クラスから MCP クライアント対応を継承します。デフォルトでは、接続中の MCP サーバーのツールを AI SDK ツールへ変換し、各ターンに追加します。
推論の前に MCP サーバーがつながるよう、waitForMcpConnections を設定します。
export class MyAgent extends Think {
waitForMcpConnections = true; // default 10s timeout
// or: waitForMcpConnections = { timeout: 5000 };
getModel() {
/* ... */
}
}export class MyAgent extends Think<Env> {
waitForMcpConnections = true; // default 10s timeout
// or: waitForMcpConnections = { timeout: 5000 };
getModel() {
/* ... */
}
}Code Mode など、Think の自動ツールセット以外で MCP ツールを出す場合は、AI SDK への直接公開をオフにします。
export class MyAgent extends Think {
includeMcpTools = false;
waitForMcpConnections = true;
getModel() {
/* ... */
}
}export class MyAgent extends Think<Env> {
includeMcpTools = false;
waitForMcpConnections = true;
getModel() {
/* ... */
}
}includeMcpTools が制御するのは、自動のモデルツールマージだけです。MCP 接続の登録、復元、発見、待機は続きます。生のカタログアクセス、直接呼び出し、Code Mode コネクタ、明示的な this.mcp.getAITools() 呼び出しも動きます。
beforeTurn の activeTools で MCP ツール名を外す代わりに、このプロパティを使います。Think は beforeTurn を呼ぶ前に MCP スキーマを変換するため、activeTools ではその変換を避けられません。コネクタランタイムの設定は Code Mode で MCP ツールを使う を参照してください。
MCP サーバーはプログラムから、または @callable メソッド経由で追加します。
import { callable } from "agents";
export class MyAgent extends Think {
getModel() {
/* ... */
}
@callable()
async addServer(name, url) {
return await this.addMcpServer(name, url);
}
@callable()
async removeServer(serverId) {
await this.removeMcpServer(serverId);
}
}import { callable } from "agents";
export class MyAgent extends Think<Env> {
getModel() {
/* ... */
}
@callable()
async addServer(name: string, url: string) {
return await this.addMcpServer(name, url);
}
@callable()
async removeServer(serverId: string) {
await this.removeMcpServer(serverId);
}
}LLM に、サンドボックス Worker 内で JavaScript を書いて実行させます。耐久的な Code Mode ランタイムに記録されます(abort-and-replay、人間の承認、監査証跡、再利用可能なスニペット)。@cloudflare/codemode と worker_loaders バインディングが必要です。
npm install @cloudflare/codemode1 行で、エージェントからすべてを推論します。state.* は this.workspace、executor は env.LOADER、ライブブラウザ(cdp.*)はバインドされていれば env.BROWSER です。
import { Think } from "@cloudflare/think";
import { createExecuteTool } from "@cloudflare/think/tools/execute";
export class MyAgent extends Think {
getModel() {
/* ... */
}
getTools() {
return {
execute: createExecuteTool(this),
};
}
}import { Think } from "@cloudflare/think";
import { createExecuteTool } from "@cloudflare/think/tools/execute";
export class MyAgent extends Think<Env> {
getModel() {
/* ... */
}
getTools() {
return {
execute: createExecuteTool(this),
};
}
}セットアップのチェックリストです。
{
"$schema": "./node_modules/wrangler/config-schema.json",
"worker_loaders": [
{
"binding": "LOADER"
}
],
"browser": {
"binding": "BROWSER"
}
}[[worker_loaders]]
binding = "LOADER"
[browser]
binding = "BROWSER" # optional — enables cdp.*// worker entry — the runtime lives in a Durable Object facet, so the class
// must be exported (the @cloudflare/codemode/vite plugin does this
// automatically; the Think framework's generated entry already includes it)
export { CodemodeRuntime } from "@cloudflare/codemode";// worker entry — the runtime lives in a Durable Object facet, so the class
// must be exported (the @cloudflare/codemode/vite plugin does this
// automatically; the Think framework's generated entry already includes it)
export { CodemodeRuntime } from "@cloudflare/codemode";欠けている部品は、その手順名を含むエラーで失敗します。
サンドボックス内で、モデルは型付き名前空間とプラットフォーム SDK を見ます。
tools.*— 独自の AI SDK ツール(オブジェクト引数、スキーマで検証)。execute関数があるツールだけ公開されます。クライアント側ツールはサンドボックスでは動きません。state.*— ワークスペースファイルシステム(state.readFile({ path })、state.glob({ pattern })、state.planEdits(...)など)。cdp.*— Browser Run バインディングがあるときのブラウザ。execute ツールのデフォルトはsession: { mode: "dynamic" }です。セッションは実行単位です。モデルがcdp.startSession()で昇格した場合を除きます。codemode.search/codemode.describe/codemode.step/codemode.run— 発見、副作用の境界、保存済みスニペット。
デフォルト以外はオーバーライドを渡します。たとえば、エージェント由来の状態と並べて独自の tools.* を足します。
execute: createExecuteTool(this, { tools: myDomainTools });execute: createExecuteTool(this, { tools: myDomainTools });または、エージェント推論なしの完全に明示的なオプションです。
import { createWorkspaceStateBackend } from "@cloudflare/shell";
createExecuteTool({
ctx: this.ctx,
tools: myDomainTools,
state: createWorkspaceStateBackend(this.workspace),
browser: this.env.BROWSER,
loader: this.env.LOADER,
});import { createWorkspaceStateBackend } from "@cloudflare/shell";
createExecuteTool({
ctx: this.ctx,
tools: myDomainTools,
state: createWorkspaceStateBackend(this.workspace),
browser: this.env.BROWSER,
loader: this.env.LOADER,
});needsApproval 付きの AI SDK ツールは、サンドボックス内ですぐには動きません。呼ぶと 実行を耐久的に一時停止します。一時停止は通常のツール出力({ status: "paused", executionId, pending })として返り、モデルがユーザーへ必要な内容を伝え、ターンが終わります。通常の getTools() ツールのクライアント側承認フローとは違います。サンドボックス内では、関数値の needsApproval を呼び出し引数に対して事前評価できないため、保守的に 常に 承認が必要です。Think は解決用の組み込み callable を同梱します。
approveExecution(executionId)— 止まった位置から実行を再開します。完了済みの作業は再生され、再実行されません。結果がトランスクリプトの一時停止出力を置き換え、チャットが自動継続します。rejectExecution(executionId, reason?)—{ status: "rejected", reason }で実行を終え、モデルが対応できるようにします。pendingExecutions()— 承認 UI 描画用の保留中アクション(引数つき)です。
動く承認カードは assistant の例 ↗ を参照してください。
ホストがツール以上の部品を必要とするとき、createExecuteRuntime が動く部分を返します。エージェントから作った場合、ハンドルは this.codemode にも割り当てられます。
import { createExecuteRuntime } from "@cloudflare/think/tools/execute";
const { runtime, connectors, tool } = createExecuteRuntime(this);
await runtime.executions(); // audit trail
await runtime.expirePaused(); // reclaim stale never-approved pauses (call from a scheduled task)
await runtime.saveSnippet("name", { executionId }); // promote a script for reuseimport { createExecuteRuntime } from "@cloudflare/think/tools/execute";
const { runtime, connectors, tool } = createExecuteRuntime(this);
await runtime.executions(); // audit trail
await runtime.expirePaused(); // reclaim stale never-approved pauses (call from a scheduled task)
await runtime.saveSnippet("name", { executionId }); // promote a script for reuseWeb ページの検査、スクレイピング、スクリーンショット、デバッグのため、エージェントに Chrome DevTools Protocol(CDP)へのアクセスを渡します。@cloudflare/codemode と Browser Run バインディングが必要です。
import { Think } from "@cloudflare/think";
import { createBrowserTools } from "@cloudflare/think/tools/browser";
export class MyAgent extends Think {
getModel() {
/* ... */
}
getTools() {
return {
...createBrowserTools({
ctx: this.ctx,
browser: this.env.BROWSER,
loader: this.env.LOADER,
}),
};
}
}import { Think } from "@cloudflare/think";
import { createBrowserTools } from "@cloudflare/think/tools/browser";
export class MyAgent extends Think<Env> {
getModel() {
/* ... */
}
getTools() {
return {
...createBrowserTools({
ctx: this.ctx,
browser: this.env.BROWSER,
loader: this.env.LOADER,
}),
};
}
}{
"$schema": "./node_modules/wrangler/config-schema.json",
"browser": {
"binding": "BROWSER"
},
"worker_loaders": [
{
"binding": "LOADER"
}
]
}[browser]
binding = "BROWSER"
[[worker_loaders]]
binding = "LOADER"browser バインディングがあるとき、耐久的な CDP ツールに加え、ステートレスな Quick Action ツールが追加されます。
| ツール | 説明 |
|---|---|
browser_execute |
CDP 経由でライブブラウザに対して JavaScript を実行します(スクリーンショット、DOM 読み取り、JS 評価)。 |
browser_markdown |
ページまたは生 HTML を Markdown として読みます。 |
browser_extract |
AI でページから構造化データを抽出します。 |
browser_links |
ページ上のリンクを一覧します。 |
browser_scrape |
CSS セレクタで特定の要素をスクレイピングします。 |
browser_execute だけ残す場合は quickActions: false を渡します。ステートレスツールを設定する場合は quickActions: { actions, maxChars, options } を渡します。Quick Action ツールは browser バインディングを共有し、Worker Loader は不要で、現在の Agent から ctx を自動解決します。ステートレスツールだけ使う場合は、@cloudflare/think/tools/browser から createQuickActionTools をインポートします。
このツールは cdp コネクタ付きの Code Mode ランタイムが裏にあります。モデルは、サンドボックス Worker isolate で動く async アロー関数を書き、cdp.send()、cdp.attachToTarget()、cdp.spec()(ライブで正規化したプロトコル記述)、セッションヘルパー(cdp.startSession()、cdp.sessionInfo()、cdp.closeSession())、デバッグログヘルパーを使います。実行は abort-and-replay 用に記録されるので、ブラウザセッションは承認の一時停止を越えて残ります。
デフォルトでは、各実行が新しいブラウザセッション(one-shot)を受け、実行終了時に破棄されます。後続の実行を同じブラウザで続ける場合は session: { mode: "dynamic" } を渡し、モデルが cdp.startSession() でセッションを昇格できるようにします。名前付きの長寿命セッションには session: { mode: "reuse", key } を使います。古いセッションはコネクタの sweep() が回収します。スケジュールタスクから呼んでください。
独自の Chrome エンドポイントでは、browser の代わりに cdpUrl を渡します。
createBrowserTools({
ctx: this.ctx,
cdpUrl: "http://localhost:9222",
loader: this.env.LOADER,
});createBrowserTools({
ctx: this.ctx,
cdpUrl: "http://localhost:9222",
loader: this.env.LOADER,
});CDP コネクタ API の全体は Web を閲覧する を参照してください。
拡張は、実行時にツールを足す、動的読み込みのサンドボックス Worker です。LLM は拡張のソースコードを書き、読み込み、次のターンで新しいツールを使えます。
拡張には worker_loaders バインディングが必要です。
import { Think } from "@cloudflare/think";
export class MyAgent extends Think {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
}import { Think } from "@cloudflare/think";
export class MyAgent extends Think<Env> {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
}起動時に読み込む拡張を定義します。
export class MyAgent extends Think {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
getExtensions() {
return [
{
manifest: {
name: "math",
version: "1.0.0",
permissions: { network: false },
},
source: `({
tools: {
add: {
description: "Add two numbers",
parameters: { a: { type: "number" }, b: { type: "number" } },
execute: async ({ a, b }) => ({ result: a + b })
}
}
})`,
},
];
}
}export class MyAgent extends Think<Env> {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
getExtensions() {
return [
{
manifest: {
name: "math",
version: "1.0.0",
permissions: { network: false },
},
source: `({
tools: {
add: {
description: "Add two numbers",
parameters: { a: { type: "number" }, b: { type: "number" } },
execute: async ({ a, b }) => ({ result: a + b })
}
}
})`,
},
];
}
}拡張ツールは名前空間化されます。math 拡張の add ツールは、モデルのツールセットでは math_add になります。
モデルに createExtensionTools を渡し、拡張を動的に読み込めるようにします。
import { createExtensionTools } from "@cloudflare/think/tools/extensions";
export class MyAgent extends Think {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
getTools() {
return {
...createExtensionTools({ manager: this.extensionManager }),
...this.extensionManager.getTools(),
};
}
}import { createExtensionTools } from "@cloudflare/think/tools/extensions";
export class MyAgent extends Think<Env> {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
getTools() {
return {
...createExtensionTools({ manager: this.extensionManager! }),
...this.extensionManager!.getTools(),
};
}
}これでモデルは次の 2 つのツールを得ます。
load_extension— JavaScript ソースから新しい拡張を読み込みますlist_extensions— 現在読み込まれている拡張を一覧します
拡張はマニフェストでコンテキストブロックを宣言できます。これらは Session に自動登録されます。
getExtensions() {
return [{
manifest: {
name: "notes",
version: "1.0.0",
permissions: { network: false },
context: [
{ label: "scratchpad", description: "Extension scratch space", maxTokens: 500 },
],
},
source: `({ tools: { /* ... */ } })`,
}];
}コンテキストブロックは notes_scratchpad として登録されます(拡張名で名前空間化されます)。
個別のツールファクトリは、カスタムストレージバックエンド向けにエクスポートされています。
import {
createReadTool,
createWriteTool,
createEditTool,
createListTool,
createFindTool,
createGrepTool,
createDeleteTool,
createWorkspaceTools,
} from "@cloudflare/think/tools/workspace";import {
createReadTool,
createWriteTool,
createEditTool,
createListTool,
createFindTool,
createGrepTool,
createDeleteTool,
createWorkspaceTools,
} from "@cloudflare/think/tools/workspace";ストレージバックエンド向けに operations インターフェイスを実装します。
const myReadOps = {
readFile: async (path) => fetchFromMyStorage(path),
stat: async (path) => getFileInfo(path),
};
const readTool = createReadTool({ ops: myReadOps });import type { ReadOperations } from "@cloudflare/think/tools/workspace";
const myReadOps: ReadOperations = {
readFile: async (path) => fetchFromMyStorage(path),
stat: async (path) => getFileInfo(path),
};
const readTool = createReadTool({ ops: myReadOps });または Workspace から一式を作ります。Bash ツールは任意で無効にできます。
import { createWorkspaceTools } from "@cloudflare/think/tools/workspace";
const tools = createWorkspaceTools(myCustomWorkspace);
const toolsWithoutBash = createWorkspaceTools(myCustomWorkspace, {
bash: false,
});import { createWorkspaceTools } from "@cloudflare/think/tools/workspace";
const tools = createWorkspaceTools(myCustomWorkspace);
const toolsWithoutBash = createWorkspaceTools(myCustomWorkspace, {
bash: false,
});