# Интеграция MCP

Accessful предоставляет MCP-сервер, через который ваше ИИ-приложение может обрабатывать PDF, проверять ход обработки и получать результат.
Нужны API-ключ Accessful и клиент с поддержкой **удалённых серверов через Streamable HTTP и пользовательских HTTP-заголовков**.

## Настройка подключения

1. В портале Accessful откройте **API-Keys** и создайте ключ для этой интеграции.
   Скопируйте его сразу: он показывается только один раз. См. [Создание API-ключа](https://docs.accessful.de/ru/authentication/#создание-api-ключа).
2. Добавьте в клиент удалённый MCP-сервер с именем **Accessful** и укажите следующие параметры:

   | Параметр | Значение |
   | --- | --- |
   | Адрес сервера | `https://api.accessful.de/mcp` |
   | Транспорт | Streamable HTTP |
   | Имя заголовка | `X-API-Key` |
   | Значение заголовка | Ваш личный API-ключ Accessful |

3. Поместите ключ в хранилище секретов клиента, сохраните настройки и подключитесь. Заголовок нужен в **каждом запросе**.
   Используйте отдельный отзываемый ключ для каждого пользователя и интеграции. **Вход через OAuth в V1 недоступен**.

Подключайте собственные веб-приложения через их бэкенд. Прямые запросы браузера с другого источника (`Origin`) отклоняются с `403`.

## Проверка подключения

Обновите список инструментов в клиенте. Должны появиться эти **четыре инструмента**:

| Инструмент | Вход | Результат |
| --- | --- | --- |
| `accessful_start_pdf` | `url`, `filename`, `requestId` | Запускает задачу по ссылке для скачивания; возвращает `caseId`, `pollAfterSeconds` |
| `accessful_start_pdf_file` | `file` (заполняет хост) | Запускает задачу по вложению в чате; возвращает `caseId`, `pollAfterSeconds` |
| `accessful_get_status` | `caseId` | Возвращает `jobStatus`, `stage`, `terminal` и доступные данные о ходе обработки |
| `accessful_get_result` | `caseId` | Возвращает `downloadUrl`, `expiresAt`, `filename`, `mimeType` |

<details>
<summary>Дополнительно: проверка через MCP Inspector</summary>

С Node.js 22.19 или новее и ключом в переменной окружения `ACCESSFUL_API_KEY`
выполните эту команду в терминале Bash или Zsh. Она выводит список инструментов, не создавая задачу PDF:

```bash
npx @modelcontextprotocol/inspector --cli https://api.accessful.de/mcp \
  --transport http --header "X-API-Key: ${ACCESSFUL_API_KEY:?Set ACCESSFUL_API_KEY}" \
  --method tools/list
```

В выводе должны присутствовать четыре имени выше. См. [документацию MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector).

</details>

## Обработка PDF

Запустите задачу **одним из двух инструментов запуска**, затем проверяйте статус и скачайте результат.
Максимум **50 МиБ**, один PDF на задачу.

### Вариант A: вложение в чате

Некоторые MCP-хосты передают вложения из чата напрямую инструментам, которые объявляют файловый параметр в `_meta["openai/fileParams"]`.
`accessful_start_pdf_file` объявляет так свой параметр `file`. Если ваш хост это поддерживает, пользователь прикрепляет PDF,
а хост заполняет `file` временной ссылкой для скачивания, поэтому модели не нужно составлять URL:

```json
{
  "file": {
    "download_url": "https://files.example.com/temporary/abc123",
    "file_id": "file_abc123",
    "mime_type": "application/pdf",
    "file_name": "Годовой отчёт.pdf"
  }
}
```

`download_url` хоста должна быть доступна Accessful по публичному HTTPS без входа в систему и дополнительных заголовков
и действовать, пока Accessful не скачает файл. Скачивание начинается вскоре после принятия задачи.
`requestId` не нужен: повторный идентичный вызов возвращает тот же `caseId`, а обновлённая ссылка на тот же файл запускает новую задачу.

Хосты без такой поддержки показывают `file` как обычный объект. В этом случае используйте вариант B; не позволяйте модели придумывать `download_url`.

### Вариант B: ссылка для скачивания

Подготовьте **прямую HTTPS-ссылку для скачивания исходного PDF**, доступную Accessful без входа в систему и дополнительных заголовков.
Подойдёт существующая ссылка на общедоступный PDF. Для закрытых файлов приложение должно создать подписанную ссылку для скачивания.
Локального пути недостаточно: если хост не поддерживает файлы, приложение должно предоставить файл по URL.
Ссылка должна действовать ещё не менее **15 минут**.

Вызовите `accessful_start_pdf` со следующими аргументами. Замените адрес из примера ссылкой на ваш PDF
и создайте новый UUID `requestId` для каждой новой задачи:

```json
{
  "url": "https://files.example.com/document.pdf",
  "filename": "document.pdf",
  "requestId": "96d35d20-7f2a-4de2-9a85-0e7d03cc68a4"
}
```

При повторах используйте **те же `requestId`, URL и имя файла**, в том числе после смены API-ключа.

### Проверка статуса и скачивание результата

1. Сохраните `caseId`, который вернул инструмент запуска. Вызывайте с ним `accessful_get_status` каждые **10 секунд**, пока `terminal` не станет `true`.
   После **120 секунд** активного ожидания покажите текущий статус и `caseId`; обработка продолжается, поэтому проверьте статус позже.
2. Когда `jobStatus` станет `completed`, вызовите `accessful_get_result` с тем же `caseId` и покажите `downloadUrl` как ссылку для скачивания.
   Она работает без API-ключа **пять минут**. После истечения срока повторите вызов, чтобы получить новую ссылку.

Приложение должно обеспечивать повторы и периодический опрос, а если хост не поддерживает файлы, то и доступ к файлу; добавление MCP-сервера не реализует этот процесс.
Не включайте ключи, подписанные ссылки и содержимое PDF в журналы или промпты. Показывайте ссылку на результат только целевому пользователю.

## Ограничения и частые ошибки

| Проблема | Что делать |
| --- | --- |
| `401` или `AUTHENTICATION_REQUIRED` | Проверьте, что `X-API-Key` отправляется в каждом запросе и ключ действителен. |
| `403` при подключении из браузера | Подключайтесь через бэкенд; запросы с других источников отклоняются. |
| `RESULT_NOT_READY` | Проверьте статус. Запрашивайте результат только после `completed`. |
| `IDEMPOTENCY_CONFLICT` | Используйте исходные URL и имя файла с этим `requestId`. Обновление подписанной исходной ссылки тоже меняет входные данные. |
| `UNSUPPORTED_FILE_TYPE` | Прикреплённый файл не является PDF. Прикрепите исходный PDF. |
| `failed` с ошибкой `DOWNLOAD_FAILED` | Accessful не удалось скачать оригинал. Проверьте, что ссылка публично доступна по HTTPS и ещё действует, затем запустите новую задачу; для вложения в чате прикрепите файл заново. |
| Тайм-аут | Задача уже может существовать. Повторите запуск с неизменёнными данными: тем же `requestId` или тем же объектом `file`. |
| Срок ссылки на результат истёк | Снова вызовите `accessful_get_result`. |
| `quota_exceeded` | Обработка завершена из-за исчерпанной квоты. См. [Лимиты и хранение](https://docs.accessful.de/ru/limits/). |

<details>
<summary>Технический справочник</summary>

- `filename`: не более 255 символов, соответствует `^[A-Za-z0-9_-]+\.pdf$`, например `document.pdf`.
- `url`: не более 8192 символов, без встроенных учётных данных и фрагментов. Внутренние сетевые адреса отклоняются;
  скачивание оригинала ограничено **120 секундами**. См. [Загрузка по URL](https://docs.accessful.de/ru/reference/upload-by-url/).
- `file`: объект с обязательными строками `download_url` и `file_id` и необязательными строками `mime_type` и `file_name`;
  другие свойства отклоняются. Для `download_url` действуют те же правила, что и для `url`. `file_name` преобразуется в допустимый `filename`
  (по умолчанию `document.pdf`); `mime_type`, отличный от `application/pdf`, приводит к `UNSUPPORTED_FILE_TYPE`.
- `caseId` и `requestId` — UUID. Неверные аргументы приводят к `INVALID_ARGUMENT`.
- Конечные состояния: `completed`, `failed`, `analyzer_failed`, `canceled`, `quota_exceeded`.
  `queued`, `running` и `quota_pending` остаются активными. Скачать результат можно только при `completed`.
- Статус может содержать `queuePosition`, `queueTotal`, `estimatedWaitSeconds` и `error`; `score` появляется только при `completed`.
  Отсутствие полей очереди не означает конечное состояние.
- Успешные ответы содержат одинаковый JSON в `structuredContent` и текстовом блоке. Результаты также включают PDF `resource_link`.
  Ссылки дают доступ к одной версии результата; обращайтесь с ними как с секретами.
- Ошибки инструментов содержат `isError: true` и `{code, message, retryable}`. HTTP-ошибки подключения могут иметь пустое тело;
  не ожидайте формат Problem Details API REST.

</details>