Ir al contenido

Guía para desarrolladores de NextPDF Connect

NextPDF Connect (nextpdf/server) expone como servicio el motor PDF 2.0 de NextPDF, independiente del framework. No reimplementa la generación de PDF. Publica cada capacidad del motor como una herramienta con nombre y descrita mediante un esquema, y sirve ese catálogo a través de tres transportes: el Model Context Protocol (MCP) sobre la entrada y la salida estándar, una interfaz de programación de aplicaciones (API) REST y gRPC. Usar esta guía al desarrollar contra el servidor, ampliar su conjunto de herramientas u operarlo en producción.

Tres conceptos sostienen todo el diseño: el registro de herramientas, los tres transportes independientes y la compuerta de confirmación con intervención humana (HITL). Esta página explica cómo encajan entre sí y cómo trabajar con ellos sin debilitar el modelo de seguridad. Para conocer los símbolos exactos de herramientas, RPC y mensajes, consulta la referencia de la API.

Requisitos previos: PHP 8.4, Composer 2 y, para los transportes en red, el binario de RoadRunner y al menos una clave de API. Instalarlo con composer require nextpdf/server.

Se deben mantener las responsabilidades en el lado correcto de cada límite. Una herramienta es un envoltorio delgado sobre una llamada al motor; no debe contener la interpretación del diseño, la semántica del documento ni inteligencia de transformación.

CapaPropiedad deResponsabilidadNo colocar aquí
Cliente o agenteLa integraciónDecidir a qué herramienta llamar; transmitir los desafíos de confirmación a una persona.Lógica del motor o detección de niveles.
Transportenextpdf/serverEstructurar las solicitudes (JSON-RPC, HTTP o Protocol Buffers), autenticar y enrutar hacia el ejecutor de herramientas.Semántica del documento.
Registro de herramientasnextpdf/serverDescubrir niveles, registrar herramientas sujetas a la lista de permitidos de seguridad y buscar una herramienta por su nombre.Generación de PDF.
Herramientanextpdf/serverValidar los argumentos contra el esquema de entrada y llamar al motor.Interpretación del diseño u orquestación de varios pasos.
Compuerta de confirmaciónnextpdf/serverRetener una operación ApprovalRequired hasta que una persona la autorice.Autenticación de quien llama.
Motornextpdf/core (y nextpdf/premium)Generar, inspeccionar y transformar el contenido del PDF.Asuntos de transporte o de autenticación.

Cada transporte tiene su propio punto de entrada y su factoría de arranque, y cada uno construye su grafo de objetos de forma explícita. No hay ningún contenedor de inyección de dependencias por registrar.

  1. Carga la configuración. El servidor MCP resuelve la configuración dando prioridad a las variables de entorno (NEXTPDF_MCP_*) sobre la sección nextpdf_mcp del archivo YAML y, a su vez, sobre los valores predeterminados integrados, y produce un readonlyMcpConfig. Los servidores REST y gRPC leen HttpConfig de las variables de entorno NEXTPDF_*. Consultar Configuración.
  2. Construye la política de seguridad. La lista de permitidos enabled_tools se construye antes que el registro para restringir el descubrimiento desde el primer registro.
  3. Construye el registro y descubre las herramientas. ToolRegistry::registerDefaults() registra el nivel core, luego los proveedores Pro y Enterprise cuando sus clases se pueden resolver, y después los proveedores de AST y de mutación incluidos, sujetos a sus controles de entorno.
  4. Construye los almacenes compartidos y la compuerta. El almacén de documentos en memoria se construye a partir del TTL y la capacidad configurados; la ConfirmationGate se ensambla con su almacén de tokens de un solo uso.
  5. Vincula el transporte. El MCP entra en un bucle de lectura, procesamiento y escritura sobre stdio hasta el fin del archivo. REST y gRPC construyen su tabla de rutas o de servicios a partir de los niveles detectados y entregan el bucle de solicitudes a RoadRunner.

A partir de ahí, una solicitud fluye así: autenticar (REST y gRPC), resolver la herramienta o la operación, ejecutar la compuerta de confirmación para el trabajo ApprovalRequired, ejecutar contra el motor y devolver el resultado. Consultar Arranque y descubrimiento.

Los tres transportes comparten el registro, la configuración y la compuerta como conceptos, pero son procesos independientes. Iniciar uno no inicia los demás.

