mCP 2.0
MCP без состояния, приложение с состоянием
Минимальный JavaScript-проект, демонстрирующий основные паттерны запросов/ответов MCP 2026-07-28 с MCP TypeScript SDK v2.
Демо охватывает:
HTTP-запросы без сохранения состояния без
Mcp-Session-Id.Состояние, управляемое приложением, адресуемое явными дескрипторами.
Многоэтапные запросы для подтверждения пользователем.
Обновления прогресса, ограниченные отдельной операцией.
Кэшируемое обнаружение инструментов с TTL и областью совместного использования.
Запуск
Требуется Node.js 20 или новее.
npm install
npm run demoКоманда запускает сервер на доступном локальном порту, выполняет все сценарии, выводит результаты и завершает работу сервера. Пример удаления использует виртуальный набор файлов в памяти и никогда не затрагивает файлы на диске.
Запустите интеграционные тесты с помощью:
npm testЧтобы оставить сервер запущенным для другого MCP-клиента:
npm run serverКонечная точка — http://127.0.0.1:3000/mcp. Установите PORT, чтобы переопределить порт.
Related MCP server: MCP RC Long-Running Task Prototype
1. Запросы без сохранения состояния
MCP 2026-07-28 удаляет HTTP-сессии на уровне протокола. Этот проект создаёт новый McpServer для каждого запроса, поэтому любой запрос может достичь любого экземпляра сервера:
const mcpHandler = createMcpHandler(
() => createStateServer(demoFiles),
{
legacy: "reject",
onerror: (error) => console.error("MCP error:", error),
},
);Клиент явно выбирает современную ревизию протокола:
const client = new Client(
{ name: "state-demo-client", version: "0.1.0" },
{
capabilities: { elicitation: { form: {} } },
versionNegotiation: {
mode: { pin: "2026-07-28" },
},
},
);Состояние, хранящееся внутри одного экземпляра McpServer, исчезает после этого запроса. Двойной вызов эфемерного счётчика демо даёт 1 от двух разных экземпляров сервера.
2. Данные приложения с сохранением состояния
MCP без состояния не требует приложения без состояния. Долговременное состояние находится вне MCP-сервера, создаваемого на каждый запрос, и выбирается с помощью явного дескриптора:
const countersById = new Map();
server.registerTool(
"create-counter",
{
outputSchema: z.object({ counterId: z.uuid(), value: z.number() }),
},
async () => {
const counterId = randomUUID();
countersById.set(counterId, 0);
const structuredContent = { counterId, value: 0 };
return {
content: [{ type: "text", text: JSON.stringify(structuredContent) }],
structuredContent,
};
},
);Клиент передаёт дескриптор между вызовами, которые в остальном независимы:
const created = await client.callTool({ name: "create-counter" });
const { counterId } = created.structuredContent;
await client.callTool({
name: "increment-counter",
arguments: { counterId },
});Map в памяти — лишь временная замена. Продакшн-сервер должен использовать базу данных или общее хранилище, привязывать дескрипторы к аутентифицированному субъекту и обеспечивать соблюдение авторизации и срока действия при каждом поиске.
3. Многоэтапное подтверждение
Инструмент, которому нужны дополнительные данные, возвращает input_required. Клиент получает ответ и повторяет исходный запрос с inputResponses, поэтому сервер не инициирует второй JSON-RPC-запрос в потоке операций.
server.registerTool(
"delete-files",
{
inputSchema: z.object({ files: z.array(z.string()).min(1) }),
annotations: { destructiveHint: true },
},
async ({ files }, ctx) => {
const confirmation = acceptedContent(
ctx.mcpReq.inputResponses,
"confirm",
confirmationSchema,
);
if (confirmation === undefined) {
return inputRequired({
inputRequests: {
confirm: inputRequired.elicit({
message: `Delete ${files.length} virtual files?`,
requestedSchema: confirmationSchema,
}),
},
});
}
if (!confirmation.confirm) {
return {
content: [{ type: "text", text: "Cancelled" }],
structuredContent: { status: "cancelled", deleted: [] },
};
}
const deleted = files.filter((file) => demoFiles.delete(file));
return {
content: [{ type: "text", text: `Deleted: ${deleted.join(", ")}` }],
structuredContent: { status: "deleted", deleted },
};
},
);SDK может выполнить запрос и автоматически повторить его с помощью обычного обработчика запроса дополнительных данных:
client.setRequestHandler("elicitation/create", async (request) => {
const confirm = await askUser(request.params.message);
return {
action: "accept",
content: { confirm },
};
});Подтверждение — это пользовательский опыт, а не авторизация. Сервер по-прежнему должен аутентифицировать вызывающего и независимо обеспечивать разрешение на удаление файлов.
4. Прогресс, ограниченный запросом
Прогресс остаётся привязанным к запросу, который начал работу. Параллельные операции получают только свои обновления, а не общий глобальный поток событий.
server.registerTool(
"run-work",
{ inputSchema: z.object({ job: z.string() }) },
async ({ job }, ctx) => {
const progressToken = ctx.mcpReq._meta?.progressToken;
for (const progress of [10, 30, 70]) {
if (progressToken !== undefined) {
await ctx.mcpReq.notify({
method: "notifications/progress",
params: {
progressToken,
progress,
total: 100,
message: `${job}: ${progress}%`,
},
});
}
}
return {
content: [{ type: "text", text: `${job}: complete` }],
structuredContent: { job, status: "complete" },
};
},
);Каждый вызов клиента предоставляет собственный обратный вызов прогресса:
await Promise.all([
client.callTool(
{ name: "run-work", arguments: { job: "alpha" } },
{ onprogress: (update) => alphaProgress.push(update) },
),
client.callTool(
{ name: "run-work", arguments: { job: "beta" } },
{ onprogress: (update) => betaProgress.push(update) },
),
]);5. Кэшируемость
Кэшируемые ответы включают срок свежести и политику совместного использования. Это снижает повторный трафик обнаружения, когда один агент подключается ко многим MCP-серверам.
Этот сервер помечает свой каталог инструментов как пригодный для повторного использования в течение пяти минут:
const server = new McpServer(
{ name: "mcp-state-demo", version: "0.1.0" },
{
cacheHints: {
"tools/list": {
ttlMs: 300_000,
cacheScope: "public",
},
},
},
);Клиент автоматически использует свежие записи:
await client.listTools(); // network request; stores the result
await client.listTools(); // cache hit; no network request
await client.listTools(undefined, {
cacheMode: "refresh",
}); // forces a network request and updates the cacheПоля применяются к результатам tools/list, prompts/list, resources/list, resources/templates/list и resources/read.
publicпозволяет клиентам и общим посредникам повторно использовать результат для разных пользователей.privateограничивает повторное использование контекстом авторизации запроса. Если хранилище кэша является общим, установитеcachePartitionклиента в стабильный идентификатор субъекта.
TTL — это оценка свежести. Уведомления об изменении списков могут аннулировать кэшированные каталоги до истечения их TTL.
Структура проекта
src/server.js MCP server and tool implementations
src/demo.js Client exercising all five scenarios
test/state.test.js Integration tests for every scenarioПакеты MCP закреплены на версии 2.0.0, включая inputRequired, acceptedContent, кэширование и API прогресса, ограниченного запросом, используемые здесь.
This server cannot be deployed
Related MCP Connectors
Cross-tool persistent memory and context for AI assistants over MCP.
AI Reasoning Cache & Consensus Layer with 11 MCP tools via Streamable HTTP.
Agentic rails for complex workflows with receipts, fees, and MCP tool access.
- WauldoOAuthcom.wauldo
Stateless agentic tools over MCP: concept extraction, long-context, knowledge graph, planning.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA reference implementation demonstrating proper MCP server patterns with HTTP transport, featuring session management, progress notifications, and example tools for testing server functionality. Serves as a clean template for building MCP servers with streamable responses and comprehensive error handling.7-
- FlicenseNot gradedqualityDmaintenanceDemonstrates MCP 2026-07-28 behavior for long-running tool calls, task lifecycle (get, update, cancel), and elicitation clarification during async tasks.-
- AlicenseNot gradedqualityCmaintenanceEducational MCP server demonstrating the 2026-07-28 stateless protocol with raw Starlette, no SDK, featuring tools, request state handles, MRTR elicitation, and subscriptions.MIT
- AlicenseNot gradedqualityBmaintenanceDemo MCP server for ACEL, a runtime verification middleware that blocks a rule-violating tool call before it executes. 5 tools (authenticate, read/validate/delete records, send payment) showing ACEL enforcing call ordering and state preconditions live via the official MCP SDK's middleware hook.1MIT