Зачем конструктору опросов свой MCP-сервер и почему мы не жалеем

Зачем конструктору опросов свой MCP-сервер и почему мы не жалеем

Привет, Хабр. Меня зовут Дима, я создаю WebAsk — конструктор опросов и тестов. Четыре года назад я писал здесь про тотализатор на коленке, спагетти-код из 5 тысяч строк и борьбу с мобильным скроллом.

Но сегодня не об этом. Расскажу, как мы дали нейросетям прямой доступ к нашему сервису через MCP, какие грабли подобрали по пути и к чему это привело.

Для тех, кто в теме: MCP (Model Context Protocol) — открытый стандарт от Anthropic. Он позволяет LLM вроде Claude, Cursor и другим напрямую подключаться к внешним сервисам — без браузера, ручных обёрток и лишнего геморроя. Про это уже много статей, пересказывать не буду.

Зачем нам это понадобилось

Мы не раз видели одну и ту же картину. Пользователь сидит в LLM: пишет текст, разбирает код, готовит отчёт. И вдруг — задача: «Собери обратную связь по конференции» или «Создай опрос для HR-отдела».

Что дальше? Новая вкладка. Вход в сервис. 15 минут тыканья по интерфейсу. Копирование ссылки. Возврат в чат с ИИ. Контекст потерян, мысль ушла, кофе остыл, коллега уже спрашивает: «Ну что с опросом?», а ты даже не проснулся.

И так — каждый раз.

Параллельно мы видели, как рынок движется. Typeform запустил MCP-сервер в бету. Atlassian, Google, MongoDB — уже подключились. Anthropic и Cursor добавили нативную поддержку MCP-серверов. Для SaaS это перестаёт быть экзотикой — MCP становится таким же ожидаемым интеграционным слоем, как REST API и вебхуки пять лет назад.

У нас к тому моменту уже был REST API, ИИ-генерация опросов и вебхуки. Добавить MCP-сервер было логичным шагом. Не потому что модно, а потому что задача ясна: дать ассистенту прямой доступ ко всему функционалу, чтобы пользователю не приходилось переключаться между окнами.

«Погоди, а зачем конструктор, если ИИ и так генерирует опросы?»

Да, ChatGPT сгенерит опрос за 30 секунд. Claude напишет анкету на React с ветвлениями. Но зачем тогда отдельный сервис?

Потому что генерация вопросов — это 5% работы. Остальные 95% — инфраструктура:

  • Кто захостит опрос? Куда пойдут 10 000 респондентов?
  • Кто соберёт ответы? В какую базу? Кто гарантирует, что ничего не потеряется?
  • Кто посчитает аналитику? NPS-сегментацию по тысячам ответов? Тепловую карту по кликам?
  • Кто экспортирует результаты в Excel для отчёта?
  • Кто отправит данные в Bitrix24, Telegram, Google Sheets?

Это как спросить: «Зачем ЮKassa, если ИИ может написать страницу оплаты?» Страницу — может. Обработать платежи — нет. Генерация вопросов — это фронтенд. А за ним нужен бэкенд: хостинг, сбор данных, аналитика, интеграции, экспорт.

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

ИИ — отличный интерфейс. Но для полноценного исследования ему нужна инфраструктура. Без MCP он может придумать вопросы. С MCP — провести исследование от начала до конца.

Как мы это строили

Технические детали:

Протокол: JSON-RPC 2.0. Авторизация — через Bearer-токен. Ключ генерируется в кабинете и показывается один раз. Потерял — генерируй новый. Записывайте сразу.

Подключение к Claude Desktop — стандартный конфиг MCP-сервера. Скопировал, вставил, перезапустил — инструменты появились. Для Cursor и VS Code — чуть другие настройки, но суть та же.

По спецификации MCP поддерживает три типа: ресурсы (чтение), инструменты (действия) и промпты (подсказки). На практике выяснилось, что Claude Desktop поддерживает только инструменты. Ресурсы и промпты — игнорируются.

Поэтому мы обернули все 19 ресурсов в инструменты-обёртки. Хотите прочитать ответы? Вызываете инструмент. Сводку? Другой. Структуру опроса? Третий.

В итоге получилось более 40 инструментов действий и 19 — для чтения. Всего около 60 тулзов. Когда посчитали — сами удивились.

