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
30 changes: 15 additions & 15 deletions src/content/docs/agents/api-reference/agents-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,21 @@ flowchart TD

## Server-side API reference

| Feature | Methods | Documentation |
| --------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------- |
| **State** | `setState()`, `onStateChanged()`, `initialState` | [Store and sync state](/agents/api-reference/store-and-sync-state/) |
| **Callable methods** | `@callable()` decorator | [Callable methods](/agents/api-reference/callable-methods/) |
| **Scheduling** | `schedule()`, `scheduleEvery()`, `getSchedules()`, `cancelSchedule()` | [Schedule tasks](/agents/api-reference/schedule-tasks/) |
| **Queue** | `queue()`, `dequeue()`, `dequeueAll()`, `getQueue()` | [Queue tasks](/agents/api-reference/queue-tasks/) |
| **WebSockets** | `onConnect()`, `onMessage()`, `onClose()`, `broadcast()` | [WebSockets](/agents/api-reference/websockets/) |
| **HTTP/SSE** | `onRequest()` | [HTTP and SSE](/agents/api-reference/http-sse/) |
| **Email** | `onEmail()`, `replyToEmail()` | [Email routing](/agents/api-reference/email/) |
| **Workflows** | `runWorkflow()`, `waitForApproval()` | [Run Workflows](/agents/api-reference/run-workflows/) |
| **MCP Client** | `addMcpServer()`, `removeMcpServer()`, `getMcpServers()` | [MCP Client API](/agents/api-reference/mcp-client-api/) |
| **AI Models** | Workers AI, OpenAI, Anthropic bindings | [Using AI models](/agents/api-reference/using-ai-models/) |
| **Protocol messages** | `shouldSendProtocolMessages()`, `isConnectionProtocolEnabled()` | [Protocol messages](/agents/api-reference/protocol-messages/) |
| **Context** | `getCurrentAgent()` | [getCurrentAgent()](/agents/api-reference/get-current-agent/) |
| **Observability** | `observability.emit()` | [Observability](/agents/api-reference/observability/) |
| Feature | Methods | Documentation |
| --------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
| **State** | `setState()`, `onStateChanged()`, `initialState` | [Store and sync state](/agents/api-reference/store-and-sync-state/) |
| **Callable methods** | `@callable()` decorator | [Callable methods](/agents/api-reference/callable-methods/) |
| **Scheduling** | `schedule()`, `scheduleEvery()`, `getSchedules()`, `cancelSchedule()`, `keepAlive()` | [Schedule tasks](/agents/api-reference/schedule-tasks/) |
| **Queue** | `queue()`, `dequeue()`, `dequeueAll()`, `getQueue()` | [Queue tasks](/agents/api-reference/queue-tasks/) |
| **WebSockets** | `onConnect()`, `onMessage()`, `onClose()`, `broadcast()` | [WebSockets](/agents/api-reference/websockets/) |
| **HTTP/SSE** | `onRequest()` | [HTTP and SSE](/agents/api-reference/http-sse/) |
| **Email** | `onEmail()`, `replyToEmail()` | [Email routing](/agents/api-reference/email/) |
| **Workflows** | `runWorkflow()`, `waitForApproval()` | [Run Workflows](/agents/api-reference/run-workflows/) |
| **MCP Client** | `addMcpServer()`, `removeMcpServer()`, `getMcpServers()` | [MCP Client API](/agents/api-reference/mcp-client-api/) |
| **AI Models** | Workers AI, OpenAI, Anthropic bindings | [Using AI models](/agents/api-reference/using-ai-models/) |
| **Protocol messages** | `shouldSendProtocolMessages()`, `isConnectionProtocolEnabled()` | [Protocol messages](/agents/api-reference/protocol-messages/) |
| **Context** | `getCurrentAgent()` | [getCurrentAgent()](/agents/api-reference/get-current-agent/) |
| **Observability** | `subscribe()`, diagnostics channels, Tail Workers | [Observability](/agents/api-reference/observability/) |

## SQL API

Expand Down
59 changes: 58 additions & 1 deletion src/content/docs/agents/api-reference/chat-agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -213,13 +213,17 @@
}
```

**Accessing custom body data**:
**Accessing custom body data and request ID**:

```ts
export class ChatAgent extends AIChatAgent {
async onChatMessage(_onFinish, options) {
const { timezone, userId } = options?.body ?? {};
// Use these values in your LLM call or business logic

// options.requestId — unique identifier for this chat request,
// useful for logging and correlating events
console.log("Request ID:", options?.requestId);
}
}
```
Expand Down Expand Up @@ -269,6 +273,36 @@

</TypeScriptExample>

### `waitForMcpConnections`

Controls whether `AIChatAgent` waits for MCP server connections to settle before calling `onChatMessage`. This ensures `this.mcp.getAITools()` returns the full set of tools, especially after Durable Object hibernation when connections are being restored in the background.

| Value | Behavior |
| ---------------------- | --------------------------------------------- |
| `{ timeout: 10_000 }` | Wait up to 10 seconds (default) |
| `{ timeout: N }` | Wait up to `N` milliseconds |
| `true` | Wait indefinitely until all connections ready |
| `false` | Do not wait (old behavior before 0.2.0) |

<TypeScriptExample>

```ts
export class ChatAgent extends AIChatAgent {
// Default — waits up to 10 seconds
// waitForMcpConnections = { timeout: 10_000 };

// Wait forever
waitForMcpConnections = true;

// Disable waiting
waitForMcpConnections = false;
}
```

</TypeScriptExample>

For lower-level control, call `this.mcp.waitForConnections()` directly inside your `onChatMessage` instead.

### `persistMessages` and `saveMessages`

For advanced cases, you can manually persist messages:
Expand Down Expand Up @@ -550,6 +584,29 @@

</TypeScriptExample>

#### Custom denial messages with `addToolOutput`

When a user rejects a tool, `addToolApprovalResponse({ id, approved: false })` sets the tool state to `output-denied` with a generic message. To give the LLM a more specific reason for the denial, use `addToolOutput` with `state: "output-error"` instead:

<TypeScriptExample>

```ts
const { addToolOutput } = useAgentChat({ agent });

