Aller au contenu

Guide développeur NextPDF Connect

NextPDF Connect (nextpdf/server) encapsule le moteur PDF 2.0 NextPDF, indépendant de tout framework, dans un service. Il ne réimplémente pas la génération de PDF. Il expose chaque capacité du moteur sous forme d’outil nommé et décrit par un schéma, puis met ce catalogue à disposition via trois transports : le Model Context Protocol (MCP) sur l’entrée et la sortie standard, une interface de programmation (API) Representational State Transfer (REST), et gRPC. Utilise ce guide lorsque tu développes avec le serveur, que tu étends son jeu d’outils ou que tu l’exploites en production.

Trois concepts structurent toute la conception : le registre d’outils, les trois transports indépendants et la barrière de confirmation human-in-the-loop (HITL). Cette page explique comment ils s’articulent et comment les utiliser sans affaiblir le modèle de sécurité. Pour les symboles exacts des outils, des RPC et des messages, consulte la référence de l’API.

Prérequis : PHP 8.4, Composer 2 et, pour les transports réseau, le binaire RoadRunner ainsi qu’au moins une clé d’API. Installe le package avec composer require nextpdf/server.

Garde chaque responsabilité du bon côté des frontières. Un outil est une fine enveloppe autour d’un appel au moteur ; il ne doit porter ni l’interprétation de la mise en page, ni la sémantique du document, ni l’intelligence de transformation.

CouchePropriétaireResponsabilitéÀ ne pas mettre ici
Client ou agentTon intégrationDécider quel outil appeler ; relayer les défis de confirmation à un humain.La logique du moteur ou la détection de niveau.
Transportnextpdf/serverEncadrer les requêtes (JSON-RPC, HTTP ou Protocol Buffers), authentifier et router vers l’exécuteur d’outils.La sémantique du document.
Registre d’outilsnextpdf/serverDécouvrir les niveaux, enregistrer les outils sous réserve de la liste d’autorisation de sécurité et retrouver un outil par son nom.La génération de PDF.
Outilnextpdf/serverValider les arguments contre le schéma d’entrée et appeler le moteur.L’interprétation de la mise en page ou l’orchestration multi-étapes.
Barrière de confirmationnextpdf/serverRetenir une opération ApprovalRequired jusqu’à son autorisation par un humain.L’authentification de l’appelant.
Moteurnextpdf/core (et nextpdf/premium)Générer, inspecter et transformer le contenu PDF.Les préoccupations de transport ou d’authentification.

Chaque transport a son propre point d’entrée et sa propre fabrique de démarrage, et chacun construit explicitement son graphe d’objets. Il n’y a aucun conteneur d’injection de dépendances dans lequel enregistrer ces objets.

  1. Charge la configuration. Le serveur MCP résout la configuration en donnant la priorité aux variables d’environnement (NEXTPDF_MCP_*) sur la section nextpdf_mcp du fichier YAML, qui prime elle-même sur les valeurs par défaut intégrées, et produit un readonlyMcpConfig. Les serveurs REST et gRPC lisent HttpConfig depuis les variables d’environnement NEXTPDF_*. Voir Configuration.
  2. Construis la politique de sécurité. La liste d’autorisation enabled_tools est construite avant le registre, afin qu’elle contraigne la découverte dès le premier enregistrement.
  3. Construis le registre et découvre les outils. ToolRegistry::registerDefaults() enregistre le niveau Core, puis les fournisseurs Pro et Enterprise lorsque leurs classes sont résolues, puis les fournisseurs AST et de mutation embarqués sous réserve de leurs barrières d’environnement.
  4. Construis les magasins partagés et la barrière. Le magasin de documents en mémoire est construit à partir du TTL et de la capacité configurés ; la ConfirmationGate est assemblée avec son magasin de jetons à usage unique.
  5. Lie le transport. Le transport MCP entre dans une boucle lecture-traitement-écriture sur stdio jusqu’à la fin de fichier. REST et gRPC construisent leur table de routes ou de services à partir des niveaux détectés et confient la boucle de requêtes à RoadRunner.

