Практика GrantTap · · 15 мин чтения

Как проектировать понятные контракты MCP-инструментов

Практикум по целям, структурированным ответам, ошибкам, повторам, отмене и правам доступа на примере собственного сервиса.

ИллюстрацияОбраз статьи создан специально для этого материала. Экраны GrantTap ниже сняты отдельно на тестовых данных.

Полезный инструмент агента позволяет заранее понять действие и после вызова установить результат. Одного названия мало. Договорённость включает ресурс, допустимые входные данные, область воздействия, структуру ответа и смысл ошибки. Если эти детали остаются неявными, разговорный помощник вынужден угадывать. Правдоподобная догадка затем может превратиться в неожиданную операцию. Хорошее описание уменьшает риск ошибки выбора, но не заменяет проверку аргументов, прав и фактического состояния на стороне исполняющего сервиса. Поэтому качество интеграции определяется также поведением при отказе, повторе и потере ответа, а не только демонстрацией успешного вызова.

Этот материал — практикум для команды, которая подключает собственные инструменты к coding-агентам и наблюдает работу через GrantTap. В примерах используется вымышленный сервис предпросмотра сайта. Предложенные поля и операции не описывают выпущенный API GrantTap. Мы проектируем интерфейс, понятный на телефоне, проверяемый без production и восстанавливаемый после разрыва связи. GrantTap помогает увидеть доступные запросы и свидетельства, однако внешний инструмент обязан сам корректно реализовать свой контракт. Описание ниже не утверждает, что перечисленные гарантии автоматически появляются у любого MCP-сервера после подключения или уже реализованы во всех существующих интеграциях.

Начните с одной операции и одного ресурса

Разберите фразу «разверни сайт» на скрытые решения. Какой репозиторий, ревизия, окружение, аккаунт и существующий сервис должны измениться? Если инструмент полагается на текущий проект, другое исполнение может получить иное значение этого контекста. Для практикума определим отдельную операцию создания preview по явно указанной ревизии выбранного проекта. Активация production останется самостоятельным действием с другой областью воздействия. Такое разделение помогает человеку оценить конкретное последствие, а агенту — сначала получить проверяемый промежуточный результат. В долгом разговоре это также уменьшает зависимость от предположений, сделанных много сообщений назад.

Не прячьте произвольную shell-команду за описанием узкой операции. Если параметр принимает любой исполняемый текст, это широкий доступ к выполнению команд, даже когда функция называется безопасным развёртыванием. Широкий инструмент иногда нужен, но его следует представить честно и применять соответствующие ограничения. Узкая обёртка должна действительно ограничивать доступные действия, а не только звучать аккуратно. При ревью полезно пройти от публичных аргументов до последнего вызываемого backend: не появляется ли там возможность поменять цель, обойти установленный environment или подставить дополнительную команду, которую человек в запросе не видел и не выбирал.

Оригинальная сгенерированная иллюстрация практикума. Это не скриншот продукта, результат проверки или измерение.
ИллюстрацияОригинальная сгенерированная иллюстрация практикума. Это не скриншот продукта, результат проверки или измерение.

Опишите проверяемые входные данные

Разделите идентификаторы для машины и подписи для человека. Project ID выбирает ресурс, читаемое имя помогает его узнать. Для сборки исходников явно задайте ревизию и поведение при её отсутствии. Для таймаута уточните, ограничивает ли он ожидание ответа или отменяет настоящую работу. Флаг force не должен скрывать несколько разных эффектов. Если его смысл приходится объяснять длинным списком исключений, лучше использовать отдельные операции или однозначные параметры. Важна возможность прочитать подготовленный запрос и предсказать, что произойдёт с указанным ресурсом, не зная внутренних привычек автора сервиса.

Спецификация MCP от 18 июня 2025 года описывает схемы входных аргументов инструментов. В нашем проектном примере схема фиксирует обязательные идентификаторы и допустимые окружения. Но она сама не устанавливает принадлежность ревизии нужному репозиторию и право пользователя на развёртывание. Эти отношения проверяет сервер по реальному состоянию. Разделите структурную проверку и проверку смысла: синтаксически правильная строка ещё не означает доступный ресурс. Выполняйте обе до начала действия, которое потребляет ресурсы или меняет что-либо за пределами разговора. При ошибке сообщайте, на каком слое запрос остановился и началось ли исполнение.