TransportePunto de entradaCuándo elegirlo
MCPbin/nextpdf-mcpUn cliente de IA local que lanza el servidor como un subproceso de confianza.
RESTbin/nextpdf-serverClientes HTTP en red; descrito por un documento OpenAPI 3.1.
gRPCbin/nextpdf-grpcClientes tipados y de streaming; el servicio nextpdf.connect.v1.NextPDFConnect.

Los transportes se eligen según el perfil de RoadRunner que se ejecute: .rr.yaml (solo REST), .rr.grpc.yaml (solo gRPC) o .rr.full.yaml (ambos). El transporte MCP es un subproceso simple y no necesita supervisor. Los detalles del protocolo por transporte están en Transporte MCP, Transporte REST y Transporte gRPC.

Ejecutar los transportes en red bajo RoadRunner con almacenes compartidos y claves montadas desde secretos. El perfil combinado permite que REST y gRPC compartan un único supervisor.

Ruta o ajustePropósito
.rr.full.yamlPerfil combinado de REST y gRPC bajo un único supervisor.
NEXTPDF_API_KEYS_FILERuta a un archivo de claves de API montado desde un secreto y con recarga en caliente.
NEXTPDF_REDIS_HOSTHabilita los almacenes de límite de tasa, idempotencia y documentos respaldados por Redis para grupos de varios workers.
NEXTPDF_WORKER_COUNT / NEXTPDF_GRPC_WORKER_COUNTDimensionamiento del grupo de workers para HTTP y gRPC.
Directorio base de salidaVolumen dedicado con permisos de sistema de archivos de mínimo privilegio para las herramientas de salida a archivo.

El siguiente ejemplo de shell arranca el perfil combinado con claves montadas desde secretos y un almacén de Redis compartido. No contiene secretos en el propio archivo; las claves se montan en /run/secrets/api-keys.

Ventana de terminal
export NEXTPDF_API_KEYS_FILE=/run/secrets/api-keys
export NEXTPDF_WORKER_COUNT=8
export NEXTPDF_GRPC_WORKER_COUNT=4
export NEXTPDF_REDIS_HOST=redis
./vendor/bin/rr serve -c .rr.full.yaml

Para un grupo de varios workers, configurar Redis y confirmar que ext-redis esté presente en la imagen en ejecución; sin él, los almacenes de límite de tasa, idempotencia y documentos son por worker. Consultar Despliegue.

Registro de herramientas y resolución de niveles

Sección titulada «Registro de herramientas y resolución de niveles»

NextPDF\Server\ToolRegistry (src/ToolRegistry.php) construye el catálogo en el arranque. El nivel es un invariante declarado: cada herramienta devuelve su propio tier() y riskLevel(); el registro nunca infiere el nivel a partir del espacio de nombres ni del empaquetado.

  1. El nivel core se registra incondicionalmente: las herramientas de documento y de diagnóstico, además de generate_barcode cuando el registro de codificadores de código de barras del core está presente, y parse_pdf solo cuando NEXTPDF_MCP_TOOL_PARSE_PDF_ENABLED es true o 1.
  2. Los proveedores Pro y Enterprise se registran cuando sus clases de proveedor se pueden resolver, comprobadas con class_exists(). Un nivel ausente se omite en silencio.
  3. Los proveedores de AST y de mutación incluidos se registran bajo el nivel Pro, controlados por NEXTPDF_AST_TOOLS_ENABLED y NEXTPDF_MUTATION_TOOLS_ENABLED (ambos habilitados de forma predeterminada).
  4. El filtro de la política de seguridad intersecta cada registro con la lista de permitidos enabled_tools. La lista de permitidos solo resta; nunca añade. El contador por nivel solo cuenta una herramienta admitida por la política.

Los recuentos resultantes por nivel y el total se informan en la respuesta initialize del MCP y en el endpoint GET /api/v1/capabilities de REST. Tratar cualquier total fijo en el texto como obsoleto; consultar el servidor en ejecución. Consultar Catálogo de herramientas.

Niveles de riesgo y la compuerta de confirmación

Sección titulada «Niveles de riesgo y la compuerta de confirmación»

Cada herramienta declara uno de los cuatro niveles de riesgo del enum RiskLevel (src/Config/RiskLevel.php): Safe (0), Caution (1), Review (2) y ApprovalRequired (3). El registro de auditoría se aplica en Caution y superiores. Una sustitución de configuración puede elevar el riesgo de una herramienta; nunca puede rebajar una herramienta que es ApprovalRequired por diseño. El cargador de configuración lanza una excepción en el momento de la carga y el servidor se niega a arrancar en lugar de ejecutarse con una compuerta debilitada.

