Управление сессией документа агента через MCP
Это одна полная сессия агента с сервером NextPDF Connect Model
Context Protocol (MCP), сообщение за сообщением: initialize,
tools/list, шесть вызовов tools/call, которые формируют
одностраничный бриф проекта, и цикл human-in-the-loop (HITL), который
контролирует финальную запись файла. Каждое сообщение JSON-RPC ниже
дословно записано из работающего процесса bin/nextpdf-mcp (только
инструменты уровня Core), затем очищено ровно двумя способами:
одноразовый токен подтверждения показан как confirm_<single-use-hex>,
а системный временный каталог машины сокращён до C:\Temp.
Идентификаторы, схемы, позиции и количество байтов — ровно те, что
отправил сервер.
Установка
Заголовок раздела «Установка»composer require nextpdf/serverПропишите транспорт stdio в вашем MCP-хосте — например, для Claude Desktop (хосты запускают команду из собственного каталога, поэтому используйте абсолютный путь; транспорту stdio, в отличие от транспорта REST, не нужен API-ключ):
{ "mcpServers": { "nextpdf": { "command": "php", "args": ["/absolute/path/to/your/project/vendor/bin/nextpdf-mcp"] } }}Сервер общается по протоколу JSON-RPC 2.0 с разделением сообщений переводом строки через stdin/stdout и строго отделяет вывод протокола от диагностики: строки запуска и аудита идут в stderr, никогда в stdout.
Концептуальный обзор
Заголовок раздела «Концептуальный обзор»Сессия документа MCP сохраняет состояние. create_pdf открывает
документ во внутреннем хранилище сервера в памяти и возвращает
document_id; каждый последующий вызов обращается к этому
идентификатору. Инструменты содержимого (set_font, add_text,
add_table) выполняются немедленно на уровне риска Caution с
журналированием аудита; preview_layout — это безопасное чтение (Safe);
а output_pdf с file_path относится к уровню Approval Required — он
не выполняется при первом вызове. Вместо этого сервер возвращает запрос
на подтверждение с одноразовым токеном, агент передаёт запрос человеку,
и только повторный вызов с _confirmation_token выполняет запись.
Документы, оставленные в хранилище, удаляются по истечении настроенного
времени жизни (по умолчанию 30 минут).
Те же вызовы инструментов управляют движком инструментов через REST и gRPC — транспорты используют один исполнитель, — поэтому всё здесь, кроме кадрирования stdio, переносится. См. Рендеринг счёта от начала до конца через REST — тот же движок на поверхности HTTP.
Поверхность API
Заголовок раздела «Поверхность API»| Инструмент | Роль в этой сессии | Уровень риска |
|---|---|---|
create_pdf | Открытие документа, получение document_id | Caution (осторожность) |
set_font | Выбор шрифта заголовка, затем основного текста | Caution (осторожность) |
add_text | Строка заголовка, затем вводный абзац | Caution (осторожность) |
add_table | Таблица-чеклист с ответственными и сроками | Caution (осторожность) |
preview_layout | Чтение состояния макета перед выводом | Safe (безопасный) |
output_pdf (режим файла) | Запись PDF — через шлюз | Approval Required (требуется одобрение) |
В зафиксированном здесь развёртывании зарегистрировано 20 инструментов
(13 Core, 6 Pro, 1 Enterprise — их количество приведено в ответе
initialize ниже); эта сессия использует только инструменты Core,
поэтому она работает без изменений в установке только с открытым
исходным кодом. Официальным каталогом является ответ tools/list
вашего собственного сервера, а лестница рисков определена в
справочнике по уровням риска HITL.
Сессия, сообщение за сообщением
Заголовок раздела «Сессия, сообщение за сообщением»1. Инициализация соединения
Заголовок раздела «1. Инициализация соединения»Клиент открывает сессию и указывает свою версию протокола:
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": { "name": "planning-agent", "version": "1.0.0" } }}Сервер подтверждает версию протокола и объявляет свои возможности, включая количество инструментов по уровням и то, что контроль HITL включён:
{ "jsonrpc": "2.0", "id": 1, "result": { "protocolVersion": "2025-06-18", "capabilities": { "tools": { "listChanged": false }, "nextpdf": { "tiers": { "core": 13, "pro": 6, "enterprise": 1 }, "tool_count": 20, "risk_model_version": 1, "hitl_enabled": true } }, "serverInfo": { "name": "NextPDF Connect", "version": "1.0.0" } }}Клиент подтверждает уведомлением (уведомления не несут id и не
получают ответа):
{ "jsonrpc": "2.0", "method": "notifications/initialized"}2. Обнаружение инструментов
Заголовок раздела «2. Обнаружение инструментов»{ "jsonrpc": "2.0", "id": 2, "method": "tools/list"}Полный ответ перечисляет все 20 зарегистрированных инструментов с их полными входными схемами. Здесь он показан сокращённым до двух инструментов, которые открывают и закрывают эту сессию — 18 опущенных записей имеют такую же форму:
{ "jsonrpc": "2.0", "id": 2, "result": { "tools": [ { "name": "create_pdf", "description": "Create a new PDF document and return a document_id for subsequent operations", "inputSchema": { "type": "object", "properties": { "page_size": { "type": "string", "description": "Page size name (e.g. \"A4\", \"Letter\", \"Legal\", \"A3\")", "default": "A4" }, "orientation": { "type": "string", "enum": [ "portrait", "landscape" ], "description": "Page orientation", "default": "portrait" }, "title": { "type": "string", "description": "Document title metadata" }, "author": { "type": "string", "description": "Document author metadata" } }, "required": [] }, "annotations": { "destructiveHint": false, "idempotentHint": false } }, { "name": "output_pdf", "description": "Finalize the PDF and output to file or return as base64", "inputSchema": { "type": "object", "properties": { "document_id": { "type": "string", "description": "The document_id returned by create_pdf" }, "file_path": { "type": "string", "description": "Absolute file path to save the PDF. If omitted, returns base64-encoded PDF data." }, "destroy": { "type": "boolean", "description": "Whether to remove the document from the store after output", "default": true } }, "required": [ "document_id" ] }, "annotations": { "destructiveHint": false, "openWorldHint": true } } ] }}Обратите внимание на схему output_pdf: file_path необязателен, а
аннотации содержат openWorldHint: true — инструмент может
воздействовать на мир за пределами сессии, именно поэтому режим файла
проходит через шлюз.
3. Открытие документа
Заголовок раздела «3. Открытие документа»{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "create_pdf", "arguments": { "page_size": "A4", "orientation": "portrait", "title": "Project kickoff brief", "author": "Planning agent" } }}{ "jsonrpc": "2.0", "id": 3, "result": { "content": [ { "type": "text", "text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"page_count\":1,\"page_size\":\"A4\",\"orientation\":\"portrait\"}" } ], "structuredContent": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "page_count": 1, "page_size": "A4", "orientation": "portrait" } }}Каждый результат работы инструмента приходит дважды в одном сообщении:
удобочитаемый текстовый блок content и машиночитаемый
structuredContent. Прочитайте structuredContent.document_id и
передавайте его через каждый следующий вызов.
4. Добавление заголовка
Заголовок раздела «4. Добавление заголовка»Задайте жирный шрифт 16 пунктов, затем разместите заголовок:
{ "jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": { "name": "set_font", "arguments": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "family": "helvetica", "style": "B", "size": 16 } }}{ "jsonrpc": "2.0", "id": 4, "result": { "content": [ { "type": "text", "text": "Font set to helvetica B 16pt on document doc_3b9f435efa0f32d1da7a131d." } ] }}{ "jsonrpc": "2.0", "id": 5, "method": "tools/call", "params": { "name": "add_text", "arguments": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "text": "Project kickoff brief" } }}{ "jsonrpc": "2.0", "id": 5, "result": { "content": [ { "type": "text", "text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"position\":{\"x\":10,\"y\":16,\"page\":0}}" } ], "structuredContent": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "position": { "x": 10, "y": 16, "page": 0 } } }}5. Добавление основного абзаца
Заголовок раздела «5. Добавление основного абзаца»Возврат к обычному шрифту 11 пунктов для вводного текста; width: 0
выбирает многоячеечную компоновку на всю ширину:
{ "jsonrpc": "2.0", "id": 6, "method": "tools/call", "params": { "name": "set_font", "arguments": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "family": "helvetica", "style": "", "size": 11 } }}{ "jsonrpc": "2.0", "id": 6, "result": { "content": [ { "type": "text", "text": "Font set to helvetica 11pt on document doc_3b9f435efa0f32d1da7a131d." } ] }}{ "jsonrpc": "2.0", "id": 7, "method": "tools/call", "params": { "name": "add_text", "arguments": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "text": "Prepared by the planning agent for the 14 July kickoff. Scope, owners, and the first-week checklist are tabled below.", "width": 0, "line_height": 6 } }}{ "jsonrpc": "2.0", "id": 7, "result": { "content": [ { "type": "text", "text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"position\":{\"x\":10,\"y\":29.75,\"page\":0}}" } ], "structuredContent": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "position": { "x": 10, "y": 29.75, "page": 0 } } }}6. Добавление таблицы-чеклиста
Заголовок раздела «6. Добавление таблицы-чеклиста»{ "jsonrpc": "2.0", "id": 8, "method": "tools/call", "params": { "name": "add_table", "arguments": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "html": "<table><tr><th>Work item</th><th>Owner</th><th>Due</th></tr><tr><td>Repository bootstrap</td><td>Devon</td><td>2026-07-15</td></tr><tr><td>CI pipeline</td><td>Ana</td><td>2026-07-17</td></tr><tr><td>Staging deploy</td><td>Priya</td><td>2026-07-21</td></tr></table>" } }}{ "jsonrpc": "2.0", "id": 8, "result": { "content": [ { "type": "text", "text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"position\":{\"x\":10,\"y\":84.75,\"page\":0}}" } ], "structuredContent": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "position": { "x": 10, "y": 84.75, "page": 0 } } }}Каждый вызов, добавляющий содержимое, возвращает обновлённую position
курсора, поэтому агент всегда знает, куда попадёт следующий элемент.
7. Предпросмотр перед запросом одобрения
Заголовок раздела «7. Предпросмотр перед запросом одобрения»preview_layout — это безопасный вызов только для чтения (Safe):
корректно ведущий себя агент проверяет то, что он построил, прежде чем
просить человека одобрить запись:
{ "jsonrpc": "2.0", "id": 9, "method": "tools/call", "params": { "name": "preview_layout", "arguments": { "document_id": "doc_3b9f435efa0f32d1da7a131d" } }}{ "jsonrpc": "2.0", "id": 9, "result": { "content": [ { "type": "text", "text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"total_pages\":1,\"current_page\":0,\"page_dimensions\":{\"width\":595.276,\"height\":841.89},\"margins\":{\"top\":10,\"right\":10,\"bottom\":10,\"left\":10},\"cursor_position\":{\"x\":10,\"y\":84.75}}" } ], "structuredContent": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "total_pages": 1, "current_page": 0, "page_dimensions": { "width": 595.276, "height": 841.89 }, "margins": { "top": 10, "right": 10, "bottom": 10, "left": 10 }, "cursor_position": { "x": 10, "y": 84.75 } } }}8. Запрос на запись файла — сначала отвечает шлюз
Заголовок раздела «8. Запрос на запись файла — сначала отвечает шлюз»Агент просит output_pdf записать готовый бриф на диск, сохраняя
документ (destroy: false) на случай, если человек отклонит запрос и
потребуется вернуться к выводу в base64:
{ "jsonrpc": "2.0", "id": 10, "method": "tools/call", "params": { "name": "output_pdf", "arguments": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "file_path": "C:\\Temp\\nextpdf-mcp\\kickoff-brief.pdf", "destroy": false } }}Файл не записывается. Поскольку режим файла имеет уровень Approval Required, сервер вместо этого отвечает запросом на подтверждение:
{ "jsonrpc": "2.0", "id": 10, "result": { "content": [ { "type": "text", "text": "⚠️ CONFIRMATION REQUIRED\n\nOperation: output_pdf\nDescription: Finalize the PDF and output to file or return as base64\n\nTo proceed, call output_pdf again with parameter _confirmation_token: \"confirm_<single-use-hex>\"\nExpires in 300 seconds." } ], "isError": false }}9. Человек одобряет — повторный вызов с токеном
Заголовок раздела «9. Человек одобряет — повторный вызов с токеном»Агент передаёт текст запроса на подтверждение человеку. После одобрения
он снова вызывает output_pdf с теми же аргументами плюс
_confirmation_token:
{ "jsonrpc": "2.0", "id": 11, "method": "tools/call", "params": { "name": "output_pdf", "arguments": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "file_path": "C:\\Temp\\nextpdf-mcp\\kickoff-brief.pdf", "destroy": false, "_confirmation_token": "confirm_<single-use-hex>" } }}Токен расходуется, запись выполняется, и результат сообщает о записанном файле:
{ "jsonrpc": "2.0", "id": 11, "result": { "content": [ { "type": "text", "text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"file_path\":\"C:\\\\Temp\\\\nextpdf-mcp\\\\kickoff-brief.pdf\",\"file_size\":3612,\"page_count\":1,\"destroyed\":false}" } ], "structuredContent": { "document_id": "doc_3b9f435efa0f32d1da7a131d", "file_path": "C:\\Temp\\nextpdf-mcp\\kickoff-brief.pdf", "file_size": 3612, "page_count": 1, "destroyed": false } }}Проверка записанного файла
Заголовок раздела «Проверка записанного файла»Сессия записала kickoff-brief.pdf (3 612 байт, одна страница, что
совпадает с structuredContent.file_size и page_count).
Зафиксированный вывод qpdf --check для этого файла:
checking kickoff-brief.pdfPDF Version: 2.0File is not encryptedFile is not linearizedNo syntax or stream encoding errors found; the file may still containerrors that qpdf cannot detectЭто структурная проверка, по собственным словам qpdf, — а не заключение о соответствии.
Крайние случаи и подводные камни
Заголовок раздела «Крайние случаи и подводные камни»- Повторный вызов должен повторять те же аргументы. Токен
подтверждения привязан к имени инструмента и каноническому дайджесту
аргументов, для которых он был выдан. Повторный вызов с любым
изменением — даже при переключении
destroy— не расходует токен; вместо этого сервер отвечает новым запросом на подтверждение. Повторите аргументы в точности и добавьте только_confirmation_token. - Токен одноразовый и имеет срок действия. Запрос на подтверждение указывает срок действия (300 секунд). После истечения срока или использования следующий вызов через шлюз получает новый запрос на подтверждение; передайте новый.
- Вывод файла попадает в каталог из списка разрешённых. Сервер
отклоняет
file_pathза пределами настроенного временного каталога с сообщениемOutput path rejected by security policy. Корневой каталог списка разрешённых по умолчанию —nextpdf-mcpвнутри системного временного каталога; операторы меняют его настройкойtemp_dirвnextpdf-mcp.yaml. - Режим base64 не проходит через шлюз.
output_pdfбезfile_pathвозвращает PDF в виде base64 на уровне Review, без побочных эффектов в файловой системе — подробнее об этой границе см. Обязательное подтверждение вывода файлов человеком. - Запрос на подтверждение — это результат, а не ошибка. Сообщение с
запросом приходит с
isError: false; ожидание одобрения — это пауза в рабочем процессе. Не повторяйте попытку в цикле и никогда не подделывайте токен. - Уведомления не получают ответа. После
notifications/initializedне блокируйтесь в ожидании строки ответа.
Производительность
Заголовок раздела «Производительность»Сессия целиком выполняется в памяти: в зафиксированном запуске вызовы, добавляющие содержимое, возвращались за миллисекунды, а общее время определяется циклом одобрения человеком — в этом и смысл шлюза. Хранилище документов удерживает сессию 30 минут простоя по умолчанию (максимум 50 документов), поэтому медленное одобрение не теряет построенный документ — но заброшенный документ высвобождается.
Замечания по безопасности
Заголовок раздела «Замечания по безопасности»- Относитесь к токену подтверждения как к одноразовому секрету. Передавайте человеку текст запроса на подтверждение; не записывайте токен в журнал и не сохраняйте его. Именно поэтому на этой странице зафиксированный токен скрыт.
- Журнал аудита идёт в stderr. Каждое выполнение уровня Caution и выше журналируется для аудита (инструмент, риск, аргументы, результат) через PSR-3, с сокрытием чувствительных параметров. Диагностика никогда не смешивается с потоком протокола.
- Список разрешённых путей — это граница файловой системы. Направьте
temp_dirна каталог, выделенный для вывода Connect; не расширяйте его до каталога общего назначения. - Уровни риска только повышаются. Переопределение оператором в
nextpdf-mcp.yamlможет повысить уровень риска инструмента, но никогда не может понизитьoutput_pdfниже Approval Required.
Соответствие
Заголовок раздела «Соответствие»Этот рецепт не делает никаких нормативных заявлений о соответствии
стандартам. Он документирует транспорт MCP stdio (JSON-RPC 2.0, версия
протокола 2025-06-18, согласованная в зафиксированном обмене
initialize) и контракт сервера по рискам и подтверждению. Шаг
qpdf --check выше подтверждает только структурную целостность
записанного файла; соответствие стандарту определяется независимым
валидатором, а не утверждается производящим программным обеспечением.
См. также
Заголовок раздела «См. также»- Обязательное подтверждение вывода файлов человеком — шлюз подтверждения подробно, включая путь отклонения.
- Рендеринг счёта от начала до конца через REST — тот же движок инструментов по HTTP, с зафиксированной записью обмена.
- Создание первого PDF — минимальная сессия Connect.
- Соглашения рецептов Connect — контракт, которому следует каждый рецепт Connect.
- Уровни риска HITL — каноническая лестница рисков и применение политики.
- Каталог инструментов — официальный каталог инструментов.