// Reject with a custom error message
addToolOutput({
toolCallId: part.toolCallId,
state: "output-error",
errorText: "User declined: insufficient budget for this quarter",
});
```

</TypeScriptExample>

This sends a `tool_result` to the LLM with your custom error text, so it can respond appropriately (for example, suggest an alternative or ask clarifying questions).

`addToolApprovalResponse` (with `approved: false`) auto-continues the conversation when `autoContinueAfterToolResult` is enabled (the default). `addToolOutput` with `state: "output-error"` does **not** auto-continue — call `sendMessage()` afterward if you want the LLM to respond to the error.

For more patterns, refer to [Human-in-the-loop](/agents/concepts/human-in-the-loop/).

## Custom request data
Expand Down Expand Up @@ -860,7 +917,7 @@

const anthropic = createAnthropic({ apiKey: this.env.ANTHROPIC_API_KEY });
const result = streamText({
model: anthropic("claude-sonnet-4-20250514"),

Check warning on line 920 in src/content/docs/agents/api-reference/chat-agents.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-year

Potential year found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)
messages: await convertToModelMessages(this.messages),
});
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class MyAgent extends AIChatAgent {

### Built-in vs custom methods

- **Built-in methods** (`onRequest`, `onEmail`, `onStateUpdate`): Already have context.
- **Built-in methods** (`onRequest`, `onEmail`, `onStateChanged`): Already have context.
- **Custom methods** (your methods): Automatically wrapped during initialization.
- **External functions**: Access context through `getCurrentAgent()`.

Expand Down
41 changes: 37 additions & 4 deletions src/content/docs/agents/api-reference/mcp-client-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ Connections persist in the agent's [SQL storage](/agents/api-reference/store-and

## Adding MCP servers

Use `addMcpServer()` to connect to an MCP server:
Use `addMcpServer()` to connect to an MCP server. For non-OAuth servers, no options are needed:

<TypeScriptExample>

```ts
// Simple connection
// Non-OAuth server — no options required
await this.addMcpServer("notion", "https://mcp.notion.so/mcp");

// With explicit callback host
// OAuth server — provide callbackHost for the OAuth redirect flow
await this.addMcpServer("github", "https://mcp.github.com/mcp", {
callbackHost: "https://my-worker.workers.dev",
});
Expand Down Expand Up @@ -119,6 +119,17 @@ await this.addMcpServer("internal", "https://internal-mcp.example.com/mcp", {

</TypeScriptExample>

### URL security

MCP server URLs are validated before connection to prevent Server-Side Request Forgery (SSRF). The following URL targets are blocked:

- Private/internal IP ranges (RFC 1918: `10.x`, `172.16-31.x`, `192.168.x`)
- Loopback addresses (`127.x`, `::1`)
- Link-local addresses (`169.254.x`, `fe80::`)
- Cloud metadata endpoints (`169.254.169.254`)

If you need to connect to an internal MCP server, use the [RPC transport](/agents/model-context-protocol/transport/) with a Durable Object binding instead of HTTP.

### Return value

`addMcpServer()` returns the connection state:
Expand Down Expand Up @@ -445,7 +456,13 @@ function Dashboard() {

### `addMcpServer()`

Add a connection to an MCP server and make its tools available to your agent. If `addMcpServer` is called with a `serverName` that already has an active connection, the existing connection is returned instead of creating a duplicate. This makes it safe to call in `onStart()` without worrying about duplicate connections on restart.
Add a connection to an MCP server and make its tools available to your agent.

Calling `addMcpServer` is idempotent when both the server name **and** URL match an existing active connection — the existing connection is returned without creating a duplicate. This makes it safe to call in `onStart()` without worrying about duplicate connections on restart.

If you call `addMcpServer` with the same name but a **different** URL, a new connection is created. Both connections remain active and their tools are merged in `getAITools()`. To replace a server, call `removeMcpServer(oldId)` first.

URLs are normalized before comparison (trailing slashes, default ports, and hostname case are handled), so `https://MCP.Example.com` and `https://mcp.example.com/` are treated as the same URL.

```ts
// HTTP transport (Streamable HTTP, SSE)
Expand Down Expand Up @@ -812,6 +829,22 @@ type MCPDiscoverResult = {
}
```

#### `this.mcp.waitForConnections()`

Wait for all in-flight MCP connection and discovery operations to settle. This is useful when you need `this.mcp.getAITools()` to return the full set of tools immediately after the agent wakes from hibernation.

```ts
// Wait indefinitely
await this.mcp.waitForConnections();

// Wait with a timeout (milliseconds)
await this.mcp.waitForConnections({ timeout: 10_000 });
```

:::note
`AIChatAgent` calls this automatically via its [`waitForMcpConnections`](/agents/api-reference/chat-agents/#waitformcpconnections) property (defaults to `{ timeout: 10_000 }`). You only need `waitForConnections()` directly when using `Agent` with MCP, or when you want finer control inside `onChatMessage`.
:::

#### `this.mcp.closeConnection()`

Close the connection to a specific server while keeping it registered.
Expand Down
Loading
Loading