# Проектирование 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. Лучше создать несколько агентов под конкретные задачи:

- агент для курсов валют;
- агент для поддержки клиентов;
- агент для заявок и инцидентов;
- агент для отчетов.

Если у агента слишком много похожих инструментов, модель чаще ошибается при выборе.

## Проверочный список

- Имя инструмента конкретное и не похоже на `call_api`.
- Описание объясняет, когда вызывать инструмент.
- У каждого важного параметра есть понятное назначение.
- Multi-action endpoint разделен на отдельные инструменты.
- Ответ не содержит лишний большой JSON.
- Для POST/PATCH задан idempotency key, если повторный вызов может создать дубль.
- Для DELETE пользователь видит двухшаговое подтверждение.
- Агенту привязаны только инструменты, нужные для его задачи.
