Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
title: Agents SDK reduces MCP schema conversion, adds exposure controls for MCP in Think and Code Mode SDK adds direct host APIs
description: Agents SDK reduces repeated MCP schema conversion and adds direct Code Mode runtime APIs for hosts that do not use the AI SDK.
products:
- agents
- workers
date: 2026-07-22
---

import { PackageManagers, TypeScriptExample } from "~/components";

This release reduces repeated MCP schema conversion and adds an opt-out for Think's automatic MCP tool exposure. It also lets non-AI-SDK hosts invoke the durable Code Mode runtime directly.

## Control direct MCP tool exposure in Think

Agents SDK MCP clients now reuse converted input and output schemas while a live connection keeps the same tool catalog. This avoids converting every MCP JSON Schema to Zod again for each model turn.

`@cloudflare/think` also adds `includeMcpTools`. Set it to `false` when you expose MCP tools through Code Mode or another mechanism outside Think's automatic tool set:

<TypeScriptExample>

```ts
import { Think } from "@cloudflare/think";

export class MyAgent extends Think<Env> {
includeMcpTools = false;
waitForMcpConnections = true;
}
```

</TypeScriptExample>

This setting skips Think's automatic `getAITools()` call. MCP registration, restoration, discovery, raw catalog access, direct calls, and Code Mode connectors continue to work.

