Tarde o temprano alguien suelta la foto de un pasaporte en un chat con un asistente y le pide que “rellene el formulario y ya”. Un modelo de visión de uso general lo intentará. Puede que incluso acierte con el nombre. Lo que no hará es decirte si los dígitos de control de la zona de lectura mecánica (MRZ) son correctos, devolver las fechas siempre en el mismo formato ni decir “no he podido leer esto” en lugar de adivinar.
Ese hueco encaja bien con una herramienta. El Model Context Protocol permite que un agente llame a una, y este artículo muestra cómo dar a Claude Desktop, Claude Code, Cursor y otros clientes una herramienta de reconocimiento de documentos, qué recibe el agente, cómo mantener acotado el coste y qué pensar antes de poner a un agente a trabajar con documentos de identidad.
Este es el blog de doc.cheap, la API que hay detrás del servidor MCP que se usa aquí. El servidor tiene licencia MIT, y las cuestiones de configuración valen para cualquier herramienta de este tipo.
Qué recibe el agente
El servidor es @doc-cheap/mcp en npm (MIT, Node 20 o posterior) y expone tres herramientas:
| Herramienta | Qué hace | ¿Gasta crédito? |
|---|---|---|
scan_document |
Reconoce un pasaporte, documento nacional de identidad o permiso de conducir a partir de una foto o un escaneo y devuelve el resultado estructurado | Sí, solo cuando se reconoce un documento |
check_balance |
Lee los créditos restantes y los contadores de este mes | No (solo lectura) |
search_docs |
Busca sin conexión en la documentación de la API incluida con el servidor | No (solo lectura) |
Cada herramienta lleva un título, una descripción (las dos que tocan el estado de la cuenta indican el precio) y las indicaciones de comportamiento de MCP que un cliente lee antes de decidir si preguntarte primero: scan_document está marcada como no de solo lectura, y las otras dos como de solo lectura. El servidor también envía instrucciones que el modelo lee antes de cualquier llamada, con lo que reconoce y lo que cuesta una llamada. Además de las herramientas hay cuatro prompts (scan_document_to_json, check_document_expiry, batch_scan, explain_error), y cada página de la documentación se expone como recurso de solo lectura, por ejemplo doccheap://docs/reference/fields.
scan_document responde con el resultado completo como JSON estructurado más un resumen de una línea, por ejemplo:
Scan 01a0af18-cd8d-7a61-9f2d-4c7b8e105da3: recognized · passport (GRC) · PARADEIGMA ELENI SOFIA · billed · 684 ms
(Un titular de espécimen inventado, tomado de la documentación). El JSON que hay detrás contiene el titular, el número de documento y las fechas en ISO 8601, cada campo con una banda de confianza, las líneas de la MRZ con un veredicto passed / failed / absent y un indicador billed. Esa estructura es lo importante: el agente no tiene que interpretar píxeles, lee campos.
Instalación: el servidor local
Todos los clientes de abajo lanzan el servidor con npx. Sin clave configurada, usa la clave pública de sandbox, que da 10 documentos reconocidos gratis por dirección IP en total y como máximo 10 peticiones por hora. Basta para probarlo. Al registrarte recibes 20 créditos gratis; después, un documento reconocido cuesta $0.01.
Claude Desktop, Cursor y Windsurf leen el mismo bloque, en claude_desktop_config.json, ~/.cursor/mcp.json y ~/.codeium/windsurf/mcp_config.json respectivamente:
{
"mcpServers": {
"doc-cheap": {
"command": "npx",
"args": ["-y", "@doc-cheap/mcp"],
"env": { "DOC_CHEAP_API_KEY": "sk_live_your_key" }
}
}
}
Quita la línea env para usar la clave de 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 y Kiro usan el mismo bloque mcpServers en sus propios archivos de configuración; la guía de MCP de la documentación (en inglés) indica la ruta de cada uno. Reinicia el cliente después de editar: el servidor solo recoge un entorno modificado al arrancar de nuevo.
Instalación: el servidor alojado
Si tu cliente se conecta a una URL en lugar de lanzar un comando, las mismas tres herramientas están alojadas en https://mcp.doc.cheap/mcp sobre Streamable HTTP, sin inicio de sesión. La clave va en una cabecera, X-Doc-Cheap-Api-Key o Authorization: Bearer (si se envían las dos, gana la cabecera con nombre propio); sin clave se usa la de sandbox.
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" }
}
}
}
En Claude Desktop y claude.ai, añádelo en Settings → Connectors como conector personalizado con esa URL.
El servidor alojado no ve los archivos de tu máquina, así que allí scan_document recibe la imagen como image_base64 o como una image_url pública.
Los archivos locales y las URL están acotados a propósito
El argumento de una herramienta lo elige un modelo, y a un modelo se le puede convencer de cosas. Por eso el servidor local no lee rutas arbitrarias:
image_pathestá desactivado hasta que configurasDOC_CHEAP_IMAGE_ROOTcon un directorio. Las rutas se resuelven primero siguiendo los enlaces simbólicos, y se rechaza..o un enlace que apunte fuera del directorio. Un archivo inexistente y uno fuera de los límites reciben el mismo mensaje, así que la herramienta no sirve para averiguar si un archivo existe.image_urldebe serhttps:, debe resolverse solo a direcciones públicas (se rechazan loopback, privadas, link-local y rangos similares), sigue como máximo tres redirecciones volviendo a comprobar cada salto, y tiene un límite de 25 MB.
Si alguna vez has conectado una herramienta de lectura de archivos a un agente, compárala con esta lista. “El modelo solo pasará rutas razonables” no es una frontera de seguridad.
Mantener acotado el coste
Dos propiedades hacen predecible el uso con agentes:
- Solo se facturan los documentos reconocidos. Una llamada se cobra cuando se determinó el tipo de documento y realmente se extrajeron datos: una MRZ cuyos dígitos de control son correctos, al menos cinco campos impresos o un código de barras decodificado correctamente. Si no se encuentra ningún documento, la imagen es ilegible, el tipo no está soportado, hay un error interno o se agota el tiempo de espera, no cuesta nada. El indicador
billeddel resultado dice qué ocurrió, siempre. Con la clave de sandbox no se cobra nada, y el indicador dice entonces si el mismo escaneo se habría facturado con una clave de producción (live). - Los reintentos pueden ser gratis.
scan_documentacepta unidempotency_key; con una clave live, una repetición con la misma clave devuelve el primer resultado guardado en lugar de cobrar otra vez. Un reintento sin clave es un segundo escaneo. Una repetición devuelve el resultado guardado, así que entreretain_hours: 0y los reintentos repetibles hay que elegir.
En la práctica:
- Haz que el agente llame a
check_balanceantes de un lote. Las propias instrucciones del servidor le piden al modelo que lo haga, y el promptbatch_scanlo hace lo primero. Con la clave de sandbox el saldo esnull, y la herramienta dice que no hay saldo en lugar de mostrar ceros. - Aprueba automáticamente solo las herramientas de solo lectura. La configuración de Kiro, por ejemplo, admite
"autoApprove": ["check_balance", "search_docs"]. Dejascan_documentdetrás de una confirmación, ya que es la que gasta. - Deja que el agente consulte la documentación.
search_docsfunciona sin conexión sobre la documentación incluida, así que preguntar “qué significaunsupported_document” no cuesta nada y no depende de la memoria del modelo.
Privacidad: preguntas que hacerte antes de empezar
Los documentos de identidad están entre los datos más sensibles que existen, y un agente añade partes al flujo. Esto es lo que se cumple del lado de la API y lo que depende de tu configuración.
Del lado de la API (según la documentación):
- La imagen subida se mantiene en memoria durante la petición y nunca se escribe en almacenamiento persistente.
- El resultado del reconocimiento se conserva para poder leerlo después, durante un periodo que se configura en la cuenta (24 horas, 7 días, 30 días o un año). El valor por defecto de una cuenta nueva es un año. Por llamada,
retain_hours: 0no escribe ninguna fila, yscan_documenttambién aceptaretain_hours. Si el agente solo necesita la respuesta una vez, configúralo. - El procesamiento se realiza en la Unión Europea. Los datos no se usan para entrenar modelos.
return_portrait: falseomiteimages.main_photo, el recorte de la foto del titular. El recorte de la página completa sigue incluyéndose, igual que la tenue segunda copia del rostro que algunos documentos imprimen en la página.
De tu lado:
- El resultado entra en el contexto del modelo. Lo que devuelve
scan_document(nombres, números, fechas) pasa a estar en la conversación, y lo procesa el proveedor de LLM que ejecute tu cliente, con las condiciones de ese proveedor. Es inherente a cualquier herramienta MCP, no algo propio de esta. - Importa cómo viaja la imagen. Con
image_pathen el servidor local, el servidor lee el archivo y lo envía directamente a la API. Conimage_base64, los bytes de la imagen van en los argumentos de la llamada a la herramienta que hace el modelo. Si quieres que los píxeles se queden fuera del contexto del modelo, usa un directorio local acotado. - Esto es reconocimiento, no verificación. El grupo
authenticitydel resultado indicanot_checked. Una MRZ correcta significa que la zona se leyó y es coherente consigo misma, no que el documento sea auténtico. No hay prueba de vida ni comparación facial. Si tu caso de uso es KYC, esto es un dato de entrada, no la decisión. - Usa documentos sintéticos mientras desarrollas. Los especímenes y las MRZ generadas bastan para conectarlo todo.
Una sesión breve
Con el servidor instalado, basta un prompt como “Lee este escaneo de pasaporte y dime si caduca en los próximos seis meses” con un espécimen sintético adjunto. Un agente que se comporta bien llama a scan_document, lee document.expiry_date y document.days_remaining del resultado y responde a partir de esos campos, no de su impresión de la imagen. Si el escaneo vuelve como unreadable, debería decirlo y pedir una foto mejor, y no se te habrá cobrado por él.
Ese último comportamiento es la verdadera razón para usar aquí una herramienta: el agente recibe un “no se ha podido leer” explícito en lugar de la tentación de rellenar un hueco.
Enlaces
- Página de MCP con el fragmento de cada cliente: https://doc.cheap/mcp
- Guía completa: https://doc.cheap/docs/guides/use-the-mcp-server
- Código fuente (MIT): https://gitlab.com/doccheap/ocr-mcp
- npm: https://www.npmjs.com/package/@doc-cheap/mcp
Si construyes algo con él, o encuentras un cliente en el que la configuración de arriba no funciona, escribe a admin@doc.cheap.
Cada afirmación sobre doc.cheap y su servidor MCP se comprobó contra su código.