MCP-сервер написан на Node.js. Это тонкая прослойка между MCP-клиентами (Claude, Cursor) и нашим основным бэкендом. Получает JSON-RPC-запрос, определяет нужный инструмент, валидирует параметры, вызывает API, возвращает результат.

Каждый инструмент — отдельный обработчик со своей схемой валидации. Общий роутер направляет запрос по имени инструмента. Архитектура простая: один файл — один инструмент, описания отдельно от логики, валидация по схеме.

Проблема контекстного окна: 60 описаний — это около 6 000 токенов. Пробовали отдавать описания частями — не сработало. Модель просто не знала о существовании скрытых инструментов. Вернулись к полному списку. Дороже, но точнее.

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

Окей, скелет готов. А теперь — грабли.

Описания инструментов: главный подвох

На описания ушло больше времени, чем на интеграцию с API. Без шуток.

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

Сначала писали кратко — по одному предложению. Логика: меньше токенов — дешевле — быстрее. На практике — промахи в 30% случаев. Модель путала похожие инструменты, например, чтение ответов и чтение сводки.

Переписали описания подробно: с примерами, пояснениями, сценариями. Да, больше токенов. Да, дороже. Но точность выросла заметно. Экономия на описаниях — ложная экономия.

Пример: два инструмента — один обновляет тексты приветственного экрана, другой — тексты вопросов. Сначала оба были описаны как «обновить тексты опроса». Модель путала их в каждом втором случае. Когда уточнили: «только экраны приветствия и благодарности» и «только вопросы» — путаница исчезла.

Разница как между «закрой дверь» и «закрой входную дверь слева, а не в ванную». Человеку понятно. Нейросети — нет.

180 запросов в минуту: агенты как стихийное бедствие

Лимиты запросов — отдельная история. Для людей 60 запросов в минуту — с запасом. Человек не нажмёт кнопку 60 раз за минуту.

Агенты — другое дело. Получил задачу: «создай 10 опросов по шаблону» — и начинает бомбить. Без пауз. Десять цепочек по 5–7 вызовов — 50–70 запросов за секунды.

60 — безопасно, но агент упрётся на нетривиальной задаче. 300 — удобно, но один пользователь может уронить сервис. Остановились на 180: хватает для цепочки из 20+ вызовов, но не приведёт к DDoS. Ссылки на экспорт живут час.

Тестирование: «Claude, ты что делаешь?»

Юнит-тесты проходили. А потом подключили реального Claude — и началось.

Модель — не детерминированный клиент. Она может вызывать инструменты в случайном порядке, додумывать параметры или просто игнорировать описание.

Один из любимых багов: просим «создай опрос с тремя вопросами и опубликуй». Claude создаёт опрос, добавляет вопросы, потом решает, что неплохо бы добавить приветственный экран (мы не просили), поменять тему (мы не просили) — и только потом публикует. Инструменты работали. Просто модель проявила инициативу.

Починили жёсткими промптами и уточнениями в описаниях: инструмент делает только то, что написано, и ничего больше. Помогло — частично.

Формальных eval-фреймворков не было. Тестировали вручную: ~20 сценариев, прогоняли после каждого изменения описаний. Пишешь промпт, смотришь, какие инструменты вызвались, если не те — правишь описание, повторяешь. Некоторые описания переписывали по 3–4 раза.

Больше всего бесили инструменты с похожими названиями и те, у которых много необязательных параметров. Тестирование MCP — это по сути тестирование промптов. Только промпты — это ваши описания инструментов.

«А где мои ресурсы?»: сюрприз от Claude Desktop

Важно знать: в спецификации MCP три компонента — tools, resources, prompts. Мы реализовали ресурсы для ответов, сводки, структуры опроса и ещё полтора десятка.

Подключили Claude Desktop. Просим прочитать ответы — а он не может. Ресурсы есть, но клиент их не поддерживает. Только тулзы. Ни ресурсов, ни промптов.

Claude Code и Cursor поддерживают всё. Но Claude Desktop — самый массовый клиент. Рассчитывать на CLI или Cursor — нереалистично.

Решение: обернули все 19 ресурсов в инструменты-обёртки. Тулз вызывает ресурс внутри себя и возвращает данные. Для модели — обычный инструмент. Для пользователя — разницы нет.

Мораль: если делаете MCP-сервер и хотите, чтобы он работал не только в CLI — дублируйте ресурсы как тулзы. По спецификации не нужно. На практике — без этого половина клиентов вас не увидит.

Как этим пользуются на практике

