Привет, Хабр. Меня зовут Дима, я делаю WebAsk - конструктор опросов и тестов. Четыре года назад я писал тут про тотализатор на коленке, спагетти-код из 5к строк и борьбу с мобильным скроллом.
Но сегодня не про это. Сегодня - как мы дали нейронкам прямой доступ к нашему сервису через MCP, какие грабли собрали по дороге и что из этого получилось.
Для тех, кто пропустил хайп: MCP (Model Context Protocol) - открытый стандарт от Anthropic. Он позволяет ллм аля клод, курсор и другим подключаться к внешним сервисам и управлять ими напрямую - без браузера, без ручных оберток, без гемора (ну как сказать). На Хабре про это уже штук пятнадцать статей, пересказывать не буду, сорян))
Зачем нам это сдалось
Мы долго наблюдали один и тот же сценарий. Человек сидит в ллмке - пишет текст, разбирает код, готовит отчет. И тут ему прилетает задача: "надо собрать обратную связь по конференции" или "создай опрос для сотрудников в отделе HR". Что происходит дальше:
Новая вкладка. Логин в сервис. 15 минут тыканья в конструктор. Копирование ссылки. Возврат к ллмке. Контекст потерян, мысль ушла, кофе остыл, коллега уже написал "ну что там с опросом?", а ты еще даже не проснулся
И так каждый раз
Параллельно мы видели, что весь мировой рынок зашевелился. Typeform выкатил свой MCP-сервер в бету. Atlassian, Google, MongoDB - уже подключились. Anthropic и Cursor добавили нативную поддержку MCP-серверов прямо в свои клиенты. Для SaaS это перестало быть экзотикой - MCP превращается в такой же ожидаемый интеграционный слой, как REST API и вебхуки пять лет назад.
У нас к тому моменту был REST API, ИИ-генерация опросов, вебхуки. Добавить MCP-сервер поверх этого было логичным следующим шагом. Не потому что модно, а потому что задача понятная: дать ассистенту прямой доступ ко всему, что умеет наш сервис, чтобы пользователю не приходилось переключаться между окнами.
"Погоди, а зачем конструктор, если ИИ и так умеет в опросы?"
ChatGPT может сгенерить опрос за тридцать секунд. Claude напишет анкету на React с ветвлениями. Зачем тогда отдельный сервис
Потому что сгенерить вопросы - это 5% работы. Остальные 95% - это инфраструктура:
Кто захостит опрос? Куда 10 000 респондентов пойдут отвечать?
Кто соберет ответы? В какую базу? Кто гарантирует, что ничего не потеряется?
Кто посчитает аналитику? NPS-сегментацию из тысячи ответов? Тепловую карту из двух тысяч кликов по макету?
Кто экспортирует результаты в Excel для отчета руководителю?
Кто отправит данные в Bitrix24, в
MAXTelegram, в Google Sheets?
Это как сказать "зачем ЮКасса, если ИИ может написать платежную страницу". Страницу - может. Платежи обработать - нет. Генерация вопросов - это фронтенд. А за ним нужен бэкенд: хостинг, сбор данных, аналитика с сегментацией, интеграции, экспорт.

MCP как раз про это. Он не помогает ИИ придумывать вопросы - с этим ИИ и сам справляется. Он дает ИИ доступ к работающей инфраструктуре: создать опрос, который реально захостится и будет принимать ответы. Собрать данные с живых респондентов, посчитать NPS, построить тепловую карту по клик-тесту, выгрузить в Excel - и все через один диалог.
ИИ - офигительный интерфейс. Но для проведения исследования ему нужна инфраструктура за спиной. Без MCP он может придумать вопросы. С MCP - провести исследование от создания до экспорта результатов.
Как мы это строили
Ладно, к технике;)

