INTEGRITY Документация

API обработчиков MCP

Agents SDK предоставляет два пути обработки запросов на сервере:

API Путь импорта Пакет MCP-сервера Поведение
createMcpHandler agents/mcp/server @modelcontextprotocol/server без сохранения состояния с устаревшим режимом совместимости по умолчанию
createLegacyMcpHandler agents/mcp @modelcontextprotocol/sdk устаревшие сессии через WorkerTransport

McpAgent устарел и не получает новых функций. Перенесите существующие McpAgent серверов к обработчику без сохранения состояния. См. руководство по миграции когда функциям с поддержкой сессий требуется поэтапное развёртывание.

Установка зависимостей

Для сервера без сохранения состояния:

npm i agents @modelcontextprotocol/[email protected] zod

Для явного устаревшего сервера:

npm i agents @modelcontextprotocol/[email protected] zod

Используйте именно те версии MCP, которые требует установленная у вас версия Agents.

createMcpHandler

createMcpHandler создаёт вызываемый обработчик запросов MCP без сохранения состояния на основе фабрики сервера MCP SDK v2. Вызывайте его из объекта fetch() экспорт или объедините его внутри другого обработчика.

import {
	createMcpHandler,
	type CreateMcpHandlerOptions,
	type StatelessMcpHandler,
} from "agents/mcp/server";
import type { McpServerFactory } from "@modelcontextprotocol/server";

function createMcpHandler(
	factory: McpServerFactory,
	options?: CreateMcpHandlerOptions,
): StatelessMcpHandler;

Параметры

Factory получает следующий контекст запроса:

interface McpRequestContext {
	era: "modern" | "legacy";
	authInfo?: AuthInfo;
	requestInfo?: Request;
}

Фабрика без аргументов остаётся действительной.

Пример

import { McpServer } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";
import { z } from "zod";

function createServer() {
	const server = new McpServer({
		name: "hello-server",
		version: "1.0.0",
	});

	server.registerTool(
		"hello",
		{
			description: "Return a greeting",
			inputSchema: { name: z.string().optional() },
		},
		async ({ name }) => ({
			content: [{ type: "text", text: `Hello, ${name ?? "World"}!` }],
		}),
	);

	return server;
}

export default {
	fetch(request, env, ctx) {
		return createMcpHandler(createServer)(request, env, ctx);
	},
};
src/index.ts
import { McpServer } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";
import { z } from "zod";

function createServer() {
	const server = new McpServer({
		name: "hello-server",
		version: "1.0.0",
	});

	server.registerTool(
		"hello",
		{
			description: "Return a greeting",
			inputSchema: { name: z.string().optional() },
		},
		async ({ name }) => ({
			content: [{ type: "text", text: `Hello, ${name ?? "World"}!` }],
		}),
	);

	return server;
}

export default {
	fetch(request, env, ctx) {
		return createMcpHandler(createServer)(request, env, ctx);
	},
} satisfies ExportedHandler;

Передавайте саму фабрику. Не создавайте единственный глобальный экземпляр сервера и не передавайте готовый сервер SDK v2 напрямую.

CreateMcpHandlerOptions

Доступны следующие параметры:

Опция Тип По умолчанию Описание
route string "/mcp" Точный путь, обрабатываемый обёрткой Worker
corsOptions CORSOptions | false Wildcard CORS заголовки ответа CORS, или false чтобы удалить их
allowedHostnames string[] Localhost или workers.dev маршрут Необязательное ограничение Host для пользовательских доменов
allowedOriginHostnames string[] | "*" Localhost, workers.dev, либо конкретный CORS Origin ограничение Origin браузера или явное делегирование middleware
authContext McpAuthContext Свойства контекста выполнения Props приложения, возвращаемые getMcpAuthContext()
legacy "stateless" | "reject" "stateless" совместимость с устаревшими версиями или отклонение запросов без сохранения состояния
responseMode "auto" | "json" | "sse" "auto" формирование запросов и ответов без сохранения состояния
onerror (error: Error) => void None Отчёты об ошибках вне обычного канала
maxSubscriptions number 1,024 Максимальное количество одновременных потоков прослушивания
keepAliveMs number 15,000 Интервал keepalive для потоков прослушивания

