Files
crank/docs/mcp-interface.md

278 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MCP-интерфейс
Crank публикует REST-операции как MCP-инструменты через Streamable HTTP.
## Endpoint агента
Каждый агент имеет собственный MCP endpoint:
```text
/mcp/v1/{workspace_slug}/{agent_slug}
```
Для demo seed:
```text
/mcp/v1/default/currency-rates
```
Полный URL зависит от вашего домена:
```text
https://crank.example.com/mcp/v1/default/currency-rates
```
## Авторизация
MCP-клиент должен передавать API-ключ агента:
```http
Authorization: Bearer <agent_api_key>
```
Ключ выдается в разделе **API ключи**. Полное значение показывается только один раз при создании.
Для операций с подтверждением человеком нужен отдельный ключ подтверждения. Его тоже выдают в разделе **API ключи**, но в режиме **Подтверждения**. Такой ключ нельзя передавать LLM или MCP-клиенту. Он нужен только вашему внешнему интерфейсу, где пользователь нажимает «Подтвердить» или «Отклонить».
## Поддерживаемые методы
MCP methods:
- `initialize`;
- `notifications/initialized`;
- `ping`;
- `tools/list`;
- `tools/call`.
HTTP transport:
- `POST /mcp/v1/{workspace_slug}/{agent_slug}` - JSON-RPC запросы;
- `GET /mcp/v1/{workspace_slug}/{agent_slug}` - server-to-client stream;
- `DELETE /mcp/v1/{workspace_slug}/{agent_slug}` - закрытие сессии.
## Обязательные заголовки
Для `POST`:
```http
Authorization: Bearer <agent_api_key>
Content-Type: application/json
Accept: application/json, text/event-stream
```
Для `GET`:
```http
Authorization: Bearer <agent_api_key>
Accept: text/event-stream
MCP-Session-Id: <session_id>
```
После `initialize` сервер возвращает заголовок:
```http
MCP-Session-Id: <session_id>
```
Если клиент передает `MCP-Protocol-Version`, он должен совпадать с версией, согласованной при инициализации.
## Пример `initialize`
```bash
curl -i https://crank.example.com/mcp/v1/default/currency-rates \
-H 'Authorization: Bearer <agent_api_key>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {
"name": "curl",
"version": "1.0.0"
}
}
}'
```
Сохраните `MCP-Session-Id` из ответа.
## Пример `tools/list`
```bash
curl https://crank.example.com/mcp/v1/default/currency-rates \
-H 'Authorization: Bearer <agent_api_key>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'MCP-Session-Id: <session_id>' \
--data '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}'
```
Ожидаемый результат для demo seed содержит инструмент:
```text
frankfurter_latest_rate
```
Если опубликованная версия агента использует подбор по запросу, `tools/list` содержит только:
```text
search_tools
call_tool
```
`search_tools` принимает текст задачи, необязательные идентификаторы разделов и предел результатов. Ответ содержит полные входные схемы найденных инструментов и `catalog_revision`.
`call_tool` принимает имя найденного инструмента, его аргументы и полученную `catalog_revision`. Если за время между поиском и вызовом опубликована новая версия агента, вызов отклоняется с кодом `catalog_revision_changed`: клиент должен повторить поиск.
## Пример `tools/call`
```bash
curl https://crank.example.com/mcp/v1/default/currency-rates \
-H 'Authorization: Bearer <agent_api_key>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'MCP-Session-Id: <session_id>' \
--data '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "frankfurter_latest_rate",
"arguments": {
"base": "USD",
"quote": "EUR"
}
}
}'
```
Crank выполнит REST-запрос:
```text
GET https://api.frankfurter.dev/v1/latest?base=USD&symbols=EUR
```
## Операции с подтверждением человеком
Если в мастере операции включено **Подтверждение человеком**, первый `tools/call` не выполняет REST-запрос сразу. Вместо этого Crank создает ожидающий запрос на подтверждение и возвращает MCP-клиенту структурированный результат:
```json
{
"status": "approval_required",
"approval_id": "approval_...",
"approval_url": "/v1/default/sales/approvals/approval_...",
"approve": {
"method": "POST",
"url": "/v1/default/sales/approvals/approval_.../approve",
"body": { "approve": "yes" }
},
"deny": {
"method": "POST",
"url": "/v1/default/sales/approvals/approval_.../deny",
"body": { "approve": "no" }
}
}
```
`approval_url` в ответе является путем на MCP-сервере. Если вы публикуете MCP через префикс `/mcp`, внешний URL будет начинаться с `/mcp/v1/...`.
Внешний интерфейс подтверждения работает отдельным ключом подтверждения:
```bash
curl https://crank.example.com/mcp/v1/default/sales/approvals \
-H 'Authorization: Bearer <approval_api_key>'
```
Статус конкретного запроса:
```bash
curl https://crank.example.com/mcp/v1/default/sales/approvals/<approval_id> \
-H 'Authorization: Bearer <approval_api_key>'
```
Подтверждение:
```bash
curl https://crank.example.com/mcp/v1/default/sales/approvals/<approval_id>/approve \
-H 'Authorization: Bearer <approval_api_key>' \
-H 'Content-Type: application/json' \
--data '{ "approve": "yes", "note": "Пользователь подтвердил действие" }'
```
После подтверждения Crank выполняет исходный REST-запрос с тем payload, который был сохранен при первом `tools/call`. Если запрос прошел успешно, заявка получает статус `completed`, а результат сохраняется в `response_payload`. Если upstream вернул ошибку, заявка получает статус `failed`, а в `response_payload` сохраняется код и текст ошибки.
Перед обращением к upstream заявка атомарно переходит в статус `executing`. Если
`mcp-server` завершился во время выполнения, другой рабочий цикл повторно захватит
заявку после истечения аренды. Для изменяющих операций рекомендуется настроить
`execution_config.idempotency` и передавать поддерживаемый upstream заголовок
идемпотентности: универсальный HTTP-клиент не может гарантировать ровно одно внешнее
побочное действие при падении процесса между ответом upstream и записью результата.
Повторный `tools/call` с теми же агентом, операцией, версией и JSON-аргументами
возвращает уже существующую активную заявку вместо создания дубликата.
Отклонение:
```bash
curl https://crank.example.com/mcp/v1/default/sales/approvals/<approval_id>/deny \
-H 'Authorization: Bearer <approval_api_key>' \
-H 'Content-Type: application/json' \
--data '{ "approve": "no", "note": "Пользователь отклонил действие" }'
```
Ключ MCP-клиента не подходит для этих endpoints. Ключ подтверждения, наоборот, не подходит для `initialize`, `tools/list` и `tools/call`.
Текущая реализация возвращает `approval_required` сразу и не держит исходный `tools/call` открытым до решения пользователя. Поэтому внешний интерфейс может восстановить результат через `GET /approvals/<approval_id>`. Долгое ожидание через Streamable HTTP/SSE запланировано отдельно.
## Как формируется каталог инструментов
MCP-клиент видит только опубликованные операции, которые привязаны к опубликованному агенту.
Способ выдачи, разделы и предел результатов входят в версионируемый контракт агента. Прямой режим не меняет обычный MCP-поток. Режим подбора скрывает исходные инструменты за совместимыми метаинструментами `search_tools` и `call_tool`; динамическое изменение `tools/list` и специальные расширения клиента не требуются.
Базовый поиск использует BM25 по имени, заголовку, описанию инструмента и описаниям его разделов. Фильтр по разделу применяется до ранжирования. По умолчанию возвращается не более восьми результатов, допустимый предел — от одного до двадцати.
Черновики операций не попадают в MCP-каталог. Если два пользователя работают в одном workspace, один может редактировать черновик, а второй публиковать агента. В опубликованный каталог попадут только опубликованные версии операций.
При каждом обновлении каталога Crank анализирует фактические определения `tools/list` и записывает в структурированный журнал:
- число инструментов;
- размер компактного JSON в байтах;
- оценочный объём контекста в токенах;
- размер крупнейшего инструмента;
- рекомендуемый предел и признак его превышения;
- число предупреждений качества каталога.
Оценка токенов равна округлённому вверх отношению размера UTF-8 к трём. Она нужна для стабильного сравнения ревизий каталога и не заменяет точный токенизатор конкретной модели.
## Обновление каталога
`mcp-server` периодически обновляет опубликованный каталог. Интервал задается:
```env
CRANK_MCP_REFRESH_MS=5000
```
После публикации операции или агента подождите один интервал обновления или перезапустите `mcp-server`.
## Ошибки
Частые причины ошибок:
- `401 Unauthorized` - отсутствует или неверен API-ключ агента.
- `403 Forbidden` - ключ не имеет нужного доступа.
- `404 Not Found` - workspace, агент или инструмент не опубликованы.
- `400 Bad Request` - неверный MCP-заголовок, session id или JSON-RPC payload.
- `429 Too Many Requests` - сработал rate limit.
Runtime-ошибки инструмента возвращаются как структурированный MCP tool error с кодом, сообщением и `request_id`.