---
title: Tools
description: Built-in workspace tools (including bash), custom tools, approvals, MCP tools, code execution, browser tools, and extensions for Think agents.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/agents/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Tools

Think provides built-in workspace file tools on every turn, plus integration points for custom tools, code execution, and dynamic extensions.

## Tool merge order

On every turn, Think merges tools from multiple sources. Later sources override earlier ones if names collide:

1. **Workspace tools** — `read`, `write`, `edit`, `list`, `find`, `grep`, `delete`, `bash` (built-in)
2. **`getTools()`** — your custom server-side tools
3. **Extension tools** — tools from loaded extensions (prefixed by extension name)
4. **Session tools** — `set_context`, `load_context`, `search_context` (from `configureSession`)
5. **Skill tools** — `activate_skill`, `read_skill_resource`, `run_skill_script` (from `getSkills()`, refer to [Agent Skills](https://developers.cloudflare.com/agents/runtime/execution/agent-skills/))
6. **MCP tools** — from connected MCP servers
7. **Client tools** — from the browser (refer to [Client tools](https://developers.cloudflare.com/agents/harnesses/think/client-tools/))

Tools belong to the agent running the turn. For parent-child orchestration, use [Agents as tools](https://developers.cloudflare.com/agents/runtime/execution/agent-tools/) instead of passing one-off tools through `chat()`.

## Built-in workspace tools

Every Think agent gets `this.workspace` — a virtual filesystem backed by Durable Object SQLite. Workspace tools are automatically available to the model with no configuration.

| Tool   | Description                                                                 |
| ------ | --------------------------------------------------------------------------- |
| read   | Read text with line numbers; pass images and PDFs to multimodal models      |
| write  | Write content to a file (creates parent directories)                        |
| edit   | Apply a find-and-replace edit to an existing file (supports fuzzy matching) |
| list   | List files and directories in a path                                        |
| find   | Find files matching a glob pattern                                          |
| grep   | Search file contents by regex or fixed string                               |
| delete | Delete a file or directory                                                  |
| bash   | Run a sandboxed Bash script against workspace files                         |

The `bash` tool is enabled by default. It mounts workspace files into a `just-bash` virtual filesystem, runs with network access disabled, and writes created, updated, and deleted files and empty directories back to the workspace. Use it for shell-style workflows that combine multiple file operations; use the narrower tools for simple reads, writes, and edits.

To keep tool calls bounded, the Bash tool snapshots up to 1,000 workspace files by default and skips files larger than 1 MB. Skipped files are reported in the tool result and are treated as protected during write-back so the script cannot accidentally overwrite or delete content that was not mounted. You can tune `maxWorkspaceFiles`, `maxWorkspaceFileBytes`, `maxOutputBytes`, `timeout`, and `network` through `workspaceBash`.

Disable the default Bash tool for conservative deployments:

* [  JavaScript ](#tab-panel-5887)
* [  TypeScript ](#tab-panel-5888)

JavaScript

```
export class MyAgent extends Think {  workspaceBash = false;
  getModel() {    /* ... */  }}
```

TypeScript

```
export class MyAgent extends Think<Env> {  workspaceBash = false;
  getModel() {    /* ... */  }}
```

### R2 spillover

By default, the workspace stores everything in SQLite. For large files, override `workspace` to add R2 spillover:

* [  JavaScript ](#tab-panel-5895)
* [  TypeScript ](#tab-panel-5896)

JavaScript

```
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() {    /* ... */  }}
```

TypeScript

```
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() {    /* ... */  }}
```

This requires an R2 bucket binding:

* [  wrangler.jsonc ](#tab-panel-5883)
* [  wrangler.toml ](#tab-panel-5884)

JSONC

```
{  "$schema": "./node_modules/wrangler/config-schema.json",  "r2_buckets": [    {      "binding": "R2",      "bucket_name": "agent-files"    }  ]}
```

TOML

```
[[r2_buckets]]binding = "R2"bucket_name = "agent-files"
```

## Custom tools

Override `getTools()` to add your own tools. These are standard AI SDK `tool()` definitions with Zod schemas:

* [  JavaScript ](#tab-panel-5909)
* [  TypeScript ](#tab-panel-5910)

JavaScript

```
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();        },      }),    };  }}
```

TypeScript

```
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();        },      }),    };  }}
```

Custom tools are merged with workspace tools automatically. If a custom tool has the same name as a workspace tool, the custom tool wins.

## Tool approval

Tools can require user approval before execution using the `needsApproval` option:

TypeScript

```
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 };      },    }),  };}
```

When `needsApproval` returns `true`, the tool call is sent to the client for approval. The conversation pauses until the client responds with `CF_AGENT_TOOL_APPROVAL`.

Note

Inside the [code execution tool](#code-execution-tool)'s sandbox, `needsApproval` behaves differently: it maps to the Code Mode runtime's durable pause/approve/resume flow, and a function-valued `needsApproval` always requires approval. Refer to [Approvals (human-in-the-loop)](#approvals-human-in-the-loop).

## Per-turn tool overrides

The `beforeTurn` hook can restrict or add tools for a specific turn:

TypeScript

```
beforeTurn(ctx: TurnContext) {  return {    activeTools: ["read", "write", "getWeather"],    tools: { emergencyTool: this.createEmergencyTool() },  };}
```

`activeTools` limits which tools the model can call. `tools` adds extra tools for this turn only (merged on top of existing tools).

## MCP tools

Think inherits MCP client support from the `Agent` base class. MCP tools from connected servers are automatically merged into every turn.

Set `waitForMcpConnections` to ensure MCP servers are connected before inference runs:

* [  JavaScript ](#tab-panel-5891)
* [  TypeScript ](#tab-panel-5892)

JavaScript

```
export class MyAgent extends Think {  waitForMcpConnections = true; // default 10s timeout  // or: waitForMcpConnections = { timeout: 5000 };
  getModel() {    /* ... */  }}
```

TypeScript

```
export class MyAgent extends Think<Env> {  waitForMcpConnections = true; // default 10s timeout  // or: waitForMcpConnections = { timeout: 5000 };
  getModel() {    /* ... */  }}
```

Add MCP servers programmatically or via `@callable` methods:

* [  JavaScript ](#tab-panel-5903)
* [  TypeScript ](#tab-panel-5904)

JavaScript

```
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);  }}
```

TypeScript

```
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);  }}
```

## Code execution tool

Let the LLM write and run JavaScript in a sandboxed Worker, recorded on a durable Code Mode runtime (abort-and-replay, human approvals, audit trail, reusable snippets). Requires `@cloudflare/codemode` and a `worker_loaders` binding.

Terminal window

```
npm install @cloudflare/codemode
```

The one-liner infers everything from the agent — `state.*` from `this.workspace`, the executor from `env.LOADER`, and a live browser (`cdp.*`) from `env.BROWSER` if bound:

* [  JavaScript ](#tab-panel-5899)
* [  TypeScript ](#tab-panel-5900)

JavaScript

```
import { Think } from "@cloudflare/think";import { createExecuteTool } from "@cloudflare/think/tools/execute";
export class MyAgent extends Think {  getModel() {    /* ... */  }
  getTools() {    return {      execute: createExecuteTool(this),    };  }}
```

TypeScript

```
import { Think } from "@cloudflare/think";import { createExecuteTool } from "@cloudflare/think/tools/execute";
export class MyAgent extends Think<Env> {  getModel() {    /* ... */  }
  getTools() {    return {      execute: createExecuteTool(this),    };  }}
```

Setup checklist:

* [  wrangler.jsonc ](#tab-panel-5885)
* [  wrangler.toml ](#tab-panel-5886)

JSONC

```
{  "$schema": "./node_modules/wrangler/config-schema.json",  "worker_loaders": [    {      "binding": "LOADER"    }  ],  "browser": {    "binding": "BROWSER"  }}
```

TOML

```
[[worker_loaders]]binding = "LOADER"
[browser]binding = "BROWSER" # optional — enables cdp.*
```

* [  JavaScript ](#tab-panel-5897)
* [  TypeScript ](#tab-panel-5898)

JavaScript

```
// 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";
```

TypeScript

```
// 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";
```

Each missing piece fails with an error naming the step.

Inside the sandbox the model sees typed namespaces plus the platform SDK:

* `tools.*` — your AI SDK tools (object args, validated against their schemas). Only tools with an `execute` function are exposed — client-side tools cannot run in the sandbox.
* `state.*` — the workspace filesystem (`state.readFile({ path })`, `state.glob({ pattern })`, `state.planEdits(...)`, and so on).
* `cdp.*` — the browser, when a Browser Run binding is configured. The execute tool defaults to `session: { mode: "dynamic" }`: sessions are per-execution unless the model promotes one with `cdp.startSession()`.
* `codemode.search` / `codemode.describe` / `codemode.step` / `codemode.run` — discovery, side-effect boundaries, and saved snippets.

Pass overrides for anything beyond the defaults — for example, custom `tools.*` alongside the agent-derived state:

* [  JavaScript ](#tab-panel-5893)
* [  TypeScript ](#tab-panel-5894)

JavaScript

```
execute: createExecuteTool(this, { tools: myDomainTools });
```

TypeScript

```
execute: createExecuteTool(this, { tools: myDomainTools });
```

Or fully explicit options (no agent inference):

* [  JavaScript ](#tab-panel-5905)
* [  TypeScript ](#tab-panel-5906)

JavaScript

```
import { createWorkspaceStateBackend } from "@cloudflare/shell";
createExecuteTool({  ctx: this.ctx,  tools: myDomainTools,  state: createWorkspaceStateBackend(this.workspace),  browser: this.env.BROWSER,  loader: this.env.LOADER,});
```

TypeScript

```
import { createWorkspaceStateBackend } from "@cloudflare/shell";
createExecuteTool({  ctx: this.ctx,  tools: myDomainTools,  state: createWorkspaceStateBackend(this.workspace),  browser: this.env.BROWSER,  loader: this.env.LOADER,});
```

### Approvals (human-in-the-loop)

An AI SDK tool with `needsApproval` does not run immediately inside the sandbox — calling it **pauses the run durably**. The pause comes back as a normal tool output (`{ status: "paused", executionId, pending }`), the model tells the user what it needs, and the turn ends. This differs from the client-side approval flow for plain `getTools()` tools: inside the sandbox a function-valued `needsApproval` cannot be evaluated against the call's arguments ahead of time, so it conservatively **always** requires approval. Think ships built-in callables to resolve it:

* `approveExecution(executionId)` — resumes the run where it stopped. Already-done work is replayed, not re-executed. The outcome replaces the paused output in the transcript and the chat auto-continues.
* `rejectExecution(executionId, reason?)` — ends the run with `{ status: "rejected", reason }` so the model can adapt.
* `pendingExecutions()` — pending actions (with full args) for rendering approval UI.

Note

**Render approval cards from `pendingExecutions()`, not the transcript.** The `pending` array in the paused tool output is a _truncated preview_ — args are bounded (\~2 KB each) so they do not blow up model context, but the full args (up to 1 MB) are what actually execute on approve. A human approving a gated call must see the authoritative args, so fetch them via `pendingExecutions(executionId)` before enabling the Approve button.

For a working approval card, refer to the [assistant example ↗](https://github.com/cloudflare/agents/tree/main/examples/assistant).

### The runtime handle

`createExecuteRuntime` returns the moving parts when the host needs more than the tool — and the handle is also assigned to `this.codemode` when created from an agent:

* [  JavaScript ](#tab-panel-5901)
* [  TypeScript ](#tab-panel-5902)

JavaScript

```
import { createExecuteRuntime } from "@cloudflare/think/tools/execute";
const { runtime, connectors, tool } = createExecuteRuntime(this);await runtime.executions(); // audit trailawait runtime.expirePaused(); // reclaim stale never-approved pauses (call from a scheduled task)await runtime.saveSnippet("name", { executionId }); // promote a script for reuse
```

TypeScript

```
import { createExecuteRuntime } from "@cloudflare/think/tools/execute";
const { runtime, connectors, tool } = createExecuteRuntime(this);await runtime.executions(); // audit trailawait runtime.expirePaused(); // reclaim stale never-approved pauses (call from a scheduled task)await runtime.saveSnippet("name", { executionId }); // promote a script for reuse
```

## Browser tools

Give your agent access to the Chrome DevTools Protocol (CDP) for web page inspection, scraping, screenshots, and debugging. Requires `@cloudflare/codemode` and a Browser Run binding.

* [  JavaScript ](#tab-panel-5913)
* [  TypeScript ](#tab-panel-5914)

JavaScript

```
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,      }),    };  }}
```

TypeScript

```
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,      }),    };  }}
```

* [  wrangler.jsonc ](#tab-panel-5889)
* [  wrangler.toml ](#tab-panel-5890)

JSONC

```
{  "$schema": "./node_modules/wrangler/config-schema.json",  "browser": {    "binding": "BROWSER"  },  "worker_loaders": [    {      "binding": "LOADER"    }  ]}
```

TOML

```
[browser]binding = "BROWSER"
[[worker_loaders]]binding = "LOADER"
```

This adds the durable CDP tool plus stateless [Quick Action](https://developers.cloudflare.com/agents/tools/browser/#quick-actions) tools when a `browser` binding is present:

| Tool              | Description                                                                             |
| ----------------- | --------------------------------------------------------------------------------------- |
| browser\_execute  | Run JavaScript against a live browser over CDP (screenshots, DOM reads, JS evaluation). |
| browser\_markdown | Read a page or raw HTML as Markdown.                                                    |
| browser\_extract  | Extract structured data from a page with AI.                                            |
| browser\_links    | List links on a page.                                                                   |
| browser\_scrape   | Scrape specific elements by CSS selector.                                               |

Pass `quickActions: false` to keep only `browser_execute`, or pass `quickActions: { actions, maxChars, options }` to configure the stateless tools. The Quick Action tools share the `browser` binding, need no Worker Loader, and resolve `ctx` from the current Agent automatically. To use only the stateless tools, import `createQuickActionTools` from `@cloudflare/think/tools/browser`.

The tool is backed by a Code Mode runtime with the `cdp` connector: the model writes async arrow functions that run in a sandboxed Worker isolate, with `cdp.send()`, `cdp.attachToTarget()`, `cdp.spec()` (the live, normalized protocol description), session helpers (`cdp.startSession()`, `cdp.sessionInfo()`, `cdp.closeSession()`), and debug-log helpers. Executions are recorded for abort-and-replay, so browser sessions survive approval pauses.

By default each execution gets a fresh browser session (`one-shot`), torn down when the run ends. Pass `session: { mode: "dynamic" }` to let the model promote a session with `cdp.startSession()` so later executions continue in the same browser, or `session: { mode: "reuse", key }` for a named long-lived session. Stale sessions are reclaimed by the connector's `sweep()` — call it from a scheduled task.

Note

The simplest setup is the unified execute tool in [Code execution tool](#code-execution-tool): `createExecuteTool(this)` already includes `cdp.*` alongside `state.*` and `tools.*` when `env.BROWSER` is bound — one tool, one durable history. Use `createBrowserTools` when you want a separate, browser-only tool.

For a custom Chrome endpoint, pass `cdpUrl` instead of `browser`:

* [  JavaScript ](#tab-panel-5907)
* [  TypeScript ](#tab-panel-5908)

JavaScript

```
createBrowserTools({  ctx: this.ctx,  cdpUrl: "http://localhost:9222",  loader: this.env.LOADER,});
```

TypeScript

```
createBrowserTools({  ctx: this.ctx,  cdpUrl: "http://localhost:9222",  loader: this.env.LOADER,});
```

For the full CDP connector API, refer to [Browse the web](https://developers.cloudflare.com/agents/tools/browser/).

## Extensions

Extensions are dynamically loaded sandboxed Workers that add tools at runtime. The LLM can write extension source code, load it, and use the new tools on the next turn.

Extensions require a `worker_loaders` binding:

* [  JavaScript ](#tab-panel-5911)
* [  TypeScript ](#tab-panel-5912)

JavaScript

```
import { Think } from "@cloudflare/think";
export class MyAgent extends Think {  extensionLoader = this.env.LOADER;
  getModel() {    /* ... */  }}
```

TypeScript

```
import { Think } from "@cloudflare/think";
export class MyAgent extends Think<Env> {  extensionLoader = this.env.LOADER;
  getModel() {    /* ... */  }}
```

### Static extensions

Define extensions that load at startup:

* [  JavaScript ](#tab-panel-5923)
* [  TypeScript ](#tab-panel-5924)

JavaScript

```
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 })            }          }        })`,      },    ];  }}
```

TypeScript

```
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 })            }          }        })`,      },    ];  }}
```

Extension tools are namespaced — a `math` extension with an `add` tool becomes `math_add` in the model's tool set.

### LLM-driven extensions

Give the model `createExtensionTools` so it can load extensions dynamically:

* [  JavaScript ](#tab-panel-5921)
* [  TypeScript ](#tab-panel-5922)

JavaScript

```
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(),    };  }}
```

TypeScript

```
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(),    };  }}
```

This gives the model two tools:

* `load_extension` — load a new extension from JavaScript source
* `list_extensions` — list currently loaded extensions

### Extension context blocks

Extensions can declare context blocks in their manifest. These are automatically registered with the Session:

TypeScript

```
getExtensions() {  return [{    manifest: {      name: "notes",      version: "1.0.0",      permissions: { network: false },      context: [        { label: "scratchpad", description: "Extension scratch space", maxTokens: 500 },      ],    },    source: `({ tools: { /* ... */ } })`,  }];}
```

The context block is registered as `notes_scratchpad` (namespaced by extension name).

## Custom workspace backends

The individual tool factories are exported for use with custom storage backends:

* [  JavaScript ](#tab-panel-5915)
* [  TypeScript ](#tab-panel-5916)

JavaScript

```
import {  createReadTool,  createWriteTool,  createEditTool,  createListTool,  createFindTool,  createGrepTool,  createDeleteTool,  createWorkspaceTools,} from "@cloudflare/think/tools/workspace";
```

TypeScript

```
import {  createReadTool,  createWriteTool,  createEditTool,  createListTool,  createFindTool,  createGrepTool,  createDeleteTool,  createWorkspaceTools,} from "@cloudflare/think/tools/workspace";
```

Implement the operations interface for your storage backend:

* [  JavaScript ](#tab-panel-5917)
* [  TypeScript ](#tab-panel-5918)

JavaScript

```
const myReadOps = {  readFile: async (path) => fetchFromMyStorage(path),  stat: async (path) => getFileInfo(path),};
const readTool = createReadTool({ ops: myReadOps });
```

TypeScript

```
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 });
```

Or create the full set from a `Workspace`, optionally disabling the Bash tool:

* [  JavaScript ](#tab-panel-5919)
* [  TypeScript ](#tab-panel-5920)

JavaScript

```
import { createWorkspaceTools } from "@cloudflare/think/tools/workspace";
const tools = createWorkspaceTools(myCustomWorkspace);const toolsWithoutBash = createWorkspaceTools(myCustomWorkspace, {  bash: false,});
```

TypeScript

```
import { createWorkspaceTools } from "@cloudflare/think/tools/workspace";
const tools = createWorkspaceTools(myCustomWorkspace);const toolsWithoutBash = createWorkspaceTools(myCustomWorkspace, {  bash: false,});
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/agents/harnesses/think/tools/#page","headline":"Tools · Cloudflare Agents docs","description":"Built-in workspace tools (including bash), custom tools, approvals, MCP tools, code execution, browser tools, and extensions for Think agents.","url":"https://developers.cloudflare.com/agents/harnesses/think/tools/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/agents/","name":"Agents"}},{"@type":"ListItem","position":3,"item":{"@id":"/agents/harnesses/","name":"Harnesses"}},{"@type":"ListItem","position":4,"item":{"@id":"/agents/harnesses/think/","name":"Think"}},{"@type":"ListItem","position":5,"item":{"@id":"/agents/harnesses/think/tools/","name":"Tools"}}]}
```