Сделайте чтение состояния самостоятельным действием

Перед изменением агенту часто нужен список или status. Пусть запрос отвечает на конкретный вопрос. Состояние preview должно относиться к указанному проекту и операции, содержать наблюдаемый этап и доступные сведения о времени наблюдения. Если backend недоступен, так и ответьте. Пустой список вместо ошибки превращает проблему доступности в ложный факт об отсутствии ресурсов. На основании такого ответа агент может создать замену уже существующему preview. Эта ошибка особенно неприятна после потери связи, когда пользователь ожидает проверить прежнее действие, а получает новое действие с новым побочным эффектом.

Для списков объясните пагинацию, фильтры и усечение. Первая страница не является полным инвентарём. Ограниченный фрагмент журнала должен содержать явное указание границы. Успешный транспорт ответа не делает данные полными. На телефоне это особенно важно: клиент часто показывает полученную проекцию, а не все записи исходной системы. Человек должен различать отсутствие подходящих элементов, недоступность источника и частичный результат. Не заставляйте его определять это по длине списка или по косвенным признакам в технических деталях. Именно такие различия позволяют агенту выбрать корректный следующий запрос, не устраивая ненужный повтор изменения.

Спроектируйте результат раньше реализации

Определите, что должен устанавливать успешный ответ создания preview. Например, идентичность preview, ревизию исходников, окружение и operation ID для последующих проверок. Если операция только ставит сборку в работу, ответ должен обозначать принятие или выполнение, а не завершение. URL указывает ресурс, но сам по себе не доказывает, что по адресу работает нужное приложение. Для более сильного утверждения требуется отдельное наблюдение. Обсудите эти значения до написания обработчика, иначе статус complete легко станет означать то принятие задачи, то конец сборки, то успешное обслуживание пользовательского запроса в зависимости от места вызова.

MCP предусматривает structuredContent и необязательную схему результата. В выбранной спецификации структурированный ответ при объявленной выходной схеме должен ей соответствовать. Согласуйте машинные поля и поясняющий текст: фраза «готово» рядом с состоянием queued создаёт два разных сценария продолжения для человека и агента. Добавьте тест на это противоречие. Сам механизм структурированных данных не сертифицирует бизнес-смысл выбранного вами статуса. Значение устанавливает реализация и подтверждающие проверки. Поэтому рядом с примером ответа полезно записать, какое наблюдение backend разрешает выставить именно этот этап и какие условия всё ещё не проверены.

Ошибка должна подсказывать корректное продолжение

Неверная ревизия, истёкшая сессия и недоступный backend требуют разных следующих шагов. Дайте каждому состоянию устойчивое объяснение и укажите, началась ли работа. Ошибка валидации до создания ресурса может означать, что ничего не запускалось. Потерянный ответ после создания такой гарантии не даёт. Не возвращайте бодрый успешный текст, в конце которого спрятана причина отказа. Вызывающей стороне нужен надёжный исход до решения о продолжении. Особенно полезно заранее определить, какие ошибки требуют исправить аргумент, какие — восстановить доступ, а какие — проверить существующую операцию вместо её повторного создания.

Следуйте модели ошибок выбранной версии протокола, различая сбой самого протокольного вызова и ошибку выполняемой операции. Помимо этого договоритесь о восстановлении бизнес-процесса. Истёкшая авторизация ведёт к поддерживаемому входу соответствующего сервиса. Неизвестный проект требует исправленной цели. Таймаут с неизвестным исходом ведёт к status по operation ID. Такие ответы заслуживают собственных тестов. Они определяют реальную удобность инструмента: счастливый путь может выглядеть прекрасно, пока первый потерянный пакет не заставит пользователя выбирать между бесконечным ожиданием и повтором действия, о количестве уже созданных результатов которого никто не может сказать уверенно.

