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

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

Это руководство охватывает MCP SDK v2 ↗ обновление в Agents SDK v0.20.0. Оно объясняет, как перенести серверы на @modelcontextprotocol/server, используйте временный устаревший канал только тогда, когда этого требуют функции с состоянием сессии, и обновляйте MCP-клиенты.

Выберите путь сервера

Используйте следующую таблицу, чтобы выбрать путь миграции:

Текущий сервер Путь миграции
Сервер SDK v1 без зависимостей от сессии Перенесите определение сервера в фабрику SDK v2 и передайте фабрику в createMcpHandler.
Сервер SDK v1 с зависимостями от сессии Добавьте SDK v2 route. Оставьте createLegacyMcpHandler только на временном устаревшем пути, пока эти зависимости заменяются.
McpAgent без устаревших функций с сохранением состояния Мигрировать напрямую на фабрику SDK v2 и createMcpHandler.
McpAgent который использует устаревшие функции с сохранением состояния Спроектируйте эквиваленты без сохранения состояния, обслуживайте путь без сохранения состояния и устаревший путь одновременно, а затем опустошите устаревший путь.

Agents SDK v0.20.0 помечает следующие API как устаревшие:

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

Установка пакетов MCP

Устанавливайте только те поколения пакета MCP, которые импортирует ваше приложение. Версию v2 сохраняйте точно.

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

npm i agents @modelcontextprotocol/[email protected] zod

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

npm i agents @modelcontextprotocol/[email protected] zod

Для Agent, который подключается к серверам MCP:

npm i agents @modelcontextprotocol/[email protected]

Следуйте указаниям вашего менеджера пакетов о peer-зависимостях. Обновляйте конкретные версии MCP вместе с релизом Agents, который их поддерживает.

Определите, нужен ли временный устаревший канал

Не оставляйте SDK v1 только потому, что сервер сейчас его импортирует. Переходите сразу на фабрику SDK v2, если конечная точка не зависит ни от одной из этих функций, зависящих от сессии:

Если эндпойнт использует одну из этих функций, разверните сначала маршрут без сохранения состояния. Оставляйте маршрут SDK v1 только до тех пор, пока не замените зависимость от сессий. Направляйте запросы с помощью isLegacyRequest() как показано в Совместный запуск каналов без сохранения состояния и устаревших каналов.

Для эндпойнта SDK v1, который не использует McpAgent, используйте createLegacyMcpHandler только на этой временной устаревшей ветке. Удалите её после того, как клиенты мигрируют, а существующие сессии завершатся.

Перенос сервера без сохранения состояния на SDK v2

Стейтлес createMcpHandler принимает фабрику. Фабрика возвращает McpServer или Server от @modelcontextprotocol/server.

  1. Следуйте указаниям upstream Руководство по миграции TypeScript SDK v2 ↗ для изменений регистрации сервера.

  2. Импортируйте сервер из @modelcontextprotocol/server.

  3. Перенесите создание и регистрацию сервера в фабрику.

  4. Сохраните экспорт объекта Worker по умолчанию. Внутри его fetch() метод, передайте фабрику в createMcpHandler и вызывайте возвращённый вызываемый объект как раньше. Используйте fetch(request, options?) метод только для низкоуровневой интеграции запросов.

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

  6. Удалите параметры транспорта и сессии SDK v1.

  7. Проверьте эндпойнт с помощью stateless- и legacy-клиентов.

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