Une requête suit ensuite ce flux : authentifier (REST et gRPC), résoudre l’outil ou l’opération, exécuter la barrière de confirmation pour le travail ApprovalRequired, exécuter l’appel sur le moteur et renvoyer le résultat. Voir Démarrage et découverte.

Les trois transports partagent le registre, la configuration et la barrière comme concepts, mais ce sont des processus indépendants. Démarrer l’un ne lance pas les autres.

TransportPoint d’entréeQuand le choisir
MCPbin/nextpdf-mcpUn client IA local qui lance le serveur comme sous-processus de confiance.
RESTbin/nextpdf-serverDes clients HTTP en réseau ; décrit par un document OpenAPI 3.1.
gRPCbin/nextpdf-grpcDes clients typés et en streaming ; le service nextpdf.connect.v1.NextPDFConnect.

Choisis les transports en fonction du profil RoadRunner que tu exécutes : .rr.yaml (REST uniquement), .rr.grpc.yaml (gRPC uniquement), ou .rr.full.yaml (les deux). Le transport MCP est un simple sous-processus et n’a besoin d’aucun superviseur. Les détails de protocole par transport se trouvent dans Transport MCP, Transport REST, et Transport gRPC.

Exécute les transports réseau sous RoadRunner avec des magasins partagés et des clés montées depuis un secret. Le profil combiné permet à REST et gRPC de partager le même superviseur.

Chemin ou réglageRôle
.rr.full.yamlProfil REST et gRPC combiné sous un seul superviseur.
NEXTPDF_API_KEYS_FILEChemin vers un fichier de clés d’API monté depuis un secret et rechargé à chaud.
NEXTPDF_REDIS_HOSTActive les magasins de limitation de débit, d’idempotence et de documents adossés à Redis pour les pools multi-workers.
NEXTPDF_WORKER_COUNT / NEXTPDF_GRPC_WORKER_COUNTDimensionnement du pool de workers pour les pools HTTP et gRPC.
Répertoire de sortie de baseVolume dédié avec des permissions de système de fichiers respectant le moindre privilège pour les outils de sortie vers fichier.

L’exemple de shell suivant démarre le profil combiné avec des clés montées depuis un secret et un magasin Redis partagé. Aucun secret n’est écrit dans le fichier lui-même ; les clés sont montées sur /run/secrets/api-keys.

Fenêtre 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

Pour un pool multi-workers, configure Redis et vérifie que ext-redis est présent dans l’image exécutée ; sans lui, les magasins de limitation de débit, d’idempotence et de documents restent propres à chaque worker. Voir Déploiement.

NextPDF\Server\ToolRegistry (src/ToolRegistry.php) construit le catalogue au démarrage. Le niveau est un invariant déclaré : chaque outil renvoie ses propres tier() et riskLevel() ; le registre n’infère jamais le niveau depuis l’espace de noms ni l’empaquetage.

  1. Le niveau Core s’enregistre sans condition : les outils de document et de diagnostic, plus generate_barcode quand le registre d’encodeurs de codes-barres du core est présent, plus parse_pdf seulement quand NEXTPDF_MCP_TOOL_PARSE_PDF_ENABLED vaut true ou 1.
  2. Les fournisseurs Pro et Enterprise s’enregistrent lorsque leurs classes de fournisseur sont résolues, après sondage avec class_exists(). Un niveau absent est ignoré silencieusement.
  3. Les fournisseurs AST et de mutation embarqués s’enregistrent sous le niveau Pro, contrôlés par NEXTPDF_AST_TOOLS_ENABLED et NEXTPDF_MUTATION_TOOLS_ENABLED (les deux activés par défaut).
  4. Le filtre de politique de sécurité croise chaque enregistrement avec la liste d’autorisation enabled_tools. La liste d’autorisation ne fait que soustraire ; elle n’ajoute jamais. Le compteur par niveau ne comptabilise que les outils admis par la politique.

Les décomptes par niveau qui en résultent, ainsi que le total, sont rapportés dans la réponse initialize du MCP et l’endpoint REST GET /api/v1/capabilities. Considère comme obsolète tout total figé dans le texte ; interroge le serveur en cours d’exécution. Voir Catalogue d’outils.