Протокол: JSON-RPC 2.0, авторизация через Bearer-токен. Ключ генерируется в личном кабинете и показывается ровно один раз. Если потеряли - генерируете новый. Так что записывайте сразу, второго шанса не будет.
Подключение к Claude Desktop - стандартный конфиг MCP-сервера:
{ "mcpServers": { "your-service": { "command": "npx", "args": ["-y", "@anthropic-ai/mcp-fetch"], "env": { "MCP_URL": "https://your-service.example.com/mcp/v1", "MCP_AUTH_HEADER": "Authorization", "MCP_AUTH_VALUE": "Bearer YOUR_API_KEY" } } } }
Скопировал, вставил, перезапустил Claude Desktop - все, инструменты появились в списке. Для Cursor и VS Code конфиги чуть другие, но принцип тот же
Что внутри. По спеке MCP умеет три штуки: ресурсы (чтение), инструменты (действия) и промпты (подсказки). На деле оказалось, что Claude Desktop поддерживает только инструменты - ресурсы и промпты просто игнорирует. Claude Code и Cursor поддерживают все, но рассчитывать что все сидят именно там - не вариант.
Поэтому мы обернули все ресурсы в инструменты-обертки. Хочешь прочитать ответы? Вызываешь инструмент-обертку. Сводку? Другой инструмент. Структуру опроса? Третий. 19 таких оберток - по одной на каждый ресурс.
В итоге получилось 40+ инструментов действий плюс 19 инструментов чтения - итого под 60 тулзов. Когда мы посчитали итоговое количество - сами немного офигели.
Интересная табличка 👇
Группа | Что внутри |
Жизненный цикл | Создать, переименовать, дублировать, архивировать, удалить, опубликовать |
Контент | Редактировать вопросы, тексты, настройки, логику ветвлений, загружать медиа |
Оформление | Применить тему, создать свою, настроить под бренд |
Аналитика | Сводка, фильтрованные отчеты, теги ответов |
Экспорт | CSV, XLSX, PDF, Word |
Промокоды | Создать группы, добавить коды, управлять списками |

Как это устроено внутри. MCP-сервер написан на Node.js и работает как прослойка между MCP-клиентами (Claude, Cursor) и нашим основным бэкендом. По сути тонкая прослойка: получил JSON-RPC запрос, определил какой инструмент вызвать, провалидировал параметры, дернул API, вернул результат.
Каждый инструмент - отдельный обработчик со своей схемой валидации. Общий роутер разбирает запрос по имени инструмента и перенаправляет в нужный обработчик. Упрощенно это выглядит так:
// описания лежат отдельно от логики const tools = loadToolDescriptions('./tools/descriptions/') // роутер function handleToolCall(name, params) { const handler = handlers[name] // находим обработчик const schema = tools[name].params // берем схему валидации validate(params, schema) // валидируем параметры return handler.execute(params) // вызываем основной API } // обработчик - один файл на один инструмент // tools/handlers/rename_quiz.js export function execute({ quiz_id, name }) { return api.patch(/quizzes/${quiz_id}, { name }) }
Ничего сложного, но когда их под 60 - важно не превратить в кашу:) Один файл = один инструмент, описания отдельно от логики, валидация автоматическая по схеме.
Проблема контекстного окна. Под 60 описаний инструментов - это примерно 6 000 токенов контекста, которые модель получает при каждом обращении. Пробовали отдавать описания группами: сначала только базовые, остальные - по запросу. Не взлетело. Модель не знала о существовании инструментов из непрогруженных групп и даже не пыталась их запросить. Вернулись к полному списку. Дороже по токенам, зато модель видит все и выбирает точнее.
Описания отдельно от кода. Еще одно архитектурное решение, которое себя оправдало: описания инструментов хранятся отдельно от бизнес-логики обработчиков. Итерируем их независимо - можно переписать описание, не трогая код, и наоборот. Звучит очевидно, но когда ты в спешке пишешь все в одном файле - потом это больно разделять.