Определите смысл повторного вызова

Разрыв связи оставляет вопрос: не выполнилась операция или не дошёл только ответ? Если второе preview создавать нежелательно, предусмотрите идемпотентность для конкретного намерения и вызывающей стороны. Повтор с тем же ключом должен восстанавливать исходный результат, а не создавать новый ресурс. Повторное использование ключа с другими аргументами нужно отклонять. Срок хранения и внутреннее устройство определяет ваш сервис, но внешнее поведение требуется описать. Важно проверить и одновременные дубликаты, когда два обработчика принимают одинаковый запрос до того, как первый успел записать свой результат.

Обновление статуса не должно снова вызывать create. В практикуме агент сохраняет operation ID и после обрыва спрашивает о нём. Человек на телефоне видит состояние той же операции. Похожее различение доставки и активности объясняет глава Message delivery, но нельзя предполагать одинаковые гарантии у любого внешнего инструмента. Проверьте реальную реализацию, включая потерю ответа сразу после фиксации побочного эффекта. Этот сценарий часто обнаруживает проблему, которую не видно при простом последовательном повторе уже завершившегося вызова: сервер успел сделать работу, а клиент всё ещё уверен, что её результат неизвестен.

Аннотации не заменяют ограничения

Инструмент может описывать себя как читающий или заявлять идемпотентность. Такие подсказки помогают понять намерение автора, но сами ничего не обеспечивают. Описание read only не мешает реализации записать данные. Дружелюбное название не выдаёт доступ к приватному проекту. Рассматривайте аннотации как описание, доверие к которому требует основания. Принудительные проверки должны находиться внутри контролируемой границы. На ревью полезно искать место, где утверждение действительно обеспечивается кодом: валидацией цели, проверкой роли, ограничением операции или иной явной механикой, а не только строкой документации, которую агент прочитал перед вызовом.

Для учебного сервиса проверяйте права на проект и environment при каждом существенном действии. Протестируйте пользователя, который видит preview, но не может активировать production. Проверьте валидный operation ID чужого проекта: полезная ошибка не должна раскрыть через него недоступный ресурс. В GrantTap смотрите поддерживаемое состояние Governance, помня о разнице сохранённой политики и подтверждённого применения. Права внешнего сервиса остаются необходимыми даже при наличии клиентского approval. Это отдельные слои: человек может согласиться с запросом, которому исполняющий сервис всё равно обязан отказать из-за недостаточных полномочий указанного субъекта.

У отмены тоже должен быть контракт

Уточните, что отменяет операция: ожидание клиента, фоновую задачу или уже начавшиеся внешние эффекты. Подтверждение получения cancel ещё не означает остановку worker. Если активация уже произошла, вместо отмены может потребоваться отдельный rollback. Закрытие окна на телефоне не является наблюдением остановки backend. Опишите эти состояния заранее и предусмотрите способ получить окончательный результат. Иначе интерфейс легко застревает в stopping навсегда либо показывает canceled в момент, когда сервис продолжает создавать ресурс, а пользователь начинает новое действие, исходя из неверного представления о прежнем.

Проверьте гонку отмены с завершением. Ответ должен назвать исходную операцию и фактическое финальное состояние. Если отмена опоздала, предоставьте доступ к завершённому preview вместо выдуманного успеха отмены. В удалённой работе похожим образом различаются Stop агента, Cancel сообщения и Dismiss уведомления. Выбирайте глагол по настоящей цели. Одна общая кнопка не может честно обещать обращение всех этапов распределённого процесса. Ревьюеру полезно увидеть в тесте число созданных ресурсов и их итоговое состояние, а не только красивую надпись на клиенте после нажатия элемента управления.

Отдельная ImageGen-иллюстрация сценария. Изображённые предметы не являются свидетельствами выполненного действия или подтверждённого результата.
ИллюстрацияОтдельная ImageGen-иллюстрация сценария. Изображённые предметы не являются свидетельствами выполненного действия или подтверждённого результата.

Делайте свидетельства компактными