Chaque outil déclare l’un des quatre niveaux de risque de l’énumération RiskLevel (src/Config/RiskLevel.php) : Safe (0), Caution (1), Review (2), et ApprovalRequired (3). La journalisation d’audit s’applique à partir de Caution inclus. Un remplacement de configuration peut élever le risque d’un outil ; il ne peut jamais abaisser un outil qui est ApprovalRequired par conception. Le chargeur de configuration lève une exception au chargement, et le serveur refuse de démarrer plutôt que de tourner avec une barrière affaiblie.

Quand un outil ApprovalRequired est invoqué sans jeton valide, la ConfirmationGate (src/Mcp/ConfirmationGate.php) renvoie un jeton de défi à usage unique. Le jeton lie le nom de l’outil, un nonce aléatoire et une durée de vie (TTL) de 300 secondes, mais pas les arguments, car les clients peuvent re-sérialiser les arguments avec un ordre de clés différent lors d’une nouvelle tentative. L’agent relaie le défi à un humain et réinvoque le même outil avec le jeton dans l’argument _confirmation_token. Le jeton est consommé à l’usage, ce qui autorise exactement un appel contrôlé.

L’exemple PHP suivant est un assistant indépendant du transport qui pilote un appel d’outil MCP et, lors d’un défi de confirmation, transmet le défi à un approbateur humain avant de réessayer avec le jeton émis. Il déclare des types stricts, est entièrement typé et intercepte l’exception la plus spécifique plutôt que d’avaler toutes les erreurs.

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);
}
}

Raccorde McpClientInterface, HumanApproverInterface et ApprovalDeniedException à ton propre transport et à ton propre canal d’approbation. La nouvelle tentative réutilise les arguments d’origine plus le jeton émis ; n’approuve jamais automatiquement un défi sans une décision humaine. Voir Niveaux de risque HITL.

Le serveur s’étend en ajoutant des outils et des fournisseurs, pas en modifiant le registre.

Point d’extensionÀ utiliser pourContrainte
Une classe implémentant ToolInterfaceUne nouvelle capacité du moteur exposée sous forme d’outil.Déclare tier(), riskLevel(), category() et un inputSchema() JSON Schema ; garde-le comme une fine enveloppe autour du moteur.
Un fournisseur ToolProviderInterfaceEnregistrer un jeu d’outils pour un niveau.Les fournisseurs Pro et Enterprise sont découverts par class_exists() ; ne rends pas le package propriétaire obligatoire dans le serveur.
enabled_tools, liste d’autorisationRestreindre le catalogue exposé au moindre privilège.La liste d’autorisation ne fait que soustraire ; elle ne peut pas enregistrer un outil absent.
risk_level_overridesDurcir un déploiement en élevant le risque d’un outil.En montée uniquement ; un abaissement d’un outil ApprovalRequired fait échouer le démarrage.
Points d’injection de transport et de worker injectablesTester le serveur de façon isolée.Ces points existent pour les tests, pas pour le câblage applicatif.
  1. Choisis un profil. Exécute .rr.yaml, .rr.grpc.yaml, ou .rr.full.yaml pour les transports que tu exposes.
  2. Monte les clés depuis un secret. Pointe NEXTPDF_API_KEYS_FILE vers un fichier de secret ; préfère le magasin de clés basé sur un fichier rechargé à chaud pour que la rotation n’exige aucun redémarrage.
  3. Configure les magasins partagés. Définis NEXTPDF_REDIS_HOST et vérifie la présence de ext-redis pour tout pool de plus d’un worker ; place le magasin de jobs SQLite sur un volume accessible en écriture par tous les workers.
  4. Termine le TLS. Exécute REST derrière un terminateur Transport Layer Security (TLS) ; exécute gRPC avec TLS mutuel sur tout réseau non fiable, avec la clé du serveur, le certificat du serveur et l’autorité de certification client fournis comme secrets de déploiement.
  5. Sonde la santé. Utilise les endpoints anonymes /healthz et /readyz (REST) ou les RPC HealthCheck et ReadinessCheck (gRPC) pour les sondes de l’orchestrateur.
  6. Restreins le catalogue. Restreins enabled_tools au jeu minimal dont une intégration a besoin.