Use [`listTools()`](/agents/model-context-protocol/apis/client-api/#thismcplisttools) when you only need the raw MCP catalog. For connector setup, refer to [Use MCP tools with Code Mode](/agents/tools/codemode/mcp/).

## Invoke the Code Mode runtime without the AI SDK

`@cloudflare/codemode@latest` adds `execute()`, `search()`, and `describe()` to the durable runtime handle. MCP servers and other hosts can now execute code and discover connector methods without adapting the runtime to an AI SDK tool.

<TypeScriptExample>

```ts
const matches = await runtime.search("create issue");
const docs = await runtime.describe(matches.results[0].path);
const outcome = await runtime.execute({
code: `async () => github.create_issue({ title: "Bug" })`,
});
```

</TypeScriptExample>

Search and describe results include `requiresApproval: true` for protected connector methods. Resolve a paused execution with the existing `approve()` and `reject()` methods.

For setup and exact method types, refer to [Create a durable Code Mode runtime](/agents/tools/codemode/durable-runtime/) and the [Code Mode API reference](/agents/tools/codemode/api-reference/).

## Upgrade

<PackageManagers pkg="agents@latest @cloudflare/think@latest @cloudflare/codemode@latest" />
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Think is configured by overriding methods and properties on your `Think` subclas
| `getSkillScriptRunner()` | `null` | Enable the optional `run_skill_script` tool |
| `workspaceBash` | `true` | Include or configure the default workspace `bash` tool — refer to [Tools](/agents/harnesses/think/tools/) |
| `messageConcurrency` | `"queue"` | How overlapping submits behave — refer to [Client tools](/agents/harnesses/think/client-tools/#message-concurrency) |
| `includeMcpTools` | `true` | Convert connected MCP tools to AI SDK tools and add them to model turns. Refer to [MCP tools](/agents/harnesses/think/tools/#mcp-tools) |
| `waitForMcpConnections` | `false` | Wait for MCP servers before inference |
| `chatRecovery` | `true` | Wrap WebSocket, sub-agent, programmatic, and continuation turns in `runFiber` for durable execution. Set to a configuration object with `maxAttempts`, `stableTimeoutMs`, `terminalMessage`, and `onExhausted` to tune bounded recovery |
| `chatStreamStallTimeoutMs` | `0` (off) | Opt-in inactivity watchdog: abort a turn whose model stream produces no chunk for this long (measures the gap between chunks, including tool execution). With `chatRecovery` on, a stall routes into bounded recovery |
Expand Down
25 changes: 23 additions & 2 deletions src/content/docs/agents/harnesses/think/tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ On every turn, Think merges tools from multiple sources. Later sources override
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](/agents/runtime/execution/agent-skills/))
6. **MCP tools** — from connected MCP servers
6. **MCP tools** — from connected MCP servers when `includeMcpTools` is `true`
7. **Client tools** — from the browser (refer to [Client tools](/agents/harnesses/think/client-tools/))

Tools belong to the agent running the turn. For parent-child orchestration, use [Agents as tools](/agents/runtime/execution/agent-tools/) instead of passing one-off tools through `chat()`.
Expand Down Expand Up @@ -181,7 +181,7 @@ beforeTurn(ctx: TurnContext) {

## MCP tools

Think inherits MCP client support from the `Agent` base class. MCP tools from connected servers are automatically merged into every turn.
Think inherits MCP client support from the `Agent` base class. By default, Think converts tools from connected MCP servers to AI SDK tools and adds them to every turn.

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

Expand All @@ -200,6 +200,27 @@ export class MyAgent extends Think<Env> {

</TypeScriptExample>

If you expose MCP tools through Code Mode or another mechanism outside Think's automatic tool set, turn off direct AI SDK tool exposure:

<TypeScriptExample>

```ts
export class MyAgent extends Think<Env> {
includeMcpTools = false;
waitForMcpConnections = true;

getModel() {
/* ... */
}
}
```

</TypeScriptExample>

`includeMcpTools` controls only the automatic model tool merge. MCP connections still register, restore, discover, and wait. Raw catalog access, direct calls, Code Mode connectors, and explicit `this.mcp.getAITools()` calls continue to work.

Use this property instead of removing MCP tool names through `activeTools` in `beforeTurn`. Think converts MCP schemas before it calls `beforeTurn`, so `activeTools` cannot avoid that conversion. To configure a connector runtime, refer to [Use MCP tools with Code Mode](/agents/tools/codemode/mcp/).

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

<TypeScriptExample>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,14 @@ Once connected, access the server's capabilities:

### Get available tools

Use `listTools()` to inspect the raw MCP catalog without preparing tools for an AI SDK model call:

<TypeScriptExample>

```ts
const state = this.getMcpServers();
const tools = this.mcp.listTools();

// All tools from all connected servers
for (const tool of state.tools) {
for (const tool of tools) {
console.log(`Tool: ${tool.name}`);
console.log(` From server: ${tool.serverId}`);
console.log(` Title: ${tool.title ?? tool.annotations?.title ?? tool.name}`);
Expand All @@ -336,6 +337,8 @@ for (const tool of state.tools) {

</TypeScriptExample>

`getMcpServers().tools` returns the same raw tool records as part of the full MCP client state. Neither API converts the tool schemas.

#### Integration with AI SDK

To use MCP tools with the AI SDK, use `this.mcp.getAITools()` which converts MCP tools to AI SDK format:
Expand Down Expand Up @@ -364,7 +367,7 @@ export class MyAgent extends Agent<Env> {

:::note

`getMcpServers().tools` returns raw MCP `Tool` objects for inspection. Use `this.mcp.getAITools()` when passing tools to the AI SDK.
Use `this.mcp.listTools()` or `getMcpServers().tools` for discovery. Call `this.mcp.getAITools()` when passing tools to the AI SDK. `getAITools()` converts each input and output schema to Zod, then reuses those schemas while the live connection keeps the same current catalog.

:::

Expand Down Expand Up @@ -1145,6 +1148,16 @@ Close all active server connections while preserving registrations.
async closeAllConnections(): Promise<void>
```

#### `this.mcp.listTools()`

Get raw MCP tool records without converting their schemas to Zod.

```ts
listTools(filter?: MCPServerFilter): Array<Tool & { serverId: string }>
```

Use this method for catalog discovery and inspection. Pass an `MCPServerFilter` to limit the returned tools to specific connections.

#### `this.mcp.getAITools()`

Get all discovered MCP tools in a format compatible with the AI SDK.
Expand All @@ -1155,6 +1168,8 @@ getAITools(filter?: MCPServerFilter): ToolSet

Tools are automatically namespaced by server ID to prevent conflicts when multiple MCP servers expose tools with the same name.

`getAITools()` reuses converted schemas for the current catalog on each live connection. It converts schemas again after discovery replaces the catalog or the live connection changes. Each call returns fresh tool records and execute functions. Use `this.mcp.listTools()` when you only need the raw catalog.

Pass an `MCPServerFilter` to scope the returned tools to a subset of connected servers:

<TypeScriptExample>
Expand Down
10 changes: 10 additions & 0 deletions src/content/docs/agents/tools/codemode/api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ interface CodemodeRuntimeHandle {
tool(
options?: CodemodeRuntimeToolOptions,
): Tool<ProxyToolInput, ProxyToolOutput>;
execute(input: ProxyToolInput): Promise<ProxyToolOutput>;
search(query: string): Promise<SearchOutput>;
describe(target: string): Promise<DescribeOutput>;
approve(options: CodemodeApproveOptions): Promise<ProxyToolOutput>;
reject(options: CodemodeRejectOptions): Promise<boolean>;
rollback(options: CodemodeRollbackOptions): Promise<void>;
Expand All @@ -70,6 +73,9 @@ The handle methods have these effects:
| Method | Effect |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tool(options?)` | Returns the AI SDK tool given to the model. `description` replaces the default description. `connectorHints` adds a one-line hint for each connector when using the default description. |
| `execute({ code })` | Runs code directly without adapting the runtime to an AI SDK tool. The result can complete, pause for approval, or return an error status. |
| `search(query)` | Searches connector methods and saved snippets without running sandbox code. |
| `describe(target)` | Returns on-demand TypeScript documentation for a connector, method, or saved snippet. |
| `approve({ executionId })` | Resumes a paused execution through replay. The result can complete, pause again, or return an error status. It does not revive a non-paused execution. |
| `reject({ seq, executionId })` | Rejects one pending action and terminates the execution. Returns `false` if the action is no longer pending. It does not roll back earlier actions. |
| `rollback({ executionId })` | Calls available `revert` functions in reverse call order. Missing connectors and methods without `revert` remain applied. It attempts later reverts after a failure. |
Expand Down Expand Up @@ -230,6 +236,7 @@ type SearchResult = {
connector: string;
method: string;
description?: string;
requiresApproval?: boolean;
kind: "method" | "snippet";
score: number;
};
Expand All @@ -243,11 +250,14 @@ type SearchOutput = {
type DescribeOutput = {
path: string;
description?: string;
requiresApproval?: boolean;
types: string;
kind: "connector" | "method" | "snippet";
};
```

`requiresApproval` is `true` when a connector method pauses before execution. It is omitted for methods that do not require approval and for snippets.

### Snippet types

```ts
Expand Down
26 changes: 24 additions & 2 deletions src/content/docs/agents/tools/codemode/durable-runtime.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Create a durable Code Mode runtime
description: Create a Code Mode runtime with connectors, durable approvals, rollback, execution history, and reusable snippets.
description: Create a Code Mode runtime with connectors, direct host APIs, durable approvals, rollback, execution history, and reusable snippets.
pcx_content_type: how-to
sidebar:
order: 1
Expand All @@ -23,7 +23,7 @@ Code Mode is experimental and may introduce breaking changes. Use caution in pro

## Prerequisites

You need an existing Agents SDK application that uses `AIChatAgent`, Vite, and the AI SDK.
You need an existing Agents SDK application with a Durable Object and Vite. The example uses `AIChatAgent` and the AI SDK.

## Integrate Code Mode

Expand Down Expand Up @@ -234,6 +234,28 @@ You need an existing Agents SDK application that uses `AIChatAgent`, Vite, and t

</Steps>

## Use the runtime without the AI SDK

Use `execute()`, `search()`, and `describe()` when an MCP server or another host invokes Code Mode without an AI SDK tool adapter:

<TypeScriptExample>

```ts
const runtime = this.#runtime();
const matches = await runtime.search("create note");
const method = matches.results[0];
const docs = await runtime.describe(method.path);
const outcome = await runtime.execute({
code: `async () => notes.createNote({ text: "Follow up" })`,
});
```

</TypeScriptExample>

`search()` and `describe()` do not run sandbox code. Their results include `requiresApproval: true` for connector methods that pause before execution.

`execute()` returns the same durable result as the model-facing tool. A result can complete, pause, or contain an execution error. Resolve a paused result with `approve()` or `reject()`.

## Verify the integration

Ask the model to list saved notes. The model receives one `codemode` tool and can discover connector methods inside the sandbox:
Expand Down
19 changes: 19 additions & 0 deletions src/content/docs/agents/tools/codemode/mcp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,23 @@ You need:

Await `codemodeRuntime()`, then pass `runtime.tool()` to your model as the `codemode` tool. Await the helper again before calling approval, rejection, rollback, or snippet methods. This ensures MCP connections have finished restoring after hibernation.

If the runtime belongs to a `Think` agent, keep MCP tools out of the direct model tool set:

<TypeScriptExample>

```ts
import { Think } from "@cloudflare/think";

export class Chat extends Think<Env> {
includeMcpTools = false;
waitForMcpConnections = true;
}
```

</TypeScriptExample>

`includeMcpTools = false` skips Think's automatic `getAITools()` call. The MCP connection remains available to `McpConnector`.

4. **Let the model discover and call tools**

Tell the model to use `codemode.search()` and `codemode.describe()` before calling unfamiliar methods. Model-generated sandbox code can then discover and call the generated methods:
Expand Down Expand Up @@ -183,3 +200,5 @@ const codemode = createCodeTool({
</TypeScriptExample>

This approach exposes the MCP tools under the default `codemode` namespace. It does not use the connector runtime's durable pause, approval, and resume flow. Use `McpConnector` when tools can cause side effects or when the model needs on-demand discovery.

`getAITools()` converts MCP input and output schemas for use by the AI SDK. The Agents SDK reuses those converted schemas while each live connection keeps the same current catalog. Use `this.mcp.listTools()` instead when you only need to inspect the raw MCP catalog.