Параметры транспорта SDK v1 не применяются к этому обработчику. Он отклоняет такие параметры, как transport, storage, sessionIdGenerator, eventStore, а также enableJsonResponse.

Используйте responseMode: "json" вместо enableJsonResponse: true. Режим JSON отбрасывает уведомления, отправленные до финального результата.

Жизненный цикл factory

Handler создаёт один MCP-сервер для каждого запроса. Это соответствует модели черновика протокола, в которой версия, идентичность и возможности передаются с каждым запросом, а не через сессию протокола.

Данные приложения всё равно могут быть долговременными. Храните межзапросные данные за аутентифицированным хендлом в Durable Object, D1, KV или R2, а не привязывайте их к ID сессии MCP.

Elicitation с обработчиком без сохранения состояния

Elicitation через обработчик без сохранения состояния возвращает input_required и завершается через многораундовые запросы (MRTR). При каждой повторной попытке SDK повторно отправляет последний requestState и отправляет ответы только на непосредственно предшествующий раунд ввода. Он не накапливает более ранние inputResponses. Worker не остаётся приостановленным, пока пользователь отвечает.

Используйте inputRequired(...) чтобы запросить ввод. Считайте содержимое принятой формы за этот ход из context.mcpReq.inputResponses с acceptedContent(...). Запечатывайте доверенные промежуточные значения, необходимые для последующих раундов, в защищённые от изменения requestState.

См. пример elicitation без сохранения состояния ↗ для двухраундового потока вызова инструмента. Для push-запросов с сохранением состояния см. Elicitation на устаревших серверах.

Проверка Origin и CORS

Обёртка Workers проверяет каждое присутствующее значение Origin браузера. Она отклоняет некорректные, непрозрачные и не HTTP значения Origin с 403. MCP-клиенты не из браузера и без Origin остаются допустимыми.

Стандартный allowlist включает значения Origin типа localhost, эндпоинта workers.dev hostname и конкретный hostname из corsOptions.origin. Обработчик также применяет аналогичные проверки Host к localhost и workers.dev эндпойнты. Это сохраняет локальную защиту от DNS rebinding без необходимости отдельного списка Origin для типовых маршрутов Workers.

Для пользовательского домена с wildcard-CORS установите allowedHostnames и allowedOriginHostnames явно. Если corsOptions.origin это конкретный URL, обработчик автоматически определяет его имя хоста Origin:

export default {
	fetch(request, env, ctx) {
		return createMcpHandler(createServer, {
			allowedHostnames: ["mcp.example.com"],
			corsOptions: {
				origin: "https://app.example.com",
			},
		})(request, env, ctx);
	},
};
export default {
	fetch(request, env, ctx) {
		return createMcpHandler(createServer, {
			allowedHostnames: ["mcp.example.com"],
			corsOptions: {
				origin: "https://app.example.com",
			},
		})(request, env, ctx);
	},
} satisfies ExportedHandler;

Значения allowlist представляют собой имена хостов без схемы и порта. Сопоставление origin игнорирует схему и порт.

Задайте allowedOriginHostnames: "*" только если доверенный middleware проверяет Origin перед вызовом обработчика. Это значение отключает проверку Origin в обработчике, включая отклонение некорректных и непрозрачных значений Origin. HTTP-серверы MCP обязаны проверять Origin браузера.

Заголовки ответа CORS не являются аутентификацией. Защитите эндпойнт MCP с помощью OAuth или другого уровня аутентификации.

Handler не выводит allowlist значений Host из request.url. Если развёртывание принимает произвольные значения Host, проверяйте их перед вызовом обработчика. Локальным серверам за пределами Cloudflare Workers следует придерживаться рекомендаций upstream SDK по защите от DNS rebinding.

Совместимость с устаревшими клиентами

По умолчанию legacy: "stateless" параметр принимает обычные устаревшие инструменты, промпты и ресурсы. Этот вариант использует транспорт SDK v2 на основе веб-стандартов и не импортирует WorkerTransport.

