172 lines
8.1 KiB
Markdown
172 lines
8.1 KiB
Markdown
# Проектирование MCP-инструментов
|
||
|
||
Этот документ описывает, как делать REST-инструменты, с которыми LLM-клиент работает предсказуемо.
|
||
|
||
## Главная идея
|
||
|
||
Хороший инструмент решает одну понятную задачу. Модель должна легко понять:
|
||
|
||
- когда вызывать инструмент;
|
||
- какие параметры нужны;
|
||
- что считается успешным результатом;
|
||
- что делать при ошибке.
|
||
|
||
Плохой инструмент обычно слишком общий: `call_api`, `manage_user`, `execute_request`. Такие названия и описания заставляют модель угадывать.
|
||
|
||
## Название и описание
|
||
|
||
Имя инструмента должно быть техническим и стабильным:
|
||
|
||
```text
|
||
get_customer_by_email
|
||
create_support_ticket
|
||
delete_draft_invoice
|
||
```
|
||
|
||
Описание должно начинаться с действия и объяснять сценарий:
|
||
|
||
```text
|
||
Получает карточку клиента по email. Используйте, когда нужно найти клиента перед созданием обращения или проверкой статуса заказа. Возвращает идентификатор клиента, имя, email и текущий статус.
|
||
```
|
||
|
||
Не используйте расплывчатые формулировки:
|
||
|
||
```text
|
||
Работает с клиентами.
|
||
Обрабатывает данные.
|
||
Выполняет запрос к API.
|
||
```
|
||
|
||
## Входные параметры
|
||
|
||
Каждый параметр должен иметь понятный смысл. Если значение выбирается из небольшого набора, лучше задать `enum`.
|
||
|
||
Плохо:
|
||
|
||
```json
|
||
{
|
||
"action": "string",
|
||
"data": "object"
|
||
}
|
||
```
|
||
|
||
Хорошо:
|
||
|
||
```json
|
||
{
|
||
"email": "user@example.com",
|
||
"include_orders": true
|
||
}
|
||
```
|
||
|
||
Если endpoint делает несколько разных действий через параметр `action`, чаще всего лучше разделить его на несколько инструментов.
|
||
|
||
## Ответ инструмента
|
||
|
||
Не отдавайте модели весь ответ внешнего API без необходимости. Лучше выбрать только поля, которые нужны для следующего шага рассуждения.
|
||
|
||
Плохо:
|
||
|
||
```json
|
||
{
|
||
"response": "{ весь JSON от внешнего API }"
|
||
}
|
||
```
|
||
|
||
Хорошо:
|
||
|
||
```json
|
||
{
|
||
"customer_id": "cus_123",
|
||
"status": "active",
|
||
"open_orders_count": 2
|
||
}
|
||
```
|
||
|
||
Для массивов ограничивайте размер ответа и возвращайте только важные поля.
|
||
|
||
## Ошибки
|
||
|
||
Crank возвращает структурированные ошибки. MCP-клиент получает код ошибки, сообщение, признак повторяемости и рекомендацию.
|
||
|
||
Пример:
|
||
|
||
```json
|
||
{
|
||
"error_code": "upstream_rate_limited",
|
||
"message": "Внешний API вернул HTTP 429.",
|
||
"recoverable": true,
|
||
"suggested_action": "Повторите запрос позже.",
|
||
"request_id": "req_123"
|
||
}
|
||
```
|
||
|
||
Это лучше, чем отдавать сырой ответ внешнего API или stack trace.
|
||
|
||
## Повторное выполнение POST/PATCH
|
||
|
||
Для операций, которые меняют состояние, используйте idempotency key. Это защищает от повторного создания сущности, если MCP-клиент повторит вызов после таймаута.
|
||
|
||
Пример входа:
|
||
|
||
```json
|
||
{
|
||
"request_id": "order-2026-06-21-001",
|
||
"customer_id": "cus_123",
|
||
"amount": 1200
|
||
}
|
||
```
|
||
|
||
В конфигурации операции можно указать, что `request_id` используется как ключ идемпотентности и передается во внешний API через заголовок `Idempotency-Key`.
|
||
|
||
## Опасные операции
|
||
|
||
Для `DELETE` Crank использует двухшаговое подтверждение.
|
||
|
||
Первый вызов не отправляет запрос во внешний API. Он возвращает confirmation token:
|
||
|
||
```json
|
||
{
|
||
"error_code": "confirmation_required",
|
||
"message": "Операция требует подтверждения. Повторите вызов с _crank_confirmation_token=\"ct_...\"."
|
||
}
|
||
```
|
||
|
||
Второй вызов должен повторить те же аргументы и добавить токен:
|
||
|
||
```json
|
||
{
|
||
"order_id": "ord_123",
|
||
"_crank_confirmation_token": "ct_..."
|
||
}
|
||
```
|
||
|
||
Токен одноразовый, имеет короткий срок жизни и привязан к агенту, операции, версии и исходным аргументам.
|
||
|
||
## Каталог агента
|
||
|
||
Не привязывайте к одному агенту весь API. Лучше создать несколько агентов под конкретные задачи:
|
||
|
||
- агент для курсов валют;
|
||
- агент для поддержки клиентов;
|
||
- агент для заявок и инцидентов;
|
||
- агент для отчетов.
|
||
|
||
Если у агента слишком много похожих инструментов, модель чаще ошибается при выборе.
|
||
|
||
Crank измеряет опубликованный каталог по тому же компактному JSON, который возвращается в `tools/list`. В расчёт входят имя, заголовок, описание, входная JSON-схема и добавляемое Crank описание подтверждения опасной операции. Для сравнения используется независимая от конкретной модели консервативная оценка: один токен на три байта UTF-8. Это не счётчик токенов конкретного поставщика, а стабильная инженерная метрика для поиска регрессий.
|
||
|
||
Рекомендуемый бюджет одного агентского каталога — не более 4096 оценочных токенов. Превышение не блокирует публикацию, потому что допустимый объём зависит от модели, но создаёт предупреждение. Сначала сокращайте лишние описания и схемы. Если инструменты решают разные задачи, разделяйте их между специализированными агентами. Выбор нужного агента и постепенное раскрытие каталогов выполняет оркестратор Drivetrain, а не Crank.
|
||
|
||
## Проверочный список
|
||
|
||
- Имя инструмента конкретное и не похоже на `call_api`.
|
||
- Описание объясняет, когда вызывать инструмент.
|
||
- У каждого важного параметра есть понятное назначение.
|
||
- Multi-action endpoint разделен на отдельные инструменты.
|
||
- Ответ не содержит лишний большой JSON.
|
||
- Для POST/PATCH задан idempotency key, если повторный вызов может создать дубль.
|
||
- Для DELETE пользователь видит двухшаговое подтверждение.
|
||
- Агенту привязаны только инструменты, нужные для его задачи.
|
||
- Опубликованный каталог укладывается в выбранный бюджет контекста модели.
|