Python MCP-сервер
Python MCP-сервер
Заголовок раздела «Python MCP-сервер»Python SDK NextPDF включает сервер Model Context Protocol (MCP), который предоставляет операции извлечения из PDF как нативные инструменты для агентов. Агент с поддержкой MCP, например Claude Code, регистрирует сервер один раз, а затем вызывает инструменты NextPDF так же, как любые другие инструменты.
Сервер представляет собой тонкий адаптер. Каждый инструмент читает PDF с локального диска, через асинхронный клиент обращается к вашей конечной точке NextPDF Connect и возвращает результат в виде строки JSON. Сервер не содержит бизнес-логики и не хранит данные между вызовами.
Установите SDK с дополнительным пакетом MCP:
pip install nextpdf[mcp]Дополнительный пакет mcp устанавливает основной пакет mcp (ограничение mcp>=1.0,<2.0). Серверу требуется Python 3.10 или новее.
Запустите модуль сервера из конфигурации вашего MCP-клиента. В примере ниже оба параметра подключения считываются из окружения хоста, а не хранятся как секрет в файле конфигурации (см. Безопасность):
{ "mcpServers": { "nextpdf": { "command": "python", "args": ["-m", "nextpdf.mcp"], "env": { "NEXTPDF_BASE_URL": "https://connect.example.com", "NEXTPDF_API_KEY": "${NEXTPDF_API_KEY}" } } }}Точка входа python -m nextpdf.mcp вызывает main(), который запускает сервер через стандартные потоки ввода-вывода (stdio) с помощью asyncio.run(serve()). Не путайте её с python -m nextpdf, которая запускает интерфейс командной строки (CLI), а не MCP-сервер.
NEXTPDF_BASE_URL и NEXTPDF_API_KEY обязательны. Сервер создаёт клиент лениво — при первом вызове инструмента. Если хотя бы одна из переменных пуста, он вызывает RuntimeError и возвращает исключение агенту как ошибку инструмента, а не аварийно завершает процесс.
Каталог инструментов и сопоставление с SDK
Заголовок раздела «Каталог инструментов и сопоставление с SDK»Сервер регистрирует восемь инструментов. В имени каждого инструмента используется префикс nextpdf_. Каждый инструмент сопоставляется с методом из пространства имён ast асинхронного клиента (AsyncNextPDF.ast), за исключением двух составных инструментов, отмеченных ниже. Сервер формирует их из вызовов более низкого уровня.
| Инструмент MCP | Вызов SDK | Примечания |
|---|---|---|
nextpdf_extract_text | ast.extract_cited_text(pdf_data, page_index=..., headings_only=...) | Возвращает список CitedTextBlock. |
nextpdf_extract_tables | ast.extract_cited_tables(pdf_data, page_range=...) | Возвращает ExtractCitedTablesResponse. |
nextpdf_get_ast | ast.get_document_ast(pdf_data, page_range_start=0, page_range_end=..., token_budget=...) | Возвращает AstDocument. |
nextpdf_info | ast.get_document_ast(pdf_data) | Сервер формирует сводку метаданных; выделенной конечной точки нет. |
nextpdf_health | нет | Проверяет только переменные окружения; сетевых вызовов не выполняет. |
nextpdf_search | ast.search_ast_nodes(pdf_data, node_type=..., page_index=..., text_query=..., max_results=...) | Возвращает SearchAstNodesResponse. |
nextpdf_get_outline | ast.search_ast_nodes(pdf_data, node_type="heading", max_results=500) | Сервер преобразует узлы заголовков в структуру оглавления. |
nextpdf_diff | ast.get_ast_diff(original_pdf_data, modified_pdf_data) | Возвращает GetAstDiffResponse. |
Учитывайте следующие детали входных данных инструментов, прежде чем подключать агента:
- Все входные пути (
pdf_path,original_pdf_path,modified_pdf_path) — это абсолютные пути к файлам на машине, где работает сервер. Агент передаёт путь, а сервер читает байты локально. Отдельного инструмента загрузки нет. nextpdf_extract_textобъявляет полеmax_pagesв своей входной схеме, но обработчик текста не передаёт его в SDK. Для текста ограничение по страницам задаётся черезpage_index(одна страница с отсчётом от 0). Используйтеnextpdf_get_astсmax_pages, когда нужно ограничить обход всего документа.nextpdf_get_astпреобразуетmax_pagesво включительный диапазон страниц[0, max_pages - 1](значениеmax_pagesпо умолчанию — 50). Передайтеtoken_budget, чтобы ограничить размер возвращаемого дерева.nextpdf_infoвозвращаетschema_version,source_hash,page_count,estimated_tokens,root_node_typeиroot_children_count. Эти значения берутся из моделиAstDocument, гдеestimated_tokens— вычисляемое свойство (примерно четыре символа на токен).nextpdf_get_outlineвозвращает по одной записи для каждого заголовка с полямиid,page_index,textиdepth(значение берётся изattributes["level"]узла, по умолчанию 1), а такжеheading_count,total_matchesиtruncated.
Инструменты извлечения с цитированием прикрепляют CitationAnchor к каждому результату. Каждый якорь включает node_id, page_index, нормализованный bbox (координаты в диапазоне от 0.0 до 1.0) и оценку уверенности confidence (от 0.0 до 1.0). Агенты, которым нужны сведения о происхождении результата, должны показывать эти поля, а не только необработанный текст.
Обработка ошибок, тайм-ауты и квоты
Заголовок раздела «Обработка ошибок, тайм-ауты и квоты»Сервер никогда не пропускает исключения на транспортный уровень агента. Диспетчер call_tool перехватывает каждую ошибку и возвращает её в виде JSON TextContent, поэтому при неудачном вызове инструмент возвращает структурированную полезную нагрузку, которую агент может прочитать, вместо разрыва соединения. Форматы полезной нагрузки:
| Условие | Возвращаемый JSON |
|---|---|
| Неизвестное имя инструмента | {"error": "Unknown tool: <name>"} |
| Отсутствует входной файл | {"error": "PDF file not found: <path>"} |
Любой NextPDFError или его подкласс | {"error": "<message>", "error_type": "<class>", "status_code": <int?>} |
| Любое другое исключение | {"error": "Unexpected error: <message>"} |
status_code появляется, только если он есть у базовой ошибки. SDK сопоставляет ответы HTTP с типизированной иерархией исключений, корнем которой является NextPDFError:
| Исключение | Статус HTTP | error_code | Когда |
|---|---|---|---|
NextPDFLicenseError | 402 | license/tier-required | Для этой операции конечной точке требуется более высокий уровень лицензии на стороне сервера. |
AstNoStructTreeError | 422 | ast/no-struct-tree | PDF не размечен, а эвристический резервный механизм на сервере не включён. |
QuotaExceededError | 429 | quota/exceeded | Достигнут лимит частоты запросов или квота. Содержит retry_after (в секундах), когда сервер отправляет заголовок Retry-After. |
AstBuildTimeoutError | 504 | ast/build-timeout | Построение AST превысило бюджет времени сервера. Уменьшите диапазон страниц. |
NextPDFAPIError | другие 4xx/5xx | предоставляется сервером | Любой другой сбой на уровне API. |
Используйте эти рекомендации для интеграций с агентами:
- Тайм-ауты. HTTP-клиент использует фиксированные тайм-ауты по умолчанию: 60 секунд в сумме и 10 секунд на подключение. Медленный или большой документ приводит либо к
AstBuildTimeoutError(сервер прекратил построение AST), либо, если истёк тайм-аут самого клиента, к полезной нагрузкеUnexpected errorот транспортного уровня. Когда вы видитеast/build-timeout, попросите агента сузить область: уменьшитеmax_pagesвnextpdf_get_astили задайтеpage_index/page_startиpage_endв инструментах извлечения. - Квоты и пауза перед повтором. При 429 инструмент возвращает
error_typeсо значениемQuotaExceededErrorиstatus_code429. Значениеretry_afterхранится в объекте исключения. Поскольку сервер сериализует толькоerror,error_typeиstatus_code, агенту следует расценивать 429 как сигнал сделать паузу и повторить позже, а не пытаться получить заголовок повтора из вывода инструмента. Контролируйте соблюдение квот на конечной точке Connect, а не в агенте. - Неразмеченные PDF. 422
ast/no-struct-treeозначает, что у исходного PDF нет дерева структуры. Включите эвристический режим на сервере для таких документов или отправьте их на этап разметки перед извлечением.
Безопасность: ограничение области API-ключа и принцип наименьших привилегий
Заголовок раздела «Безопасность: ограничение области API-ключа и принцип наименьших привилегий»Относитесь к API-ключу как к секрету и обращайтесь с ним так же осторожно, как с паролем базы данных.
- Никогда не встраивайте ключ в файл конфигурации MCP. Приведённый выше пример JSON ссылается на
${NEXTPDF_API_KEY}, чтобы значение подставлялось из окружения хоста или менеджера секретов во время запуска. Файл конфигурации может быть помещён в систему контроля версий; секрет — нет. - Ограничьте область действия ключа извлечением только для чтения. MCP-сервер использует только интерфейс извлечения AST (
extract_cited_text,extract_cited_tables,get_document_ast,search_ast_nodes,get_ast_diff). Он не отрисовывает, не подписывает, не редактирует и не изменяет документы. Выдайте агенту ключ, область действия которого на стороне сервера ограничена этими путями чтения, чтобы скомпрометированный агент не мог получить доступ к операциям записи или операциям более высокого уровня. - Используйте отдельный ключ для каждого агента. Отдельный ключ для каждого агента позволяет отзывать или менять одну интеграцию, не затрагивая другие, и делает журналы конечной точки привязанными к конкретному агенту.
- Ограничьте файловую систему. Поскольку каждый инструмент читает абсолютный путь с локального диска, сервер может прочитать любой файл, доступный процессу хоста. Запускайте его от имени непривилегированного пользователя, ограничьте его рабочий каталог папкой с документами и никогда не запускайте его от привилегированной учётной записи.
- Предпочитайте Transport Layer Security (TLS). Указывайте
NEXTPDF_BASE_URLнаhttps://-конечную точку для любого нелокального развёртывания. SDK отправляет ключ в виде токенаBearerв заголовкеAuthorization, поэтому передача открытым текстом раскрыла бы его в канале.
См. Безопасность и эксплуатация Connect для средств управления на стороне конечной точки, которые поддерживают эти клиентские практики.
Локальное тестирование сервера перед подключением агента
Заголовок раздела «Локальное тестирование сервера перед подключением агента»Проверьте сервер отдельно, прежде чем подключать агента. Самая быстрая проверка не требует ни PDF, ни сети:
python -c "from nextpdf.mcp import _tool_definitions; print(len(_tool_definitions()))"Корректная установка выводит 8. Если вы видите ImportError с упоминанием дополнительного пакета mcp, значит необязательная зависимость отсутствует. Переустановите с помощью pip install nextpdf[mcp].
Затем проверьте через CLI те же кодовые пути SDK, которые используют инструменты. CLI обращается к вашей конечной точке с теми же двумя переменными окружения. Задайте их один раз:
export NEXTPDF_BASE_URL="https://connect.example.com"export NEXTPDF_API_KEY="$(cat /run/secrets/nextpdf_api_key)"Затем проверьте версию, соединение и реальное извлечение:
nextpdf versionnextpdf info /path/to/sample.pdfnextpdf extract text /path/to/sample.pdf --headings-onlynextpdf version выполняется без учётных данных и подтверждает, что пакет импортируется. nextpdf info задействует get_document_ast — тот же вызов, на котором основаны nextpdf_get_ast и nextpdf_info. Если оба вызова успешны, учётные данные и конечная точка корректны, и соответствующие инструменты MCP будут работать.
Чтобы управлять протоколом MCP напрямую, используйте основной MCP Inspector (поставляется с пакетом mcp). Укажите ему ту же команду и окружение, которые будет использовать ваш агент, затем вручную получите список инструментов и вызовите их. Убедитесь, что nextpdf_health сообщает status: "ok". Он возвращает misconfigured всякий раз, когда NEXTPDF_BASE_URL или NEXTPDF_API_KEY не задана, и это самый быстрый способ обнаружить отсутствующее значение окружения, прежде чем агент вызовет реальный инструмент.
Мониторинг и отладка вызовов инструментов
Заголовок раздела «Мониторинг и отладка вызовов инструментов»MCP-сервер обменивается данными через стандартные потоки ввода-вывода (stdio), поэтому по стандартному выводу идёт поток протокола, и он должен оставаться чистым. Сервер не настраивает собственное журналирование приложения. Ваши основные каналы наблюдаемости — это структурированные полезные нагрузки с ошибками инструментов, CLI и собственные журналы вашей конечной точки.
- Полезные нагрузки с ошибками инструментов — основной сигнал. Каждый неудачный вызов возвращает объект JSON с полем
errorи, для ошибок SDK,error_typeиstatus_code(см. Обработка ошибок). Настройте хост агента на запись этих полезных нагрузок. По ним видно, какой инструмент отказал и почему, без дополнительного инструментирования сервера. - Воспроизведите через CLI с отладочным журналированием. Сам MCP-сервер не выдаёт журналов, но CLI задействует те же вызовы SDK и ведёт журнал. Воспроизведите отказавший инструмент через соответствующую команду CLI с
--log-level debug. CLI пишет журнал в stderr с метками времени и записывает полные трассировки для непредвиденных ошибок, что является самым прямым способом увидеть, что делает обработчик, без подключения отладчика. - Проверка работоспособности как зонд. Вызовите
nextpdf_health, чтобы подтвердить, что сервер видит базовый URL и API-ключ. Результат сообщаетsdk_version,server_url,api_key_configured(булево значение, никогда не сам ключ) иstatus. - Наблюдаемость на стороне конечной точки. Поскольку каждый инструмент сопоставляется с одним запросом Connect, сопоставляйте активность инструментов с журналами доступа конечной точки по API-ключу и метке времени. Размещайте конечную точку за теми же средствами аутентификации, квотирования и наблюдаемости, что вы используете для других клиентов сервиса.
Устранение типичных проблем интеграции с агентами
Заголовок раздела «Устранение типичных проблем интеграции с агентами»| Симптом | Вероятная причина | Решение |
|---|---|---|
Сервер не запускается с ImportError о дополнительном пакете mcp (extra) | Необязательная зависимость mcp не установлена | Установите с помощью pip install nextpdf[mcp]. |
Первый вызов инструмента возвращает {"error": "NEXTPDF_BASE_URL environment variable is required..."} | Блок env MCP не передал базовый URL, или оболочка не развернула ${NEXTPDF_BASE_URL} | Задайте переменную в окружении хоста агента и убедитесь, что средство запуска её разворачивает. |
nextpdf_health сообщает "status": "misconfigured" | Одна из двух обязательных переменных пуста | Укажите обе переменные: NEXTPDF_BASE_URL и NEXTPDF_API_KEY. |
Каждый инструмент с путём возвращает {"error": "PDF file not found: <path>"} | Агент передал относительный путь или путь на стороне хоста, который процесс сервера не видит | Передайте абсолютный путь, доступный для чтения пользователю сервера; проверьте с помощью nextpdf info <path>. |
Инструмент возвращает error_typeNextPDFLicenseError (статус 402) | Для операции нужен более высокий уровень лицензии на стороне сервера | Используйте конечную точку и ключ, дающие право на эту операцию. |
Инструмент возвращает error_typeAstNoStructTreeError (статус 422) | PDF не размечен, а эвристический резервный механизм отключён | Включите эвристический режим на конечной точке или сначала разметьте PDF. |
Инструмент возвращает error_typeQuotaExceededError (статус 429) | Достигнут предел частоты запросов или квота | Сделайте паузу и повторите попытку; повысьте квоту конечной точки, если предел слишком низок. |
Инструмент возвращает error_typeAstBuildTimeoutError (статус 504) или тайм-аут транспорта | Документ слишком велик для бюджета времени | Сузьте область с помощью max_pages, page_index или page_start/page_end. |
| Агент не регистрирует ни одного инструмента NextPDF | Агент вызвал python -m nextpdf (CLI) вместо python -m nextpdf.mcp | Используйте python -m nextpdf.mcp в качестве command/args. |
Сведения об отказах на уровне конечной точки и проверках развёртывания см. в разделе Устранение неполадок Connect. Об операциях SDK, на которых построены эти инструменты, см. в справочнике CLI и в обзоре SDK.