Этот путь совместимости не обеспечивает полный транспорт сессии:

Задайте legacy: "reject" для эндпойнта, работающего только без сохранения состояния. Во время миграции направляйте устаревшие клиенты, которым всё ещё нужны сессии протокола, на временный createLegacyMcpHandler или McpAgent дорожка.

Возвращаемое значение

createMcpHandler возвращает StatelessMcpHandler. Он вызываемый и предоставляет средства управления запросами и уведомлениями:

interface StatelessMcpHandler {
	(request: Request, env: unknown, ctx: ExecutionContext): Promise<Response>;

	fetch(
		request: Request,
		options?: McpHandlerRequestOptions,
	): Promise<Response>;

	notify: {
		toolsChanged(): void;
		promptsChanged(): void;
		resourcesChanged(): void;
		resourceUpdated(uri: string): void;
	};
}

type McpHandlerRequestOptions = {
	authInfo?: AuthInfo;
	parsedBody?: unknown;
};

Вызов обработчика

Вызовите обработчик из объекта Worker fetch() экспорт:

export default {
	fetch(request, env, ctx) {
		return createMcpHandler(createServer)(request, env, ctx);
	},
} satisfies ExportedHandler;

Не экспортируйте вызываемый объект напрямую как экспорт Worker по умолчанию. Wrangler считает экспорты функций по умолчанию WorkerEntrypoint классы.

Используйте fetch() когда другой фреймворк или уровень аутентификации уже разобрал или проверил данные запроса:

const response = await handler.fetch(request, {
	authInfo,
	parsedBody,
});

authInfo передаётся фабрике сервера и обработчикам запросов. Обработчик не извлекает его из заголовков запроса и не проверяет токены доступа. parsedBody позволяет избежать повторного разбора тела JSON, которое уже было считано вышестоящим middleware.

Публикация изменений списков и ресурсов

notify методы публикуют типизированные события изменений в подходящие открытые subscriptions/listen потоки:

Метод MCP-уведомление
notify.toolsChanged() notifications/tools/list_changed
notify.promptsChanged() notifications/prompts/list_changed
notify.resourcesChanged() notifications/resources/list_changed
notify.resourceUpdated(uri) notifications/resources/updated

Вызов notifier при отсутствии подходящей открытой подписки ничего не делает.

Используйте единый обработчик для уведомлений

Маршрутизация уведомлений принадлежит экземпляру обработчика. Создание нового обработчика внутри каждого Worker fetch() вызов подходит для обычных инструментов, промптов, ресурсов и MRTR elicitation. Он не может уведомить subscriptions/listen поток, принадлежащий более раннему экземпляру обработчика.

Создайте обработчик один раз в области видимости модуля при использовании notify или subscriptions/listen, затем вызовите его из экспорта объекта Worker:

const handler = createMcpHandler(createServer);

export default {
	fetch(request, env, ctx) {
		return handler(request, env, ctx);
	},
} satisfies ExportedHandler;

Уведомления привязаны к isolate. Уведомление, опубликованное в одном Worker isolate, не достигает потока подписки, выполняющегося в другом isolate.

createLegacyMcpHandler

createLegacyMcpHandler обслуживает сервер SDK v1 через WorkerTransport.

import {
	createLegacyMcpHandler,
	type CreateLegacyMcpHandlerOptions,
	type LegacyMcpHandler,
} from "agents/mcp";
import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

function createLegacyMcpHandler(
	server: McpServer | Server,
	options?: CreateLegacyMcpHandlerOptions,
): LegacyMcpHandler;

Используйте этот обработчик только как временный мост миграции, если существующий эндпойнт SDK v1 всё ещё требует устаревших сессий, хранилища транспорта, повторного воспроизведения событий или push-запросов от сервера к клиенту.

src/index.js
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { createLegacyMcpHandler } from "agents/mcp";

function createServer() {
	return new McpServer({ name: "legacy-server", version: "1.0.0" });
}

