Привет, Хабр. Меня зовут Дима, я создаю 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-сервер — расскажите в комментариях, как решали проблему описаний. У нас ощущение, что можно лучше, но пока не понимаем как.