Vérifie la santé de Redis plutôt que de la présumer : le serveur REST se rabat sur les magasins en mémoire quand une connexion Redis configurée échoue. Voir Déploiement et Sécurité et exploitation.

DéfaillanceOù elle apparaîtRéponse recommandée
Identifiant inconnu document_idExécution d’outilRetourner une erreur définie à l’appelant ; lui demander d’appeler d’abord create_pdf.
ETag obsolète sur une mutationOutil de mutation ASTRelire le document avec get_document_ast et réessayer avec le nouvel ETag.
Clé d’API manquante ou invalide (REST)Middleware d’authentificationRetourner 401 avec un défi WWW-Authenticate: Bearer ; ne pas divulguer quelle partie était erronée.
Niveau non habilité (REST)AutorisationRetourner 403 ; le niveau de la clé est inférieur à celui de l’opération.
Route de niveau absente (REST)RouteurRetourner 404 ; le package n’est pas installé. C’est un cas attendu, pas une défaillance.
Jeton incorrect (gRPC)Authentificateur gRPCFaire échouer l’appel avec UNAUTHENTICATED.
Redis injoignableDémarrage ou exécutionDégrader vers les magasins en mémoire ; alerter les opérateurs et vérifier la santé de Redis.
Chemin de sortie hors du répertoire de baseOutil de sortie vers fichierÉchouer en mode sûr ; le chemin est canonicalisé et la traversée est rejetée.

Faire remonter les défaillances du moteur sous forme d’objets d’erreur définis, jamais comme des succès silencieux. Le modèle d’erreur de chaque transport est détaillé dans la référence de l’API.

PréoccupationPar défautQuand le remplacer
parse_pdfDésactivé (activation explicite via NEXTPDF_MCP_TOOL_PARSE_PDF_ENABLED).À activer seulement quand une intégration a besoin d’une inspection structurelle.
enabled_toolsVide (tous les outils découverts sont autorisés).Définis une liste d’autorisation explicite pour les déploiements au moindre privilège.
Remplacements de risqueAucun.Élève le risque pour un déploiement durci ; ne tente jamais un abaissement.
document_ttl / max_documents1800 secondes / 50 documents.Abaisse-les pour les déploiements sensibles à la résidence des données ou à mémoire contrainte.
allow_file_outputActivé.Définis-le à false pour les déploiements sans état, sensibles à la résidence des données.
Nombre de workersQuatre (HTTP), deux (gRPC).Dimensionne-le selon la latence observée et les cœurs disponibles.
Écouteur RESTHTTP en clair derrière un terminateur TLS.Termine toujours le TLS en amont ; n’expose jamais de trafic en clair sur un réseau non fiable.
gRPC sur des réseaux non fiablesTLS mutuel.Requis ; n’exécute jamais d’écouteur gRPC en clair sur un réseau non fiable.
  • Les tests du registre vérifient qu’un niveau Pro ou Enterprise absent est ignoré silencieusement et que le catalogue Core s’enregistre quand même.
  • Les tests de la liste d’autorisation vérifient que enabled_tools soustrait et n’ajoute jamais un outil que le registre n’a pas découvert.
  • Les tests de la barrière de confirmation vérifient qu’un outil ApprovalRequired renvoie un défi au premier appel, ne s’exécute qu’une seule fois avec un jeton valide à usage unique, et que le jeton expire après son TTL.
  • Les tests d’abaissement vérifient qu’une entrée risk_level_overrides affaiblissant un outil ApprovalRequired fait échouer le démarrage.
  • Les tests d’authentification couvrent les clés manquantes, malformées, désactivées et expirées sur REST (401 avec WWW-Authenticate) et gRPC (UNAUTHENTICATED), ainsi que le rejet pour niveau insuffisant (403).
  • Les tests de concurrence vérifient qu’un ETag obsolète fait échouer une mutation et qu’une idempotency_key répétée rejoue le résultat mis en cache.
  • Les tests de confinement de chemin vérifient qu’un chemin de sortie vers fichier dont la résolution sort du répertoire de base est rejeté.
  • Garde des fixtures petites et non sensibles ; ne commite jamais une vraie clé d’API ni du contenu de document.