Результат должен содержать достаточно данных для оценки своего утверждения. Для сборки это могут быть ревизия, build ID, исход завершения и ссылка на ограниченный журнал. Полная среда с секретами для этого не нужна. Краткое пояснение называет наблюдавшийся результат и оставшиеся проверки. Если вывод обрезан, укажите границу. На телефоне небольшой пакет свидетельств удобнее бесконечного текста, а агенту он помогает не принять отсутствующий хвост за отсутствие ошибки. При этом компактность не должна удалять идентичность операции, без которой даже содержательный фрагмент лога трудно связать с нужным действием.

Различайте сообщение worker об успехе, HTTP-проверку preview и ручное прохождение пользовательского сценария. Это свидетельства разных этапов. Связывайте их идентичностью операции, не сворачивая в универсальный флаг success. Глава Runtime и доказательства объясняет аналогичную границу между запрошенным вызовом, сообщённым результатом и отдельным наблюдением изменения. Если один этап не проверен, оставьте это видимым. Человек сможет решить, достаточно ли уже полученного для следующего действия. Скрытая неопределённость хуже явно ограниченного результата, потому что она заставляет и агента, и ревьюера опираться на более сильное обещание, чем реально обеспечивает сервис.

Испытывайте обычные ошибки и неудобный порядок событий

Составьте сценарии из реальных помех: двойное создание, разрыв после принятия, чужая ревизия, истёкшая сессия между status и activation, частичный журнал, отмена завершённой операции. Для них не нужен сложный атакующий: это обычная жизнь нескольких клиентов и асинхронного backend. Заранее определите наблюдаемый итог и допустимое количество ресурсов. Тогда тест проверяет поведение, а не просто совпадение текста ответа с ожиданием автора. Особое внимание уделите точке фиксации эффекта: до неё повтор может быть новым действием, после неё повтор должен уметь восстановить уже существующий результат.

Используйте тестовый аккаунт или управляемую подмену backend. Если важен побочный эффект, проверяйте настоящее состояние тестовой системы. Утверждение о формате ответа может пропустить двойное создание. Разрушительные действия включайте только в изолированное упражнение, где они действительно нужны. Критерий полезности прост: может ли вызывающая сторона выбрать правильный следующий шаг из полученного результата? Полированное объяснение вероятного успеха не заменяет эту проверку. Сохраните сценарии рядом с реализацией и повторяйте относящиеся к контракту случаи при изменении схемы, стратегии повторов и внутреннего механизма исполнения операций сервиса.

Показывайте на телефоне конкретное решение

В мобильном запросе приоритетны действие, цель, environment, ревизия и непосредственный эффект. Длинный журнал можно вынести в детали. Не прячьте production activation внутри preview и не заменяйте цель расплывчатым текущим проектом. После перерыва подготовленный запрос должен сохранять те же идентификаторы. Изменение выбранного устройства или рабочей директории не должно незаметно перенаправлять уже созданную операцию. Это проверяется сценарием: открыть запрос, сменить контекст в другом месте, вернуться и убедиться, что смысл ожидающего решения остался прежним либо его изменение явно потребовало нового подтверждения.

Сверяйтесь с маршрутом вопросов и approvals для используемой интеграции. Провайдеры передают неодинаковые сведения, поэтому отсутствующее поле нельзя угадывать по ближайшему сообщению. Дополнительное подтверждение внешнего сервиса следует объяснять как отдельный слой. Хорошая поверхность решения позволяет разрешить конкретное действие и затем увидеть фактический исход. Отправка решения при этом не выдаётся за подтверждение его применения. Такая последовательность уменьшает повторные нажатия: пользователь понимает, что именно ожидает компьютер, что уже получил сервис и какую проверку стоит выполнить, если ответ на один из этапов потерялся.

Разведите вход и выбор ресурса

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

У пользователя GrantTap сессия провайдера, связанный компьютер и аккаунт приложения — разные уровни; это разобрано в главе входа провайдера. Такое же разделение полезно для собственного инструмента. Живой транспорт не доказывает право публикации. Валидный deployment credential не доказывает правильность выбранной ревизии. Проверяйте эти слои раздельно, чтобы ошибка указывала недостающее условие. Тогда пользователь не проходит лишний вход из-за неверного project ID и не повторяет создание ресурса, пытаясь вылечить проблему, которая на самом деле относится к доступу или к исходному содержимому запроса.