Cuando se invoca una herramienta ApprovalRequired sin un token válido, la ConfirmationGate (src/Mcp/ConfirmationGate.php) devuelve un token de desafío de un solo uso. El token vincula el nombre de la herramienta, un nonce aleatorio y un tiempo de vida (TTL) de 300 segundos, no los argumentos, porque los clientes pueden volver a serializar los argumentos con un orden de claves distinto en el reintento. El agente transmite el desafío a una persona y vuelve a invocar la misma herramienta con el token en el argumento _confirmation_token. El token se consume al usarlo, habilitando exactamente una llamada controlada.

El siguiente ejemplo de PHP muestra un ayudante independiente del transporte que dirige una llamada a una herramienta MCP y, ante un desafío de confirmación, muestra el desafío a un aprobador humano antes de reintentar con el token emitido. Declara tipos estrictos, está completamente tipado y captura la excepción más específica en lugar de ocultar cualquier error.

examples/connect/confirm-and-call.php
<?php
declare(strict_types=1);
namespace App\Connect;
use JsonException;
/**
* Drives one tool call and resolves an ApprovalRequired confirmation
* challenge through a human approver before retrying.
*/
final readonly class ConfirmingToolCaller
{
public function __construct(
private McpClientInterface $client,
private HumanApproverInterface $approver,
) {}
/**
* @param non-empty-string $toolName
* @param array<string, mixed> $arguments
*
* @return array<string, mixed> The tool result content
*
* @throws JsonException When a response cannot be decoded
* @throws ApprovalDeniedException When the human declines the challenge
*/
public function call(string $toolName, array $arguments): array
{
$response = $this->client->callTool($toolName, $arguments);
if (!isset($response['challenge'], $response['token'])) {
return $response;
}
$challenge = (string) $response['challenge'];
$token = (string) $response['token'];
if (!$this->approver->approve($toolName, $challenge)) {
throw new ApprovalDeniedException($toolName);
}
$arguments['_confirmation_token'] = $token;
return $this->client->callTool($toolName, $arguments);
}
}

Conectar McpClientInterface, HumanApproverInterface y ApprovalDeniedException al transporte y canal de aprobación propios. El reintento reutiliza los argumentos originales más el token emitido; no aprobar nunca un desafío automáticamente sin una decisión humana. Consultar Niveles de riesgo HITL.

El servidor se extiende añadiendo herramientas y aportando proveedores, no editando el registro.

Punto de extensiónUsoRestricción
Una clase que implementa ToolInterfaceUna nueva capacidad del motor expuesta como herramienta.Declarar tier(), riskLevel(), category() y un inputSchema() de JSON Schema; mantenerla como un envoltorio delgado del motor.
Un ToolProviderInterface como proveedorRegistrar un conjunto de herramientas para un nivel.Los proveedores Pro y Enterprise se descubren mediante class_exists(); no exigir el paquete propietario desde el servidor.
enabled_tools (lista de permitidos)Acotar con mínimo privilegio el catálogo expuesto.La lista de permitidos solo resta; no puede registrar una herramienta ausente.
risk_level_overridesEndurecer un despliegue elevando el riesgo de una herramienta.Solo permite elevar; rebajar una herramienta ApprovalRequired hace fallar el arranque.
Costuras inyectables de transporte y de workerProbar el servidor de forma aislada.Estas costuras existen para las pruebas, no para el cableado de la aplicación.
  1. Elegir un perfil. Ejecutar .rr.yaml, .rr.grpc.yaml o .rr.full.yaml para los transportes expuestos.
  2. Montar las claves desde un secreto. Apuntar NEXTPDF_API_KEYS_FILE a un archivo de secreto; preferir el almacén de claves de archivo con recarga en caliente para que la rotación no necesite reinicio.
  3. Configurar los almacenes compartidos. Establecer NEXTPDF_REDIS_HOST y confirmar ext-redis para cualquier grupo de más de un worker; colocar el almacén de trabajos de SQLite en un volumen en el que todos los workers puedan escribir.
  4. Terminar el TLS. Ejecutar REST detrás de un terminador de seguridad de la capa de transporte (TLS); ejecutar gRPC con TLS mutuo en cualquier red no confiable, con la clave del servidor, el certificado del servidor y la autoridad certificadora del cliente aportados como secretos de despliegue.
  5. Sondear el estado. Usar los endpoints anónimos /healthz y /readyz (REST) o los RPC HealthCheck y ReadinessCheck (gRPC) para los sondeos del orquestador.
  6. Acotar el catálogo. Restringir enabled_tools al conjunto mínimo que necesite una integración.

