← Создание агентов на Cloudflare / agents / model-context-protocol / protocol
Транспорт
Спецификация Model Context Protocol (MCP) определяет два стандартных транспортные механизмы ↗ для взаимодействия между клиентами и серверами:
- stdio : Обмен данными через стандартный ввод и стандартный вывод, предназначенный для локальных подключений MCP.
- Streamable HTTP : стандартный способ транспорта для удалённых подключений MCP, введено ↗ в марте 2025 года. Он использует единую HTTP конечную точку для двусторонней передачи сообщений.
MCP-серверы, созданные с помощью Agents SDK используйте createMcpHandler чтобы обрабатывать транспорт Streamable HTTP.
Реализация удалённого транспорта MCP
Используйте createMcpHandler чтобы создать MCP-сервер, который обрабатывает транспорт Streamable HTTP. Это рекомендуемый подход для новых MCP-серверов.
Быстрое начало работы
Вы можете создать удалённый MCP-сервер с помощью кнопки «Deploy to Cloudflare».
Удалённый MCP-сервер (без аутентификации)
Создайте MCP-сервер с помощью createMcpHandler. Посмотрите полный пример на GitHub ↗.
import { createMcpHandler } from "agents/mcp/server";
import { McpServer } from "@modelcontextprotocol/server";
import { z } from "zod";
function createServer() {
const server = new McpServer({
name: "My MCP Server",
version: "1.0.0",
});
server.registerTool(
"hello",
{
description: "Returns a greeting message",
inputSchema: { name: z.string().optional() },
},
async ({ name }) => {
return {
content: [{ text: `Hello, ${name ?? "World"}!`, type: "text" }],
};
},
);
return server;
}
export default {
fetch(request, env, ctx) {
return createMcpHandler(createServer)(request, env, ctx);
},
};import { createMcpHandler } from "agents/mcp/server";
import { McpServer } from "@modelcontextprotocol/server";
import { z } from "zod";
function createServer() {
const server = new McpServer({
name: "My MCP Server",
version: "1.0.0",
});
server.registerTool(
"hello",
{
description: "Returns a greeting message",
inputSchema: { name: z.string().optional() },
},
async ({ name }) => {
return {
content: [{ text: `Hello, ${name ?? "World"}!`, type: "text" }],
};
},
);
return server;
}
export default {
fetch(request, env, ctx) {
return createMcpHandler(createServer)(request, env, ctx);
},
} satisfies ExportedHandler;MCP-сервер с аутентификацией
Если ваш сервер MCP реализует аутентификацию и авторизацию с помощью Workers OAuth Provider ↗ библиотеку, используйте createMcpHandler на apiRoute и apiHandler свойства. Просмотрите полный пример на GitHub ↗.
export default new OAuthProvider({
apiRoute: "/mcp",
apiHandler: createMcpHandler(createServer),
// ... other OAuth configuration
});export default new OAuthProvider({
apiRoute: "/mcp",
apiHandler: createMcpHandler(createServer),
// ... other OAuth configuration
});Серверы, которым нужны сессии протокола
У MCP нет сессии на уровне протокола на пути без сохранения состояния. Приложения могут хранить устойчивые бизнес-данные за отдельной границей хранилища.
При миграции устаревших сессий существующие серверы могут временно сохранять createLegacyMcpHandler с WorkerTransport или McpAgent route рядом с новым stateless route. Эти API поддерживают transport state, event replay, pushed elicitation, sampling и запросы roots. McpAgent устарел и не получает новых функций.
Добавьте маршрут без сохранения состояния перед переносом клиентов и сохраняйте обе линии, пока не завершатся существующие сессии. См. Мигрировать на MCP SDK v2 для поэтапной миграции. См. McpAgent: Возобновляемость потока для существующего поведения потока.
RPC-транспорт
RPC-транспорт предназначен для внутренних приложений, где ваш MCP-сервер и агент работают на Cloudflare: они могут даже работать в одном Worker. Он отправляет сообщения JSON-RPC напрямую через RPC-привязки не выходя в публичный интернет.
- Быстрее : никаких накладных расходов на сеть, прямые вызовы функций между Durable Object
- Проще : нет HTTP-эндпоинтов, нет управления соединениями
- Только внутренние : идеально подходит для агентов, вызывающих MCP-серверы в рамках того же Worker
RPC-транспорт не поддерживает аутентификацию. Для внешних подключений, требующих OAuth, используйте Streamable HTTP.
Подключите Agent к существующему McpAgent через RPC
1. Определите свой MCP-сервер
Создайте свой McpAgent с инструментами, которые вы хотите предоставить:
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export class MyMCP extends McpAgent {
server = new McpServer({ name: "MyMCP", version: "1.0.0" });
initialState = { counter: 0 };
async init() {
this.server.tool(
"add",
"Add to the counter",
{ amount: z.number() },
async ({ amount }) => {
this.setState({ counter: this.state.counter + amount });
return {
content: [
{
type: "text",
text: `Added ${amount}, total is now ${this.state.counter}`,
},
],
};
},
);
}
}import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
type State = { counter: number };
export class MyMCP extends McpAgent<Env, State> {
server = new McpServer({ name: "MyMCP", version: "1.0.0" });
initialState: State = { counter: 0 };
async init() {
this.server.tool(
"add",
"Add to the counter",
{ amount: z.number() },
async ({ amount }) => {
this.setState({ counter: this.state.counter + amount });
return {
content: [
{
type: "text",
text: `Added ${amount}, total is now ${this.state.counter}`,
},
],
};
},
);
}
}2. Подключите свой Agent к MCP-серверу
В вашем Agent, вызовите addMcpServer() с привязкой Durable Object в onStart():
import { AIChatAgent } from "@cloudflare/ai-chat";
export class Chat extends AIChatAgent {
async onStart() {
// Pass the DO namespace binding directly
await this.addMcpServer("my-mcp", this.env.MyMCP);
}
async onChatMessage(onFinish) {
const allTools = this.mcp.getAITools();
const result = streamText({
model,
tools: allTools,
// ...
});
return createUIMessageStreamResponse({ stream: result });
}
}import { AIChatAgent } from "@cloudflare/ai-chat";
export class Chat extends AIChatAgent<Env> {
async onStart(): Promise<void> {
// Pass the DO namespace binding directly
await this.addMcpServer("my-mcp", this.env.MyMCP);
}
async onChatMessage(onFinish) {
const allTools = this.mcp.getAITools();
const result = streamText({
model,
tools: allTools,
// ...
});
return createUIMessageStreamResponse({ stream: result });
}
}RPC-соединения автоматически восстанавливаются после гибернации Durable Object, как и HTTP-соединения. Имя привязки и props сохраняются в хранилище, поэтому соединение можно восстановить без дополнительного кода.
Для транспорта RPC, если addMcpServer вызывается с именем, для которого уже есть активное соединение, возвращается существующее соединение вместо создания дубликата. Для транспорта HTTP дедупликация выполняется как по имени сервера, так и по URL (см. API MCP-клиента для подробностей). Это делает безопасным вызов в onStart().
3. Настройте привязки Durable Object
В вашем wrangler.jsonc, определите привязки для обоих Durable Objects:
{
"durable_objects": {
"bindings": [
{ "name": "Chat", "class_name": "Chat" },
{ "name": "MyMCP", "class_name": "MyMCP" },
],
},
"migrations": [
{
"new_sqlite_classes": ["MyMCP", "Chat"],
"tag": "v1",
},
],
}4. Настройте обработчик fetch для Worker
Маршрутизируйте запросы к своему Чат-агенту:
import { routeAgentRequest } from "agents";
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// Optionally expose the MCP server via HTTP as well
if (url.pathname.startsWith("/mcp")) {
return MyMCP.serve("/mcp").fetch(request, env, ctx);
}
const response = await routeAgentRequest(request, env);
if (response) return response;
return new Response("Not found", { status: 404 });
},
};import { routeAgentRequest } from "agents";
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const url = new URL(request.url);
// Optionally expose the MCP server via HTTP as well
if (url.pathname.startsWith("/mcp")) {
return MyMCP.serve("/mcp").fetch(request, env, ctx);
}
const response = await routeAgentRequest(request, env);
if (response) return response;
return new Response("Not found", { status: 404 });
},
} satisfies ExportedHandler<Env>;Передача props MCP-серверу
Поскольку у RPC-транспорта нет потока OAuth, вы можете передавать контекст пользователя напрямую через props:
await this.addMcpServer("my-mcp", this.env.MyMCP, {
props: { userId: "user-123", role: "admin" },
});await this.addMcpServer("my-mcp", this.env.MyMCP, {
props: { userId: "user-123", role: "admin" },
});Ваш McpAgent затем могут обращаться к этим props:
export class MyMCP extends McpAgent {
async init() {
this.server.tool("whoami", "Get current user info", {}, async () => {
const userId = this.props?.userId || "anonymous";
const role = this.props?.role || "guest";
return {
content: [{ type: "text", text: `User ID: ${userId}, Role: ${role}` }],
};
});
}
}export class MyMCP extends McpAgent<
Env,
State,
{ userId?: string; role?: string }
> {
async init() {
this.server.tool("whoami", "Get current user info", {}, async () => {
const userId = this.props?.userId || "anonymous";
const role = this.props?.role || "guest";
return {
content: [{ type: "text", text: `User ID: ${userId}, Role: ${role}` }],
};
});
}
}Props типобезопасны (TypeScript извлекает тип Props из вашего McpAgent обобщённый тип), постоянные (хранятся в хранилище Durable Object) и доступны сразу же, ещё до каких-либо вызовов инструментов.
Настройка тайм-аута транспортного сервера RPC
Транспорт RPC имеет настраиваемый тайм-аут ожидания ответов инструментов. По умолчанию сервер ждёт 60 секунд для ответа обработчика инструмента. Это можно настроить, переопределив getRpcTransportOptions() в вашем McpAgent:
export class MyMCP extends McpAgent {
server = new McpServer({ name: "MyMCP", version: "1.0.0" });
getRpcTransportOptions() {
return { timeout: 120000 }; // 2 minutes
}
async init() {
this.server.tool(
"long-running-task",
"A tool that takes a while",
{ input: z.string() },
async ({ input }) => {
await longRunningOperation(input);
return {
content: [{ type: "text", text: "Task completed" }],
};
},
);
}
}export class MyMCP extends McpAgent<Env, State> {
server = new McpServer({ name: "MyMCP", version: "1.0.0" });
protected getRpcTransportOptions() {
return { timeout: 120000 }; // 2 minutes
}
async init() {
this.server.tool(
"long-running-task",
"A tool that takes a while",
{ input: z.string() },
async ({ input }) => {
await longRunningOperation(input);
return {
content: [{ type: "text", text: "Task completed" }],
};
},
);
}
}Выбор транспорта
| Транспорт | Когда использовать | Плюсы | Недостатки |
|---|---|---|---|
| Streamable HTTP | Внешние серверы MCP, продакшен-приложения | Стандартный протокол, безопасный, поддерживает аутентификацию | Небольшие накладные расходы на сеть |
| RPC | Внутренние агенты на Cloudflare | Самая быстрая и простая настройка | Без аутентификации, только привязки Durable Object |
| SSE | Совместимость с более старыми клиентами | Обратно совместимый | Устарело, используйте Streamable HTTP |
Мигрировать с McpAgent
Если эндпойнт не использует устаревшие функции с сохранением состояния, переходите сразу на фабрику сервера без сохранения состояния из @modelcontextprotocol/server и передайте его в createMcpHandler.
Если это зависит от состояния сессии MCP, RPC, отправляемых от сервера к клиенту запросов, автономных потоков или replay, сначала спроектируйте эквиваленты без сохранения состояния. Например, перенесите бизнес-состояние в явное хранилище приложения и замените отправляемые запросы на ввод elicitation без сохранения состояния. Обслуживайте путь без сохранения состояния и устаревший путь одновременно, пока клиенты не перейдут, а существующие сессии не завершатся.
См. Мигрировать на MCP SDK v2 для сопоставления функций, маршрутизации между двумя поколениями и шагов развёртывания.
Тестирование с MCP-клиентами
Вы можете протестировать свой MCP-сервер с помощью MCP-клиента, поддерживающего удалённые подключения, либо использовать mcp-remote ↗, адаптер, который позволяет MCP клиентам, поддерживающим только локальные подключения, работать с удалёнными MCP серверами.
Следуйте это руководство для инструкций о том, как подключить ваш удалённый MCP-сервер к Claude Desktop, Cursor, Windsurf и другим MCP-клиентам.