Skip to main content
Glama

mcp-worker-starter

Минимальный Model Context Protocol сервер для Cloudflare Workers. Ноль зависимостей, один файл, обычный POST.

Я запускаю MCP-сервер в продакшене, который отдаёт Claude живые бизнес-данные через аутентифицированные инструменты. Это тот сервер, из которого убрали бизнес-логику, а шрамы оставили.

Счастливый путь MCP-сервера — это около сорока строк. Публиковать стоит три ловушки ниже, потому что каждая из них молчалива, и одна из них обрушила два моих продукта.


405, который стоит вам простоя

MCP-клиенты открывают GET с Accept: text/event-stream, чтобы слушать сообщения, отправляемые сервером. Если ваш сервер не говорит на SSE, протокол требует ответить 405. Этот статус — сигнал больше не открывать это.

Я вместо этого отвечал 200 с дружелюбным JSON-телом, потому что 200 казался полезнее ошибки.

Клиент воспринял этот 200 как поток, который умер, и переподключился. Сразу. Без backoff и без единой ошибки там, куда я смотрел.

201 936 запросов за один день. Это сожгло дневную квоту запросов всего аккаунта Cloudflare и обрушило второй, совершенно не связанный продукт, которому случилось делить этот аккаунт. Сам MCP-сервер не залогировал ни одной ошибки, потому что с его стороны всё было в порядке. Он отвечал на каждый запрос правильно — 201 936 раз.

200 там, где протокол ожидает 405, — это не более дружелюбный ответ. Это бесконечный цикл с хорошими манерами.

if ((request.headers.get("accept") ?? "").includes("text/event-stream")) {
  return new Response(JSON.stringify({ error: "This server does not expose an SSE stream. Use POST." }), {
    status: 405,
    headers: { "content-type": "application/json; charset=utf-8", allow: "POST" },
  });
}

Related MCP server: Remote MCP Server (Authless)

У уведомлений нет id, и им не положено тела

JSON-RPC-уведомление — это «выстрелил и забыл». Оно приходит без id, и вызывающая сторона не ждёт ответа. Ответьте {"jsonrpc":"2.0","result":{}} — и строгие клиенты сочтут обмен некорректным, потому что вы ответили на то, о чём никто не спрашивал.

202 с пустым телом — это правильное «получено, сказать нечего».

if (id === undefined || id === null) return new Response(null, { status: 202 });

Возвращайте protocolVersion клиента

При initialize возвращайте protocolVersion, который предложил клиент, а не зашивайте свой собственный. Зашитая в код версия даёт рукопожатие, которое работает сегодня и тихо перестаёт работать в ту неделю, когда клиент обновится. Откатывайтесь к значению по умолчанию, только если клиент не назвал ни одной версии.


Использование

npm install
npx wrangler dev
curl -s http://localhost:8787 \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq

Деплой:

npx wrangler deploy

Затем добавьте задеплоенный URL как MCP-сервер в вашем клиенте. Он говорит по POST.

Добавляйте свои инструменты

Отредактируйте массив TOOLS в src/index.ts. Два правила, которые важнее, чем кажется:

  • description — это промпт. Модель выбирает инструменты, читая его. Пишите его для читателя, который не видит ваш код и не станет перечитывать схему дважды.

  • Возвращайте данные, а не прозу. Модель лучше умеет описывать ваш JSON, чем вы — угадывать, что она хочет о нём сказать.

Аутентификация и ограничение частоты запросов

Обе функции отключены по умолчанию, поэтому стартовый шаблон работает без настройки.

Аутентификация по токену включается, когда вы задаёте MCP_TOKEN. Запросы должны тогда предъявлять Authorization: Bearer <token>.

npx wrangler secret put MCP_TOKEN

Почасовое ограничение частоты запросов включается, когда вы привязываете KV-namespace как RATE_LIMIT. Лимит по умолчанию — 300 запросов в час. В продакшене я ограничиваю каждого тенанта по отдельности, а не глобально, ключуя по тому, что идентифицирует вызывающего.

[[kv_namespaces]]
binding = "RATE_LIMIT"
id = "your-kv-namespace-id"

Ограничение частоты здесь — не паранойя. Ловушка номер один — это ровно та форма отказа, которую лимит поймал бы за минуты, а не за день.

Чем это не является

Это не SDK, не фреймворк и не пытается им быть. Если вам нужны батарейки в комплекте, используйте официальный TypeScript SDK или Agents SDK от Cloudflare.

Это для случая, когда вы хотите прочитать весь сервер за один присест и точно знать, что он делает.

Тесты

npm test

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

Лицензия

MIT

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    Allows deploying a Model Context Protocol server on Cloudflare Workers without authentication, enabling AI assistants to access custom tools through the MCP standard.
    -