Verificar el estado de Redis en lugar de darlo por sentado: el servidor REST recurre a almacenes en memoria cuando una conexión de Redis configurada falla. Consultar Despliegue y Seguridad y operaciones.

FalloDónde apareceRespuesta recomendada
Desconocido: document_idEjecución de la herramientaDevolver un error definido a quien llama; indicarle que llame primero a create_pdf.
ETag obsoleto en una mutaciónHerramienta de mutación de ASTVolver a leer el documento con get_document_ast y reintentar con el ETag fresco.
Clave de API ausente o inválida (REST)Middleware de autenticaciónDevolver 401 con un desafío WWW-Authenticate: Bearer; no revelar qué parte estaba mal.
Nivel sin derecho (REST)AutorizaciónDevolver 403; el nivel de la clave está por debajo del nivel de la operación.
Ruta de nivel ausente (REST)EnrutadorDevolver 404; el paquete no está instalado. Es una operación esperada, no un fallo.
Token incorrecto (gRPC)Autenticador de gRPCHacer fallar la llamada con UNAUTHENTICATED.
Redis inalcanzableArranque o ejecuciónDegradar a almacenes en memoria; alertar a los operadores y verificar el estado de Redis.
Ruta de salida fuera del directorio baseHerramienta de salida a archivoFallar de forma cerrada; la ruta se canonicaliza y el recorrido se rechaza.

Exponer los fallos del motor como objetos de error definidos, nunca como éxitos silenciosos. El modelo de error por transporte se detalla en la referencia de la API.

AsuntoPredeterminadoCuándo sustituirlo
parse_pdfDeshabilitado (opcional mediante NEXTPDF_MCP_TOOL_PARSE_PDF_ENABLED).Habilitarlo solo cuando una integración necesite inspección estructural.
enabled_toolsVacío (todas las herramientas descubiertas permitidas).Definir una lista de permitidos explícita para despliegues de mínimo privilegio.
Sustituciones de riesgoNinguna.Elevar el riesgo para un despliegue endurecido; no intentar nunca una rebaja.
document_ttl / max_documents1800 segundos / 50 documentos.Reducirlos para despliegues sensibles a la residencia o con memoria limitada.
allow_file_outputHabilitado.Establecerlo en false para despliegues sin estado y sensibles a la residencia.
Número de workersCuatro (HTTP), dos (gRPC).Dimensionarlos según la latencia observada y los núcleos disponibles.
Escucha RESTHTTP en texto plano detrás de un terminador de TLS.Terminar siempre el TLS aguas arriba; no exponer nunca texto plano en una red no confiable.
gRPC en redes no confiablesTLS mutuo.Obligatorio; no ejecutar nunca una escucha gRPC en texto plano en una red no confiable.
  • Las pruebas del registro verifican que un nivel Pro o Enterprise ausente se omite en silencio y que el catálogo core sigue registrándose.
  • Las pruebas de la lista de permitidos verifican que enabled_tools resta y nunca añade una herramienta que el registro no descubrió.
  • Las pruebas de la compuerta de confirmación verifican que una herramienta ApprovalRequired devuelve un desafío en la primera llamada y se ejecuta una vez con un token válido de un solo uso, y que el token expira tras su TTL.
  • Las pruebas de rebaja verifican que una entrada de risk_level_overrides que debilita una herramienta ApprovalRequired hace fallar el arranque.
  • Las pruebas de autenticación cubren claves ausentes, malformadas, deshabilitadas y expiradas en REST (401 con WWW-Authenticate) y gRPC (UNAUTHENTICATED), y el rechazo por derecho de nivel (403).
  • Las pruebas de concurrencia aseveran que un ETag obsoleto hace fallar una mutación y que una idempotency_key repetida reproduce el resultado en caché.
  • Las pruebas de contención de rutas verifican que se rechaza una ruta de salida a archivo que se resuelve fuera del directorio base.
  • Mantener los fixtures pequeños y no sensibles; no confirmar nunca una clave de API real ni el contenido de un documento.