Piloter une session documentaire d’agent via MCP
En un coup d’œil
Section intitulée « En un coup d’œil »Voici une session d’agent complète sur le serveur Model Context
Protocol (MCP) de NextPDF Connect, message par message : initialize,
tools/list, six invocations de tools/call qui construisent une note
de projet d’une page, et l’aller-retour avec supervision humaine (HITL)
qui conditionne l’écriture finale du fichier. Chaque message JSON-RPC
ci-dessous a été capturé mot pour mot depuis un processus
bin/nextpdf-mcp en fonctionnement (outils de niveau Core uniquement),
puis expurgé de deux manières précises : le jeton de confirmation à
usage unique est affiché sous la forme confirm_<single-use-hex>, et le
répertoire temporaire système de la machine est raccourci en C:\Temp.
Les identifiants, schémas, positions et nombres d’octets correspondent exactement à
ce que le serveur a envoyé.
Installation
Section intitulée « Installation »composer require nextpdf/serverRelie le transport stdio à ton hôte MCP — pour Claude Desktop (les hôtes lancent la commande depuis leur propre répertoire, il faut donc utiliser un chemin absolu ; le transport stdio ne nécessite aucune clé d’API, contrairement au transport REST) :
{ "mcpServers": { "nextpdf": { "command": "php", "args": ["/absolute/path/to/your/project/vendor/bin/nextpdf-mcp"] } }}Le serveur parle le JSON-RPC 2.0 délimité par des sauts de ligne sur stdin/stdout et sépare strictement la sortie du protocole des diagnostics : les lignes de démarrage et d’audit vont vers stderr, jamais vers stdout.
Vue d’ensemble conceptuelle
Section intitulée « Vue d’ensemble conceptuelle »Une session de document MCP conserve un état. create_pdf ouvre un document
dans le magasin en mémoire du serveur et renvoie un document_id ;
chaque appel ultérieur cible cet identifiant. Les outils de contenu
(set_font, add_text, add_table) s’exécutent immédiatement au niveau
de risque Prudence avec journalisation d’audit ; preview_layout est
une lecture de niveau Sûr ; et output_pdf avec un file_path relève
d’Approbation requise — il ne s’exécute pas au premier appel. À la place,
le serveur renvoie un défi assorti d’un jeton à usage unique, l’agent
transmet le défi à l’humain, et seul un rappel portant
_confirmation_token exécute l’écriture. Les documents laissés dans le
magasin expirent après la durée de vie configurée (30 minutes par
défaut).
Les mêmes appels d’outils pilotent le moteur d’outils via REST et gRPC — les transports partagent un seul exécuteur — de sorte que tout ce qui figure ici, hormis le tramage stdio, reste valable. Voir Générer une facture de bout en bout via REST pour le même moteur sur la surface HTTP.
Surface d’API
Section intitulée « Surface d’API »| Outil | Rôle dans cette session | Niveau de risque |
|---|---|---|
create_pdf | Ouvrir le document, obtenir le document_id | Prudence |
set_font | Choisir la police du titre, puis celle du corps | Prudence |
add_text | Ligne de titre, puis paragraphe d’introduction | Prudence |
add_table | Tableau de la liste de contrôle (responsable/échéance) | Prudence |
preview_layout | Lire l’état de la mise en page avant la sortie | Sûr |
output_pdf (mode fichier) | Écrire le PDF — soumis à la barrière | Approbation requise |
Le déploiement capturé ici a enregistré 20 outils (13 Core, 6 Pro, 1
Enterprise — ces nombres apparaissent dans la réponse initialize
ci-dessous) ; cette session utilise uniquement des outils Core, elle
fonctionne donc sans modification sur une installation purement open
source. Le catalogue faisant foi est la réponse tools/list de ton
propre serveur, et l’échelle de risque est définie dans la
référence des niveaux de risque HITL.
La session, message par message
Section intitulée « La session, message par message »1. Initialiser la connexion
Section intitulée « 1. Initialiser la connexion »Le client ouvre la session et indique sa version de protocole :
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": { "name": "planning-agent", "version": "1.0.0" } }}Le serveur confirme la version de protocole et déclare ses capacités, y compris le nombre d’outils par niveau et l’activation de la barrière 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" } }}Le client accuse réception par une notification (les notifications ne
portent pas d’id et ne reçoivent aucune réponse) :
{ "jsonrpc": "2.0", "method": "notifications/initialized"}2. Découvrir les outils
Section intitulée « 2. Découvrir les outils »{ "jsonrpc": "2.0", "id": 2, "method": "tools/list"}La réponse complète liste les 20 outils enregistrés avec leurs schémas d’entrée intégraux. Elle est présentée ici réduite aux deux outils qui ouvrent et ferment cette session — les 18 entrées omises ont la même forme :
{ "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 } } ] }}Remarque le schéma d’output_pdf : file_path est facultatif, et les
annotations portent openWorldHint: true — l’outil peut agir sur le
monde extérieur à la session, ce qui explique précisément pourquoi le
mode fichier est soumis à la barrière.
3. Ouvrir le document
Section intitulée « 3. Ouvrir le document »{ "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" } }}Chaque résultat d’outil arrive en double dans un même message : un bloc
de texte content lisible par un humain, et un structuredContent
lisible par une machine. Lis structuredContent.document_id et
réutilise-le dans chaque appel suivant.
4. Ajouter le titre
Section intitulée « 4. Ajouter le titre »Définis une police grasse de 16 points, puis place le titre :
{ "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. Ajouter le paragraphe de corps
Section intitulée « 5. Ajouter le paragraphe de corps »Retour à une police normale de 11 points pour le texte
d’introduction ; width: 0 sélectionne une disposition
multicellulaire pleine largeur :
{ "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. Ajouter le tableau de la liste de contrôle
Section intitulée « 6. Ajouter le tableau de la liste de contrôle »{ "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 } } }}Chaque appel de contenu renvoie la position actualisée du curseur, de
sorte que l’agent sait toujours où atterrira l’élément suivant.
7. Prévisualiser avant de demander l’approbation
Section intitulée « 7. Prévisualiser avant de demander l’approbation »preview_layout est un appel Sûr, en lecture seule — un agent consciencieux vérifie ce qu’il a construit avant de demander à un humain
d’approuver une écriture :
{ "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. Demander l’écriture du fichier — la barrière répond en premier
Section intitulée « 8. Demander l’écriture du fichier — la barrière répond en premier »L’agent demande à output_pdf d’écrire la note finalisée sur le disque,
en gardant le document actif (destroy: false) au cas où l’humain
refuserait et où il faudrait se rabattre sur une sortie en 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 } }}Le fichier n’est pas écrit. Comme le mode fichier relève d’Approbation requise, le serveur répond plutôt par un défi de confirmation :
{ "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. L’humain approuve — rappel avec le jeton
Section intitulée « 9. L’humain approuve — rappel avec le jeton »L’agent transmet le texte du défi à l’humain. Après approbation, il
rappelle output_pdf avec les mêmes arguments, plus
_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>" } }}Le jeton est consommé, l’écriture s’exécute, et le résultat rend compte du fichier écrit :
{ "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 } }}Vérifier le fichier écrit
Section intitulée « Vérifier le fichier écrit »La session a écrit kickoff-brief.pdf (3 612 octets, une page,
correspondant à structuredContent.file_size et page_count). Sortie
capturée de qpdf --check pour ce fichier précis :
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 detectIl s’agit d’une vérification structurelle, selon les propres termes de qpdf — pas d’un verdict de conformité.
Cas limites & pièges
Section intitulée « Cas limites & pièges »- Le rappel doit répéter les mêmes arguments. Le jeton de
confirmation est lié au nom de l’outil ainsi qu’à un condensé canonique
des arguments pour lesquels il a été émis. Un rappel avec le moindre
changement — même en inversant
destroy— ne consomme pas le jeton ; le serveur répond plutôt par un nouveau défi. Répète exactement les arguments et ajoute uniquement_confirmation_token. - Le jeton est à usage unique et expire. Le défi indique le délai d’expiration (300 secondes). Après expiration ou consommation, le prochain appel soumis à la barrière reçoit un nouveau défi ; transmets ce dernier.
- La sortie de fichier atterrit dans un répertoire de la liste
d’autorisation. Le serveur rejette tout
file_pathsitué hors de son répertoire temporaire configuré, en renvoyantOutput path rejected by security policy. La racine par défaut de la liste d’autorisation estnextpdf-mcpsous le répertoire temporaire système ; les opérateurs la modifient via le paramètretemp_dirdansnextpdf-mcp.yaml. - Le mode base64 n’est pas soumis à la barrière.
output_pdfsansfile_pathrenvoie le PDF en base64 au niveau Revue, sans effet de bord sur le système de fichiers — voir Exiger une approbation humaine pour la sortie de fichiers pour cette frontière en détail. - Un défi est un résultat, pas une erreur. Le message de défi arrive
avec
isError: false; une approbation en attente est une pause du flux de travail. Ne réessaie pas en boucle, et ne fabrique jamais de jeton. - Les notifications ne reçoivent aucune réponse. Après
notifications/initialized, ne bloque pas en attendant une ligne de réponse.
Performances
Section intitulée « Performances »La session est intégralement en mémoire : les appels de contenu ont répondu en quelques millisecondes lors de l’exécution capturée, et la durée totale est dominée par l’aller-retour d’approbation humaine, ce qui est tout l’intérêt de la barrière. Le magasin de documents conserve une session pendant 30 minutes d’inactivité par défaut (50 documents au maximum), de sorte qu’une approbation lente ne perd pas le document construit — mais un document abandonné est récupéré.
Notes de sécurité
Section intitulée « Notes de sécurité »- Traite le jeton de confirmation comme un secret à usage unique. Transmets le texte du défi à l’humain ; ne journalise pas le jeton et ne le conserve pas. Cette page expurge le jeton capturé précisément pour cette raison.
- La piste d’audit est sur stderr. Chaque exécution de niveau Prudence ou supérieur est journalisée pour l’audit (outil, risque, arguments, résultat) via PSR-3, avec les paramètres sensibles expurgés. Les diagnostics ne se mêlent jamais au flux du protocole.
- La liste d’autorisation de chemins est la frontière du système de
fichiers. Fais pointer
temp_dirvers un répertoire dédié à la sortie de Connect ; ne l’élargis pas à un emplacement à usage général. - Les niveaux de risque ne peuvent qu’augmenter. Une
substitution d’opérateur dans
nextpdf-mcp.yamlpeut relever le niveau de risque d’un outil mais ne peut jamais abaisseroutput_pdfen dessous d’Approbation requise.
Conformité
Section intitulée « Conformité »Cette recette ne formule aucune revendication normative de conformité à
une norme. Elle documente le transport stdio de MCP (JSON-RPC 2.0,
version de protocole 2025-06-18 telle que négociée dans l’échange
initialize capturé) ainsi que le contrat de risque et de confirmation
du serveur. L’étape qpdf --check ci-dessus confirme uniquement
l’intégrité structurelle du fichier écrit ; la conformité à une norme
est déterminée par un validateur indépendant, et non affirmée par le
logiciel producteur.
Voir aussi
Section intitulée « Voir aussi »- Exiger une approbation humaine pour la sortie de fichiers — la barrière de confirmation en détail, y compris le chemin de refus.
- Générer une facture de bout en bout via REST — le même moteur d’outils via HTTP, avec la transcription réseau capturée.
- Générer ton premier PDF — la plus petite session Connect.
- Conventions des recettes Connect — le contrat que suit chaque recette Connect.
- Niveaux de risque HITL — l’échelle de risque canonique et la résolution de politique.
- Catalogue d’outils — le catalogue d’outils faisant foi.