Проверьте отзыв доступа между этапами

Добавьте сценарий, где пользователь успешно прочитал состояние preview, но до активации потерял право на проект. Прежний status не является бессрочным разрешением следующего действия. Сервер должен заново оценить полномочия там, где начинается новый эффект. Ответ объясняет отказ без создания ресурса или раскрытия недоступных подробностей. Для клиента важно не отправлять человека по кругу между одинаковыми запросами: если условие не изменилось, повтор activation не исправит отсутствующее право. Вместо этого нужно показать границу доступа и сохранить идентичность уже созданного preview там, где просмотр ещё разрешён.

Проверьте также восстановление права в тестовом окружении. После штатного изменения сессии или роли клиент должен получить актуальное состояние и осознанно продолжить прежнее намерение, не создавая скрытую автоматическую публикацию только из-за возвращения доступа. Такая проверка связывает авторизацию, повтор и пользовательское решение в один понятный путь. Она особенно полезна при нескольких открытых клиентах, когда один экран долго хранит прежние сведения, а другой уже получил новый статус. Результат теста должен описывать настоящий эффект backend, а не только обновившийся текст кнопки в приложении.

Поддерживайте совместимость контракта

Версионируйте поведение, если его изменение влияет на следующий шаг вызывающего. Переименование поля, новый смысл complete, удалённый status и другой срок хранения ключа повторов ломают клиентов даже при прежнем имени функции. Держите примеры успеха и ошибок рядом с кодом. При обновлении проверьте старый поддерживаемый клиент с новым сервером и новый клиент с минимально поддерживаемым сервером. Совместимость относится к конкретным версиям и наблюдаемым сценариям. Одной строки в changelog недостаточно, если клиент после таймаута теперь выбирает действие, которое раньше было безопасным, а в новой реализации создаёт второй ресурс.

Документация должна отличать проектируемую гарантию от выпущенной. Если отмена или идемпотентность ещё в плане, пометьте это. Агент может принять пример за действующее правило и выстроить на нём восстановление. Поэтому полезно иметь небольшой набор реально проверяемых примеров, связанный с версией сервера. Прочитайте их как вызывающий после потери ответа: можно ли найти operation ID, узнать начало работы и выбрать status? Если для ответа нужно личное знание внутреннего backend, контракт всё ещё неполон. Это дешёвая проверка, которая часто выявляет проблему раньше первого настоящего сбоя у пользователя.

Проведите небольшое ревью готового интерфейса

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

Закончите практикум одним полным сценарием в тестовом окружении: создание, потеря ответа, проверка прежней операции, получение результата и осознанное завершение. Затем повторите его с отказом доступа. Полученные наблюдения покажут, насколько инструмент пригоден к обычной удалённой работе. Хорошая MCP-интеграция — больше, чем обнаруживаемый каталог функций. Это интерфейс, поведение которого остаётся понятным при прерывании первого вызова, ошибке клиента и обновлении сервиса. Именно такая определённость позволяет агенту помогать быстрее, а человеку сохранять ясное представление о том, какое действие было запрошено и какой результат действительно установлен.

Ранее снятый настоящий интерфейс GrantTap, сборка 230, iOS Simulator, 2026-10-27. Учебная карточка GitHub MCP в режиме Демо. Calls, duration, CPU и memory заданы тестовыми данными; это не измерение реального клиента. Это учебные данные; снимок не является результатом описанного в статье упражнения.
Экран GrantTap · тестовые данныеРанее снятый настоящий интерфейс GrantTap, сборка 230, iOS Simulator, 2026-10-27. Учебная карточка GitHub MCP в режиме Демо. Calls, duration, CPU и memory заданы тестовыми данными; это не измерение реального клиента. Это учебные данные; снимок не является результатом описанного в статье упражнения.

Источники

ДалееКак исследовать границы кода через Graph и Health →