Окей, это скелет. Дальше интереснее - грабли...(
Его величество описания инструментов
Вот тут мы вообще не ожидали подвоха. На описания инструментов ушло больше времени, чем на саму интеграцию с API. Без шуток
Проблема вот в чем. Когда LLM получает список из 40+ инструментов, она должна по описанию понять, какой из них вызвать и с какими параметрами. Если описание нечеткое или двусмысленное - модель промахивается. Вызывает не тот инструмент. Передает неправильные параметры. Или вообще решает, что нужного инструмента нет, и начинает импровизировать.
Мы сначала пошли по пути экономии. Написали краткие описания - по одному предложению на инструмент. Логика была простая: меньше токенов на контекст - дешевле - быстрее. Красиво на бумаге
На практике: промах в ~30% случаев на сложных цепочках. Модель путала похожие инструменты, не понимала разницу между чтением ответов и чтением сводки. При дублировании опроса забывала передать ID шаблона.
Переписали все описания на подробные - с пояснениями, примерами параметров, типичными сценариями использования. Да, это больше токенов. Да, это немного дороже на каждый запрос. Но точность выросла прям заметно. Экономия на описаниях - ложная экономия.
Конкретный пример: у нас есть два похожих инструмента - один обновляет тексты приветственного и финального экранов, другой - тексты самих вопросов. В первой версии оба были описаны как "обновить тексты опроса". Модель путала их в каждом втором случае. Когда мы явно прописали, что первый работает только с экранами приветствия и благодарности, а для вопросов нужен другой инструмент - путаница прекратилась.
Разница - как между "закрой дверь" и "закрой входную дверь, которая слева, а не дверь в ванную". Для человека и так понятно. Для нейронки - нет

180 запросов в минуту, или Почему агенты - это стихийное бедствие
Отдельная история - лимит запросов. Когда ты делаешь API для людей, 60 запросов в минуту - это с запасом. Человек физически не нажмет кнопку 60 раз за минуту.
С ИИ-агентами все иначе. Агент получает задачу "создай 10 опросов по шаблону с разными параметрами" - и начинает фигачить. Без пауз, без раздумий, без перерыва на кофе. Десять цепочек по 5-7 вызовов = 50-70 запросов за несколько секунд.
60 - безопасно для бэкенда, но агент упрется на любой нетривиальной задаче. 300 - удобно, но один юзер с агрессивным автоматизатором положит сервис остальным. Остановились на 180 - хватает для цепочки из 20+ вызовов, но DDoS от одного агента не устроишь. Ссылки на экспорт живут час
Тестирование, или "Claude, ты что делаешь?"
С юнит-тестами все ок. А потом мы подключили реального Claude и тут началось
Модель - это не детерминированный клиент. Она может вызвать инструменты в рандомном порядке. Додумать параметры. Или просто проигнорить описание и сделать по-своему.
Один из наших любимых багов: мы просили Claude "создай опрос с тремя вопросами и опубликуй". Claude создавал опрос, добавлял три вопроса, потом решал, что неплохо бы еще добавить приветственный экран с мотивирующим текстом (мы не просили), менял тему на "более подходящую" (мы не просили) и только потом публиковал. Инструменты работали корректно. Просто модель решила проявить инициативу :)
Починили жесткими промптами и явными указаниями в описаниях: инструмент делает только то, что написано, и ничего больше. Помогло. Частично
Формальных eval-фреймворков у нас не было. Тестировали руками: ~20 сценариев, прогоняли после каждого изменения описаний. Пишешь промпт, смотришь какие инструменты вызвались, если не те - правишь описание, повторяешь. Описания переписывали по 3-4 раза, некоторые - больше
Больше всего бесили инструменты с похожими названиями и инструменты с кучей необязательных параметров. Тестирование MCP - это, по сути, тестирование промптов. Только промпты - это ваши описания инструментов. Ну вы поняли.
"А где мои ресурсы?", или Сюрприз от Claude Desktop
Это надо знать заранее, серьезно:)
В спецификации MCP три штуки: tools, resources, prompts. Мы сделали ресурсы - для ответов, сводки, структуры опроса, еще полтора десятка. Подключили Claude Desktop. Просим прочитать ответы.
А он не может. Ресурс есть, но Claude Desktop их просто не поддерживает. Только тулзы. Ни ресурсы, ни промпты. Вот так
Claude Code и Cursor - поддерживают все: и ресурсы, и промпты. Но Claude Desktop - самый массовый клиент, и рассчитывать на то, что все пользователи сидят в CLI или Cursor, мы не могли.
Решение: обернули все 19 ресурсов в инструменты-обертки - по одной обертке на каждый ресурс. Тулз вызывает ресурс внутри себя и возвращает данные. Для модели это выглядит как обычный инструмент, для пользователя - разницы ноль.
Мораль: если делаете MCP-сервер и хотите, чтобы он работал не только в CLI - дублируйте ресурсы как тулзы. По спецификации это не нужно. На практике - без этого половина клиентов вас не увидит.
Как этим пользуются на практике
Когда запустились и полезли в логи - оказалось, что мы неправильно понимали вообще все.
Мы думали, что киллер-фича - создание опросов. Ну логично: мы конструктор, значит, люди будут конструировать. Через Claude вместо UI - быстрее, удобнее, контекст не теряется. Пять вызовов - и опрос с вопросами, темой и публичной ссылкой готов. Это и правда работает, и это и правда быстрее
Но самый частый сценарий оказался другим...;)
Главный сюрприз: никто не хотел создавать. Все хотели читать
Аналитику дергают сильно чаще, чем создание. Типичная ситуация: у человека опрос, который уже неделю собирает ответы. Накопилось 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 помнит айдишник опроса, знает текущую структуру и не теряет нить. Для юзера это выглядит как разговор, а не дерганье API.
Экспорт завершает каждую цепочку. Создал - настроил - собрал - выгрузил в CSV, Excel, PDF или Word. Без экспорта результат остается внутри сервиса. С экспортом - попадает в дашборд, в отчет, в письмо руководителю. Мы чуть не сделали экспорт в последнюю очередь. Это было бы примерно как построить дом и забыть про двери.
MCP vs REST API - зачем два интерфейса
У нас есть полноценный REST API, который покрывает всю функциональность. Зачем тогда MCP?
Потому что потребители разные.
API - для разработчиков. Вы пишете код, точно знаете, какой эндпоинт вызвать, какие параметры передать, как обработать ответ. Все детерминировано, все предсказуемо
MCP - для ИИ-агентов. Вы даете модели набор из ~60 инструментов с описаниями, а она сама решает, что и когда вызвать. Пользователь говорит "создай анкету для оценки сотрудников с NPS-шкалой и логикой ветвлений" - и агент сам разбирает это на шаги, сам выбирает инструменты, сам склеивает результат. Разработчик не писал ни строчки кода
Это не замена. Это параллельный слой. REST API, вебхуки, MCP - три разных интерфейса для трех разных задач. API - для интеграций. Вебхуки - для событий. MCP - для ИИ-агентов.

Короче
Самое неожиданное во всей этой истории - что главная работа оказалась не в коде. Архитектура простая, роутер примитивный, обработчики - обертки над API. А вот тексты описаний инструментов мы переписывали раз пять и до сих пор не уверены, что они идеальные.
Еще мы не ожидали, что люди будут больше читать через MCP, чем создавать. И что Клод в 2026 году все еще не поддерживает ресурсы MCP - пришлось городить обертки
И предвкушаю вопрос “есть ли метрики” - сразу отвечу, что рано, тк запустили недавно. Когда будет что показать - напишу, ну или пните меня)
А если вы делали свой MCP-сервер - расскажите в комментах, как решали проблему описаний. У нас ощущение, что можно лучше, но пока не понимаем как
Респект всем, кто дочитал ❤️
