Tôt ou tard, quelqu'un dépose la photo d'un passeport dans une conversation avec un assistant et lui demande de « remplir le formulaire, tout simplement ». Un modèle de vision généraliste s'y essaiera. Il trouvera peut-être même le bon nom. Ce qu'il ne fera pas, c'est vous dire si les chiffres de contrôle de la zone de lecture automatique (MRZ) sont valides, renvoyer les dates toujours dans le même format, ou dire « je n'ai pas pu lire ceci » au lieu de deviner.
C'est exactement le genre de manque qu'un outil comble bien. Le Model Context Protocol permet à un agent d'en appeler un, et cet article montre comment donner à Claude Desktop, Claude Code, Cursor et d'autres clients un outil de reconnaissance de documents, ce que l'agent reçoit en retour, comment garder le coût sous contrôle, et à quoi réfléchir avant même de confier des documents d'identité à un agent.
Ceci est le blog de doc.cheap, l'API derrière le serveur MCP utilisé ici. Le serveur est sous licence MIT, et les questions de configuration valent pour tout outil de ce type.
Ce que l'agent reçoit
Le serveur est @doc-cheap/mcp sur npm (MIT, Node 20 ou plus récent), et il expose trois outils :
| Outil | Ce qu'il fait | Consomme des crédits ? |
|---|---|---|
scan_document |
Reconnaît un passeport, une carte d'identité nationale ou un permis de conduire à partir d'une photo ou d'un scan et renvoie le résultat structuré | Oui, uniquement quand un document est reconnu |
check_balance |
Lit les crédits restants et les compteurs du mois en cours | Non (lecture seule) |
search_docs |
Recherche dans la documentation de l'API fournie avec le serveur, hors ligne | Non (lecture seule) |
Chaque outil porte un titre, une description (les deux qui touchent à l'état du compte indiquent le prix) et les indications de comportement MCP qu'un client lit avant de décider s'il doit d'abord vous demander confirmation : scan_document est marqué comme n'étant pas en lecture seule, les deux autres en lecture seule. Le serveur envoie aussi des instructions que le modèle lit avant tout appel, indiquant ce qu'il reconnaît et ce que coûte un appel. En plus des outils, il y a quatre prompts (scan_document_to_json, check_document_expiry, batch_scan, explain_error), et chaque page de la documentation est exposée comme ressource en lecture seule, par exemple doccheap://docs/reference/fields.
scan_document répond avec le résultat complet en JSON structuré, accompagné d'un résumé d'une ligne, par exemple :
Scan 01a0af18-cd8d-7a61-9f2d-4c7b8e105da3: recognized · passport (GRC) · PARADEIGMA ELENI SOFIA · billed · 684 ms
(Un titulaire de spécimen inventé, tiré de la documentation.) Le JSON sous-jacent contient le titulaire, le numéro du document et les dates en ISO 8601, chaque champ avec une tranche de confiance, les lignes de la MRZ avec un verdict passed / failed / absent, et un indicateur billed. Tout l'intérêt est là : l'agent n'a pas à interpréter des pixels, il lit des champs.
Installation : le serveur local
Tous les clients ci-dessous lancent le serveur avec npx. Sans clé définie, il utilise la clé sandbox publique, qui donne 10 documents reconnus gratuits par adresse IP au total et au plus 10 requêtes par heure. C'est suffisant pour l'essayer. L'inscription donne 20 crédits gratuits ; ensuite, un document reconnu coûte $0.01.
Claude Desktop, Cursor et Windsurf lisent le même bloc, respectivement dans claude_desktop_config.json, ~/.cursor/mcp.json et ~/.codeium/windsurf/mcp_config.json :
{
"mcpServers": {
"doc-cheap": {
"command": "npx",
"args": ["-y", "@doc-cheap/mcp"],
"env": { "DOC_CHEAP_API_KEY": "sk_live_your_key" }
}
}
}
Retirez la ligne env pour fonctionner avec la clé sandbox.
Claude Code :
claude mcp add-json doc-cheap '{"command":"npx","args":["-y","@doc-cheap/mcp"],"env":{"DOC_CHEAP_API_KEY":"sk_live_your_key"}}'
VS Code :
code --add-mcp '{"name":"doc-cheap","command":"npx","args":["-y","@doc-cheap/mcp"]}'
Gemini CLI et Kiro utilisent le même bloc mcpServers dans leurs propres fichiers de configuration ; le guide MCP (en anglais) donne chaque chemin. Redémarrez le client après modification : le serveur ne prend en compte un environnement modifié qu'à un nouveau lancement.
Installation : le serveur hébergé
Si votre client se connecte à une URL au lieu de lancer une commande, les trois mêmes outils sont hébergés à https://mcp.doc.cheap/mcp en Streamable HTTP, sans connexion. La clé se passe dans un en-tête, X-Doc-Cheap-Api-Key ou Authorization: Bearer (l'en-tête nommé l'emporte si les deux sont envoyés) ; sans clé, c'est la clé sandbox qui est utilisée.
Claude Code :
claude mcp add --transport http doc-cheap https://mcp.doc.cheap/mcp --header "Authorization: Bearer sk_live_your_key"
Cursor :
{
"mcpServers": {
"doc-cheap": {
"url": "https://mcp.doc.cheap/mcp",
"headers": { "Authorization": "Bearer sk_live_your_key" }
}
}
}
Dans Claude Desktop et claude.ai, ajoutez-le dans Settings → Connectors comme connecteur personnalisé avec cette URL.
Le serveur hébergé ne voit pas les fichiers de votre machine : là, scan_document reçoit l'image en image_base64 ou via une image_url publique.
Fichiers locaux et URL sont délibérément encadrés
L'argument d'un outil est choisi par un modèle, et un modèle peut se laisser convaincre de bien des choses. Le serveur local ne lit donc pas n'importe quel chemin :
image_pathest désactivé tant que vous n'avez pas définiDOC_CHEAP_IMAGE_ROOTsur un répertoire. Les chemins sont d'abord résolus à travers les liens symboliques, et..ou un lien qui pointe hors du répertoire est refusé. Un fichier absent et un fichier hors limites reçoivent le même message, si bien que l'outil ne peut pas servir à sonder l'existence d'un fichier.image_urldoit être enhttps:, ne doit résoudre que vers des adresses publiques (les plages loopback, privées, link-local et similaires sont refusées), suit au plus trois redirections en revérifiant chaque saut, et est plafonnée à 25 Mo.
Si vous avez déjà branché un outil de lecture de fichiers sur un agent, comparez-le à cette liste. « Le modèle ne passera que des chemins raisonnables » n'est pas une frontière de sécurité.
Garder le coût sous contrôle
Deux propriétés rendent l'usage par un agent prévisible :
- Seuls les documents reconnus sont facturés. Un appel est facturé quand le type de document a été déterminé et que des données ont réellement été extraites : une MRZ dont les chiffres de contrôle sont valides, au moins cinq champs imprimés, ou un code-barres correctement décodé. Aucun document trouvé, une image illisible, un type non pris en charge, une erreur interne ou un délai dépassé ne coûtent rien. L'indicateur
billeddu résultat dit ce qui s'est passé, à chaque fois. Avec la clé sandbox, rien n'est jamais facturé, et l'indicateur dit alors si le même scan aurait été facturé avec une clé live. - Les relances peuvent être gratuites.
scan_documentaccepte uneidempotency_key; avec une clé live, une répétition avec la même clé renvoie le premier résultat stocké au lieu de facturer à nouveau. Une relance sans clé est un second scan. Une répétition renvoie le résultat stocké :retain_hours: 0et relances rejouables, c'est l'un ou l'autre.
En pratique :
- Faites appeler
check_balanceà l'agent avant un lot. Les propres instructions du serveur demandent au modèle de le faire, et le promptbatch_scancommence par là. Avec la clé sandbox, le solde vautnull, et l'outil indique qu'il n'y a pas de solde au lieu d'afficher des zéros. - N'approuvez automatiquement que les outils en lecture seule. La configuration de Kiro, par exemple, accepte
"autoApprove": ["check_balance", "search_docs"]. Laissezscan_documentderrière une demande de confirmation, puisque c'est lui qui dépense. - Laissez l'agent chercher par lui-même.
search_docsfonctionne hors ligne sur la documentation fournie : « que signifieunsupported_document» ne coûte donc rien et ne dépend pas de la mémoire du modèle.
Confidentialité : les questions à se poser avant
Les documents d'identité comptent parmi les données les plus sensibles qui soient, et un agent ajoute des intervenants dans le circuit. Voici ce qui est vrai côté API, et ce qui dépend de votre configuration.
Côté API (tel que documenté) :
- L'image envoyée est gardée en mémoire le temps de la requête et n'est jamais écrite sur un stockage durable.
- Le résultat de la reconnaissance est conservé pour pouvoir être relu plus tard, pendant une durée fixée au niveau du compte (24 heures, 7 jours, 30 jours ou un an). La valeur par défaut d'un nouveau compte est d'un an. Par appel,
retain_hours: 0n'écrit aucune ligne, etscan_documentaccepte lui aussiretain_hours. Si l'agent n'a besoin de la réponse qu'une fois, définissez-le. - Le traitement a lieu dans l'Union européenne. Les données ne servent pas à entraîner des modèles.
return_portrait: falseometimages.main_photo, le recadrage de la photo du titulaire. Le recadrage de la page entière revient toujours, tout comme la seconde copie estompée du visage que certains documents impriment dans la page.
De votre côté :
- Le résultat entre dans le contexte du modèle. Tout ce que renvoie
scan_document(noms, numéros, dates) se retrouve dans la conversation, et est traité par le fournisseur de LLM qui fait tourner votre client, selon les conditions de ce fournisseur. C'est inhérent à tout outil MCP, pas propre à celui-ci. - Le trajet de l'image compte. Avec
image_pathsur le serveur local, le serveur lit le fichier et l'envoie directement à l'API. Avecimage_base64, les octets de l'image figurent dans les arguments de l'appel d'outil du modèle. Si vous voulez que les pixels restent hors du contexte du modèle, utilisez un répertoire local encadré. - C'est de la reconnaissance, pas de la vérification. Le groupe
authenticitydu résultat indiquenot_checked. Une MRZ valide signifie que la zone a été lue et qu'elle est cohérente en elle-même, pas que le document est authentique. Il n'y a ni détection du vivant (liveness) ni comparaison de visages. Si votre cas d'usage est le KYC, c'est une donnée d'entrée, pas la décision. - Utilisez des documents synthétiques pendant le développement. Des spécimens et des MRZ générées suffisent pour tout brancher.
Une courte session
Une fois le serveur installé, un prompt comme « Lis ce scan de passeport et dis-moi s'il expire dans les six prochains mois », avec un spécimen synthétique en pièce jointe, suffit. Un agent bien conçu appelle scan_document, lit document.expiry_date et document.days_remaining dans le résultat, et répond à partir de ces champs plutôt que de son impression de l'image. Si le scan revient unreadable, il doit le dire et demander une meilleure photo, et cela ne vous a rien coûté.
Ce dernier comportement est la vraie raison d'utiliser un outil ici : l'agent reçoit un « lecture impossible » explicite au lieu de la tentation de combler un vide.
Liens
- Page MCP avec les extraits pour chaque client : https://doc.cheap/mcp
- Guide complet : https://doc.cheap/docs/guides/use-the-mcp-server
- Code source (MIT) : https://gitlab.com/doccheap/ocr-mcp
- npm : https://www.npmjs.com/package/@doc-cheap/mcp
Si vous construisez quelque chose avec, ou trouvez un client où la configuration ci-dessus ne fonctionne pas, écrivez à admin@doc.cheap.
Chaque affirmation sur doc.cheap et son serveur MCP a été vérifiée dans leur code.