Skip to main content
Glama
weijianzhg

mCP 2.0

by weijianzhg

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 прогресса, ограниченного запросом, используемые здесь.

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    A 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
  • F
    license
    Not graded
    quality
    C
    maintenance
    Demonstrates MCP 2026-07-28 behavior for long-running tool calls, task lifecycle (get, update, cancel), and elicitation clarification during async tasks.
  • A
    license
    Not graded
    quality
    C
    maintenance
    Educational MCP server demonstrating the 2026-07-28 stateless protocol with raw Starlette, no SDK, featuring tools, request state handles, MRTR elicitation, and subscriptions.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Demo 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.
    MIT

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/weijianzhg/mcp-2-0'

If you have feedback or need assistance with the MCP directory API, please join our Discord server