Когда запустились и посмотрели логи — оказалось, что мы всё неправильно поняли.

Думали, что киллер-фича — создание опросов. Логично: мы же конструктор. Через Claude — быстрее, удобнее, контекст не теряется. Пять вызовов — и опрос с вопросами, темой и ссылкой готов. Работает, да.

Но самый частый сценарий оказался другим.

Главный сюрприз: никто не хотел создавать. Все хотели читать

Аналитику запрашивают чаще, чем создание. Типичная ситуация: опрос собирает ответы неделю. Накопилось 500 текстовых ответов. Нужно вытащить смысл — темы, жалобы, инсайты.

Раньше это означало: открыть отчёт, скроллить, к сотому ответу — потеря воли к жизни. На 500 — полдня работы и желание больше не делать открытые вопросы.

Через MCP: Cursor читает все ответы, группирует по темам, выделяет паттерны, считает метрики, отдаёт выжимку. Плюс экспорт в Excel — и отчёт готов. Минута вместо трёх часов.

Оказывается, людям лень читать ответы на свои же опросы. Мы их понимаем.

Но MCP по-настоящему раскрывается в цепочках. Один вызов — удобно, но не сильно отличается от клика. А вот цепочка из 5–9 вызовов — это уже другое.

Пример: автоматизация онбординга. Каждому новому пользователю через три дня нужно отправить персонализированную анкету — с именем, под его тариф. Потом собрать ответы и выгрузить в дашборд. Вручную — хочется уволиться на третьем пользователе.

Через MCP агент сам дублирует шаблон, подставляет имя через скрытую переменную, настраивает логику под тариф, публикует. Через три дня — сам же собирает ответы и экспортирует в CSV. Девять вызовов, ноль ручного труда.

Цепочка: webhook → duplicate → create_hidden_variables → update_texts → update_logic → publish → [3 дня] → answers → export_csv

Скрытые переменные работают отлично. Создаёте переменную с именем пользователя, привязываете к приветствию — и каждый респондент видит «Привет, Маша!», хотя опрос из одного шаблона.

Итерации прямо из IDE. Мы подключили MCP к Cursor — и для UX-исследователей это стало отдельным миром. Прочитал структуру, поправил логику, проверил ответы, выгрузил PDF — и всё не выходя из редактора. Мелочь, но в потоке это экономит не время, а нервы.

Что ещё удивило

Модель помнит контекст между вызовами. Кажется очевидным, но на практике впечатляет. Пользователь говорит: «Создай опрос». Потом: «Добавь вопрос про бюджет». Потом: «Покажи, что получилось». Claude помнит ID, структуру, не теряет нить. Для пользователя — это диалог, а не API-дерганье.

Экспорт завершает каждую цепочку. Создал, настроил, собрал — и выгрузил в CSV, Excel, PDF, Word. Без экспорта результат остаётся в сервисе. С экспортом — попадает в отчёт, в письмо, в дашборд. Мы чуть не сделали экспорт в последнюю очередь. Это было бы как построить дом и забыть про двери.

MCP vs REST API: зачем два интерфейса?

У нас есть полноценный REST API. Зачем MCP?

Потребители разные.

API — для разработчиков. Вы пишете код, знаете, какой эндпоинт вызвать, какие параметры передать. Всё детерминировано.

MCP — для ИИ-агентов. Вы даёте модели ~60 инструментов с описаниями. Она сама решает, что и когда вызвать. Пользователь говорит: «Создай анкету для оценки сотрудников с NPS и ветвлениями» — и агент сам разбирает это на шаги, выбирает инструменты, склеивает результат. Без единой строчки кода.

Это не замена. Это параллельный слой. REST API, вебхуки, MCP — три интерфейса для трёх задач. API — для интеграций. Вебхуки — для событий. MCP — для ИИ-агентов.

Самое неожиданное — что главная работа оказалась не в коде. Архитектура простая, роутер примитивный, обработчики — обёртки над API. А вот описания инструментов мы переписывали по пять раз и до сих пор не уверены, что они идеальны.

Мы не ожидали, что через MCP будут чаще читать, чем создавать. И что Claude в 2025 году всё ещё не поддерживает ресурсы MCP — пришлось городить обёртки.

А если вы делали свой MCP-сервер — расскажите в комментариях, как решали проблему описаний. У нас ощущение, что можно лучше, но пока не понимаем как.

Читать оригинал