export default {
	async fetch(request, env, ctx) {
		return createLegacyMcpHandler(createServer())(request, env, ctx);
	},
};
src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { createLegacyMcpHandler } from "agents/mcp";

function createServer() {
	return new McpServer({ name: "legacy-server", version: "1.0.0" });
}

export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext) {
		return createLegacyMcpHandler(createServer())(request, env, ctx);
	},
} satisfies ExportedHandler<Env>;

Передача сервера SDK v1 в createMcpHandler всё ещё работает, но выдаёт предупреждение об устаревании. Перенесите сервер на фабрику SDK v2 и передайте фабрику в createMcpHandler. Если sessionful-поведение мешает немедленной миграции, используйте createLegacyMcpHandler только на временном устаревшем пути.

experimental_createMcpHandler также устарел. Перенесите его сервер SDK v1 на фабрику SDK v2. Используйте createLegacyMcpHandler только как временный мост для поведения с поддержкой сессий.

CreateLegacyMcpHandlerOptions

CreateLegacyMcpHandlerOptions расширяет WorkerTransportOptions и добавляет следующие поля:

Опция Тип По умолчанию Описание
route string "/mcp" Точный путь, обрабатываемый обработчиком
authContext McpAuthContext Свойства контекста выполнения Props приложения для обработчиков инструментов
transport WorkerTransport Новый транспорт Постоянный или заранее настроенный транспорт

Типичные WorkerTransportOptions включают:

Опция Описание
sessionIdGenerator Создаёт ID сеансов протокола
enableJsonResponse Возвращает JSON вместо SSE, если это поддерживается
storage Сохраняет состояние транспорта через { get, set } адаптер
eventStore Сохраняет события для повторного воспроизведения и восстановления потока
corsOptions Добавляет заголовки ответа CORS и preflight-запросов
onsessioninitialized, onsessionclosed Отслеживать изменения жизненного цикла сессии

Создавайте новый сервер SDK v1 для каждого запроса, если только вы не предоставляете постоянный транспорт, уже подключённый к этому серверу. Один сервер не может переподключаться к нескольким транспортам.

Контекст аутентификации

Совместимый @cloudflare/workers-oauth-provider предоставляет проверенные стандартные AuthInfo к колбэкам SDK v2 по адресу context.http.authInfo.

Существующий getMcpAuthContext() вспомогательная функция по-прежнему возвращает props приложения:

interface McpAuthContext {
	props: Record<string, unknown>;
}
import { getMcpAuthContext } from "agents/mcp/server";

server.registerTool(
	"whoami",
	{ description: "Return the current identity", inputSchema: {} },
	async (_args, context) => {
		const auth = getMcpAuthContext();

		return {
			content: [
				{
					type: "text",
					text: JSON.stringify({
						clientId: context.http?.authInfo?.clientId,
						scopes: context.http?.authInfo?.scopes,
						userId: auth?.props.userId,
					}),
				},
			],
		};
	},
);
import { getMcpAuthContext } from "agents/mcp/server";

server.registerTool(
	"whoami",
	{ description: "Return the current identity", inputSchema: {} },
	async (_args, context) => {
		const auth = getMcpAuthContext();

		return {
			content: [
				{
					type: "text",
					text: JSON.stringify({
						clientId: context.http?.authInfo?.clientId,
						scopes: context.http?.authInfo?.scopes,
						userId: auth?.props.userId,
					}),
				},
			],
		};
	},
);

Не записывайте в лог и не возвращайте authInfo.token или authInfo.extra.props.

Переход

См. Мигрировать на MCP SDK v2 перед изменением существующего сервера. Руководство по миграции описывает маршрутизацию между старой и новой версией, серверы с сохранением состояния, изменения на стороне клиента и проверки при развёртывании.

Мигрировать на MCP SDK v2

Выберите путь миграции и выполните обновление SDK.

McpAgent API

Справочник по устаревшему пути сервера с сохранением состояния, который не получает новых функций, для использования во время миграции.

Защита MCP-серверов

Защитите эндпойнт MCP с помощью OAuth.