function createServer() {
	const server = new McpServer({
		name: "example-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: "example-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;

Точка входа Worker остаётся объектом. Меняется только вызов обработчика:

// SDK v1: pass a fresh constructed server.
return createMcpHandler(createServer())(request, env, ctx);

// SDK v2: pass the factory itself.
return createMcpHandler(createServer)(request, env, ctx);

Не упрощайте это до export default createMcpHandler(createServer). Обработчик Agents можно вызывать для композиции внутри другого обработчика, но Wrangler рассматривает любой экспорт функции по умолчанию как WorkerEntrypoint в качестве класса.

Обработчик SDK v2 создаёт отдельный сервер для каждого запроса MCP. Параллельные запросы Worker никогда не используют общий экземпляр подключённого сервера.

Параметры обработчика для серверов без сохранения состояния

Обёртка Agents добавляет route, corsOptions, allowedHostnames, allowedOriginHostnames, а также authContext. Она также передаёт поддерживаемые параметры SDK v2 в upstream-обработчик.

К типичным параметрам относятся:

Опция Поведение
route Задаёт точный путь запроса. Значение по умолчанию: /mcp.
legacy По умолчанию используется устаревший режим совместимости. Задайте "reject" для эндпойнта, работающего только без сохранения состояния.
responseMode Выбирает автоматическую, JSON или SSE обработку ответов.
allowedHostnames Ограничивает заголовки Host конкретными именами хостов.
allowedOriginHostnames Ограничивает браузерные Origins или принимает "*" когда доверенный middleware проверяет их.
corsOptions Управляет заголовками ответа CORS. Установите false чтобы удалить их.
onerror Сообщает об ошибках обработчика, не изменяя ответ.
maxSubscriptions, keepAliveMs Настройте subscriptions/listen доставка.

Стейтлес-обработчик отклоняет следующие опции SDK v1:

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

Проверка Origin в Workers

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

Список разрешённых источников по умолчанию включает источники класса localhost и конечной точки workers.dev hostname. Конкретный corsOptions.origin также автоматически добавляет своё имя хоста. Обработчик применяет соответствующие проверки Host к localhost и workers.dev конечные точки.

Для пользовательского домена с wildcard-CORS явно настройте ограничения и Host, и Origin:

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

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

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

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

Разберитесь с совместимостью с устаревшими клиентами

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

Fallback имеет следующие ограничения:

При миграции этих функций направляйте затронутых устаревших клиентов на временный createLegacyMcpHandler или McpAgent дорожка.

Мигрировать McpAgent сервер

Во время миграции, McpAgent остаётся сервером SDK v1. Не меняйте импорт сервера внутри устаревшего route на @modelcontextprotocol/server.

Мигрировать напрямую без устаревших функций с сохранением состояния

Если сервер не зависит от состояния сессии MCP, RPC, отправляемых от сервера к клиенту запросов, автономных потоков или воспроизведения событий, перенесите его инструменты в фабрику SDK v2 и обслуживайте его с помощью createMcpHandler.

Спланируйте эквиваленты без сохранения состояния для функций с сохранением состояния

Если сервер использует устаревшие функции с сохранением состояния, оставьте существующий McpAgent route, пока вы проектируете и развёртываете stateless-эквиваленты:

Функция с сохранением состояния на устаревшем пути Спроектируйте путь без сохранения состояния
Данные приложения с ключом по сессии MCP Храните данные за явной границей приложения, например в Durable Object, D1, KV или R2. Обращайтесь к ним через аутентифицированный идентификатор, выданный сервером, а не через ID сессии MCP.
Состояние многошагового взаимодействия Верните защищённые по целостности requestState с input_required. Привяжите его к аутентифицированному пользователю, исходному методу и параметрам, а также сроку действия.
Отправленные запросы elicitation, sampling или roots Верните inputRequired(...). Клиент выполняет встроенные запросы и повторяет исходную операцию.
Отдельный поток изменений списка Публикация изменений через subscriptions/listen. Клиенты заново открывают подписку, если её поток завершается.
Воспроизведение Session или восстановление транспорта Делайте каждый запрос без сохранения состояния независимо восстанавливаемым. Сохраняйте бизнес-прогресс в хранилище приложения, а не в транспорте MCP.
Agent-to-McpAgent RPC Замените зависимость от сессии протокола явной границей RPC или HTTP на уровне приложения, а затем предоставьте инструменты MCP без сохранения состояния отдельно.

Не удаляйте устаревший route сразу после появления реализации без сохранения состояния. Обслуживайте оба пути, пока клиенты не перейдут, а существующие сессии не завершатся.

Совместный запуск каналов без сохранения состояния и устаревших каналов

Один URL может маршрутизировать запросы без сохранения состояния на SDK v2, а устаревшие запросы направлять на существующий сервер с поддержкой сессий.

import { isLegacyRequest } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";

const stateless = createMcpHandler(createStatelessServer, {
	route: "/mcp",
	legacy: "reject",
});

const legacy = MyMcpAgent.serve("/mcp");

export default {
	async fetch(request, env, ctx) {
		if (await isLegacyRequest(request)) {
			return legacy.fetch(request, env, ctx);
		}
		return stateless(request, env, ctx);
	},
};
import { isLegacyRequest } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";

const stateless = createMcpHandler(createStatelessServer, {
	route: "/mcp",
	legacy: "reject",
});

const legacy = MyMcpAgent.serve("/mcp");

export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext) {
		if (await isLegacyRequest(request)) {
			return legacy.fetch(request, env, ctx);
		}
		return stateless(request, env, ctx);
	},
} satisfies ExportedHandler<Env>;

Оставьте legacy: "reject" для обработчика без сохранения состояния. Иначе его устаревший режим совместимости перехватывает запросы раньше, чем их получает маршрут с сохранением сессии.

Разверните оба маршрута, прежде чем переводить на них клиентов. Отслеживайте устаревший канал и дайте существующим сессиям завершиться естественным образом. Удаляйте устаревший маршрут и его привязку Durable Object, предназначенную только для протокола, только после того, как от них перестанут зависеть все клиенты. Настройку миграции Durable Object выполняйте отдельным этапом развёртывания.

Обновите клиенты MCP

Agents теперь использует @modelcontextprotocol/client внутренне. Существующие addMcpServer вызовы автоматически согласовывают поколение протокола.

Серверы на маршруте без сохранения состояния используют server/discover. Agents откатывается к initialize для устаревших серверов Streamable HTTP, SSE и RPC.

Клиентский API включает следующие изменения:

Настройка elicitation для запросов без сохранения состояния

Инструменты, промпты и ресурсы в пути без сохранения состояния могут возвращать input_required через запросы с несколькими раундами обмена (MRTR). SDK вызывает настроенный обработчик elicitation и повторяет исходную операцию. Ваш исходный callTool, getPrompt, или readResource promise остаётся в состоянии ожидания.

Каждая повторная попытка содержит ответы только на непосредственно предшествующий раунд ввода, а не на все более ранние ответы. Клиент также повторно отправляет последний непрозрачный requestState. Запечатывайте доверенные промежуточные значения, необходимые для последующих раундов, в защищённые от изменения requestState; не ожидайте inputResponses накапливаться между раундами.

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

export class MyAgent extends Agent {
	onStart() {
		this.mcp.configureElicitationHandlers({
			form: async (request, serverId, signal) => {
				return collectInput(request, serverId, signal);
			},
			url: async (request, serverId, signal) => {
				return openExternalFlow(request, serverId, signal);
			},
		});
	}
}
export class MyAgent extends Agent<Env> {
	onStart() {
		this.mcp.configureElicitationHandlers({
			form: async (request, serverId, signal) => {
				return collectInput(request, serverId, signal);
			},
			url: async (request, serverId, signal) => {
				return openExternalFlow(request, serverId, signal);
			},
		});
	}
}

Обработчики и выполняющиеся вызовы остаются в памяти. Гибернация, перезапуск isolate, потеря транспорта или восстановление соединения приводят к отклонению активного интерактивного вызова. Повторите операцию после восстановления соединения.

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

Обновите собственных провайдеров OAuth

Пользовательский AgentMcpOAuthProvider должен реализовывать v2 OAuthClientProvider контракт:

SDK v2 по умолчанию проверяет издателей метаданных OAuth. Доверенный устаревший сервер с заведомо несовпадающими метаданными может использовать skipIssuerMetadataValidation: true. Это ослабляет защиту от OAuth mix-up и не должно использоваться как запасной вариант по умолчанию.

Обзор различий протоколов

Модель MCP без сохранения состояния меняет транспорт и жизненный цикл:

Область Предыдущее поведение Новое поведение
Запуск initialize handshake Без handshake. server/discover необязателен для клиентов
Метаданные запроса Согласование в рамках подключения Версия, возможности клиента и метаданные идентичности при каждом запросе
Сеансы Необязательно Mcp-Session-Id Нет сессии протокола
Запросы сервера на ввод Сервер отправляет запросы JSON-RPC Сервер возвращает input_required. Клиент повторяет исходную операцию
Уведомления об изменениях Отдельный GET-поток и уведомления об изменении списка subscriptions/listen POST с ответом SSE
Восстановление потока Last-Event-ID может возобновлять настроенные потоки Потоки прослушивания переоткрываются после сбоя. Здесь отсутствует Last-Event-ID воспроизведении.

Пользовательские транспорты, прокси и шлюзы должны сохранять заголовки черновика запроса:

Точная версия SDK, используемая Agents, реализует редакцию MCP 2026-07-28. Она делает clientInfo необязательный и помещает идентификатор сервера в результат _meta. Используйте высокоуровневые API SDK и обновляйте пакеты MCP вместе с релизом Agents, поддерживающим соответствующую версию протокола. Необработанные результаты без сохранения состояния должны включать resultType.

Черновик спецификации помечает Roots, Sampling, Logging, старый транспорт HTTP+SSE и Dynamic Client Registration как устаревшие. На время переходного периода эти типы остаются доступны для обратной совместимости со старыми версиями. Опубликованные экспериментальные методы задач становятся io.modelcontextprotocol/tasks расширение. Agents SDK v0.20.0 не добавляет это расширение.

Совместимость интеграции

В этом релизе следующие интеграции сохраняют формат вывода сервера SDK v1:

Пока эти интеграции не начнут выдавать серверы SDK v2, изолируйте их вывод за временным createLegacyMcpHandler route. Коннектор Code Mode MCP и withX402Client принимают клиента любого поколения.

Спланируйте развёртывание

  1. Классифицируйте каждый эндпойнт по его зависимостям от сессии.

  2. Зафиксируйте версии MCP SDK, которые требует релиз Agents.

  3. Перенесите каждое определение сервера, которое может быть без сохранения состояния, в фабрику SDK v2.

  4. Добавьте обработчики без сохранения состояния рядом с конечными точками, которым по-прежнему нужен временный устаревший путь.

  5. Маршрутизируйте запросы без сохранения состояния и устаревшие запросы с помощью isLegacyRequest().

  6. Тестируйте stateless- и legacy-клиентов по отдельности.

  7. Проверьте обязательные HTTP-заголовки на каждом прокси и шлюзе.

  8. Протестируйте OAuth после чистого входа и после гибернации Durable Object.

  9. Протестируйте отмену, несколько раундов ввода и потерю транспорта.

  10. Проверяйте допустимые Origins и отклоняйте недопустимые Origins с 403.

  11. Удаляйте устаревшие маршруты только после того, как завершатся существующие сессии.

Сохранённые ID HTTP-сессий из версий Agents до v0.20.0 не содержат согласованную версию протокола. Обновлённый клиент отбрасывает такие ID и переподключается заново, вместо того чтобы отправлять небезопасный запрос на возобновление. Незавершённая работа, привязанная к старой удалённой сессии, не возобновляется.

Подробности об API см. в createMcpHandler и McpClient.