Conectar un cliente
Conecta cualquier cliente MCP por Streamable HTTP, o mediante el servidor local de npm, y autentícalo con OAuth o API key.
Puede conectarse cualquier cliente MCP que admita servidores remotos por Streamable HTTP: el servidor implementa la revisión 2026-07-28 y también atiende a clientes en 2025-11-25 y 2025-06-18 (consulta protocolo y transporte). Los clientes que solo ejecutan procesos locales usan el servidor local stdio.
Configuración de conexión
El endpoint es https://mcp.factuarea.com (/mcp sigue como alias). Configura el transporte HTTP y OAuth 2.1 o una API key en Authorization: Bearer, más la cabecera X-Company-Id cuando la clave alcanza varios NIF. La guía del protocolo incluye una petición de discovery ejecutable; los recursos y prompts explican la orientación fiscal.
Catálogo y compatibilidad
Los clientes de la revisión actual empiezan por server/discover y los anteriores por initialize; en ambos casos, sigue los cursores de tools/list con la misma credencial. El catálogo completo tiene 686 herramientas; OAuth puede acceder a 559. Ningún saludo inicial crea una sesión que sustituya a la credencial. En Claude Code, el plugin oficial registra el servidor por ti.
Servidor local stdio
Los clientes que solo ejecutan procesos locales —el fichero de configuración de Claude Desktop, los frameworks de agentes, los runners de CI— pueden usar el paquete oficial de npm @factuarea/mcp. Es un pequeño servidor stdio que reenvía cada petición a https://mcp.factuarea.com con una API key, así que las herramientas, los scopes, los guardrails y el modo test se comportan exactamente igual que por HTTP. Necesita Node 20 o superior.
{
"mcpServers": {
"factuarea": {
"command": "npx",
"args": ["-y", "@factuarea/mcp"],
"env": { "FACTUAREA_API_KEY": "fact_test_xxxxxxxxxxxxxxxxxxxxxxxx" }
}
}
}Código fuente, configuración y versiones: github.com/factuarea/factuarea-mcp.
Autenticación
El servidor MCP acepta dos tipos de credencial en el mismo
endpoint https://mcp.factuarea.com, para dos audiencias distintas. Ambas llegan como
Authorization: Bearer <token>; el servidor las distingue por la
forma del token (fact_* → API key, cualquier otra cosa → access token de OAuth).
Política de canal
Esta es la regla más importante de la superficie MCP:
| Canal | Quién | Cómo se eligen los scopes | Tools accesibles |
|---|---|---|---|
| API key | El titular de una cuenta automatizando sus NIF, o un proveedor que opera las cuentas de sus clientes | Eliges los scopes al crear la clave — hasta el super-scope *, que nunca cubre los privilegiados accounts:* | 686 (todo), más las nueve tools de cuentas gestionadas cuando la clave lista accounts:* |
| OAuth 2.1 | Una app de terceros actuando en nombre de un usuario, sobre el único NIF elegido en el consentimiento | El usuario concede los scopes en la pantalla de consentimiento, desde un catálogo curado | 559 |
El modelo refleja el de GitHub: un personal access token (API key) es la credencial de la propia cuenta y puede tener cualquier permiso de ella, mientras que una OAuth app es externa y se limita a un conjunto verificado de scopes que el usuario aprueba explícitamente.
Las 127 tools que una OAuth app nunca puede alcanzar (solo una API key puede) son las operaciones fiscales y de privacidad más sensibles, además de la administración first-party de la cuenta:
- Escrituras de VeriFactu (
verifactu:write) — 11 tools: registrar/reintentar/subsanar registros y eventos de VeriFactu (incluido reintentar de una vez todos los bloqueados), registrar/revocar la representación que permite a Factuarea remitir en tu nombre, subir/activar/revocar certificados FNMT, actualizar ajustes de VeriFactu. Tocan el cumplimiento de la AEAT y son exclusivas del propietario. - Borrado RGPD (
delivery_notes:gdpr_forget) — 1 tool: borrar la PII de auditoría de firma (Art. 17). Privilegiada, solo para administradores. - Pagos, pasarelas y tiendas (
stripe_autoinvoicing:*,payouts:read,integration_events:*,stores:*,woocommerce_store:write,shopify_store:write) — 20 tools: 13 de autofacturación Stripe, cuentas conectadas, payouts y bandeja/replay de eventos de pasarela, más 7 de gestión de tiendas y diagnóstico de conexión. Scopes granulares, solo API key, sin equivalente de consentimiento OAuth. - Emails y registro de peticiones (
emails:read,developers:read) — 7 tools: el historial de correos enviados y los registros de peticiones, de un NIF y de la cuenta. - Cartera de NIF (
companies:*) — 15 tools: listar, consultar, dar de alta, editar, activar, desactivar y archivar los NIF de la cuenta, y el resumen consolidado de cumplimiento de jornada. - Claves y personalización (
account:write) — 5 tools: crear, rotar y revocar las API keys de la cuenta, cambiar los NIF que alcanza una clave y actualizar la personalización de la cuenta. - Titularidad y traspasos de NIF (
account:owner,nif_transfers:*) — Tools para transferir la titularidad de la cuenta y emitir, aceptar, revocar y listar traspasos de NIF. - Equipo (
members:*,invitations:*) — Tools para miembros, sus grants en cada NIF, sus restricciones de módulo y las invitaciones. - Webhooks de cuenta (
webhooks:*) — 10 tools: los webhook endpoints de la cuenta y sus entregas. El scope se puede conceder por consentimiento, pero estas tools se excluyen de OAuth una a una. - Escrituras/transiciones privilegiadas de personal — 33 tools con
time_entries:write,absences:write,absences:transitionowork_schedules:write; las lecturas yemployees:writesiguen accesibles por OAuth. - Borrado de automatizaciones (
automations:delete) — 1 tool: eliminar una regla de automatización, algo que no se puede deshacer desde la API.
Estos grupos suman las 127 tools. Otras diez tools, las de
cuentas gestionadas, quedan fuera de ambos recuentos: necesitan el accounts:read o
accounts:write privilegiados, que el super-scope * no cubre y el consentimiento nunca
concede, así que solo las alcanza una API key de una cuenta que tenga el privilegio
(cuentas gestionadas).
Todo lo demás — las 559 tools de lectura/escritura/transición/envío — está disponible para ambos canales. Consulta Scopes & permisos para ver el catálogo.
Tools de la cuenta
El modelo de cuentas añade tools para administrar la cuenta y sus NIF. Este es el canal que alcanza cada una:
| Qué | Tools | Scope | Canal |
|---|---|---|---|
| Titularidad y traspasos de NIF | transfer_account_ownership, create_nif_transfer, accept_nif_transfer, revoke_nif_transfer, list_nif_transfers (detalle) | account:owner, nif_transfers:* | Solo API key |
| Equipo: miembros, grants, restricciones de módulo e invitaciones | Tools como list_account_members, set_account_member_grant y create_account_invitation (detalle) | members:*, invitations:* | Solo API key |
| Cartera de NIF | 15 tools, como list_companies, create_company y get_consolidated_workforce (detalle) | companies:* | Solo API key |
| Consumo de la cuenta | get_account_usage | account:read | API key y OAuth |
| Los NIF que alcanza una clave | update_api_key_nif_scope | account:write | Solo API key |
| Cuentas gestionadas | 9 tools, como provision_managed_account y end_account_management | accounts:read, accounts:write | Solo API key, y solo con el privilegio |
| Webhooks y registro de peticiones de la cuenta | 10 tools de webhooks (detalle) y 2 de registro | webhooks:*, developers:read | Solo API key, excluidas de OAuth tool a tool |
Las tools de cuenta necesitan una API key, y con una también comprueban quién llama: las de webhooks y registro de la cuenta necesitan detrás de la clave a un titular o administrador de la cuenta, y los scopes privilegiados necesitan el privilegio de la cuenta en cada llamada. Scopes y permisos describe las dos cosas.
Algunas cosas no tienen tool MCP, por diseño:
- La importación masiva de cuentas gestionadas. Sus tres operaciones son privilegiadas y
leen ficheros subidos, que el servidor MCP no transporta. Usa la API REST o la app, o
aprovisiona las cuentas de una en una con
provision_managed_account: importación masiva. - La gestión de un acceso cedido. Ceder, cambiar, revocar, aceptar, rechazar y renunciar al acceso de una cuenta a los NIF de otra son decisiones de una persona de cada cuenta, que la app confirma: ninguna credencial cede ni acepta acceso a los NIF de su cuenta. Acceso cedido.
- El lado del cliente de una cuenta gestionada. Consultar al proveedor, subir o bajar su nivel, terminar la gestión, asumir el pago y reclamar la cuenta lo hace el titular en la app. Cuentas gestionadas.
API keys
Una API key es un Bearer token opaco que pertenece a tu cuenta y alcanza los NIF de su ámbito, creado en el dashboard de desarrolladores en Ajustes → Desarrolladores → API Keys. El formato y las reglas son idénticos a los de la API REST:
fact_live_<24 alphanumeric characters> → production company
fact_test_<24 alphanumeric characters> → isolated sandbox companyEl prefijo es la fuente de verdad del entorno — consulta Modo de prueba. El secreto se muestra solo una vez en la creación; el backend solo almacena un hash con clave (HMAC-SHA256) de él. Pásalo a tu cliente MCP como:
Authorization: Bearer fact_test_xxxxxxxxxxxxxxxxxxxxxxxxPara el ciclo de vida completo de la clave — creación, scopes, rotación con periodo de gracia,
revocación, lista de acceso de IPs, expires_at — consulta la
guía de Autenticación canónica. Las claves se comparten entre las superficies REST
y MCP.
OAuth 2.1
Para apps de terceros, Factuarea es un OAuth 2.1 Authorization Server completo. Es compatible con Dynamic Client Registration, el flujo de authorization-code con PKCE y la rotación de refresh-token. No se requiere pre-registro ni aprobación manual de la app — un cliente se registra a sí mismo y el usuario lo autoriza. (Los clientes interactivos como Claude Code y el MCP Inspector ejecutan todo este flujo por ti; los pasos de abajo son para construir tu propio cliente.)
Descubrimiento
Los clientes descubren las capacidades del servidor a través de endpoints de metadata estándar
(sin prefijo /api):
| Endpoint | RFC | Propósito |
|---|---|---|
/.well-known/oauth-authorization-server | 8414 | Authorization Server Metadata — lista los endpoints authorize/token/register/introspect/revoke, los scopes admitidos, code_challenge_methods_supported: ["S256"]. |
/.well-known/oauth-protected-resource | 9728 | Protected Resource Metadata — declara el recurso MCP y qué authorization server emite tokens válidos. |
Cuando una petición no autenticada alcanza el endpoint, el servidor responde 401
con un header WWW-Authenticate: Bearer ..., resource_metadata="<url>" para que los clientes RFC
9728 encuentren el authorization server sin adivinar.
1. Dynamic Client Registration (RFC 7591)
Un cliente se registra a sí mismo haciendo POST de su metadata; el servidor devuelve un
client_id (y un client_secret para clientes confidenciales):
curl -X POST https://mcp.factuarea.com/api/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "My Invoicing Assistant",
"redirect_uris": ["https://myapp.example.com/callback"],
"token_endpoint_auth_method": "none"
}'Los clientes públicos (apps de navegador/nativas) se registran con
token_endpoint_auth_method: "none" y dependen de PKCE; los clientes confidenciales usan
client_secret_basic. El registro tiene un rate limit de 60 por minuto por IP. El registro dinámico admite solo estos dos métodos; para private_key_jwt consulta clientes identificados por un documento de metadatos.
2. Autorización con PKCE
Envía al usuario al endpoint authorize con un challenge PKCE
(code_challenge_method=S256 es el único método aceptado):
GET https://mcp.factuarea.com/api/oauth/authorize
?response_type=code
&client_id=<client_id>
&redirect_uri=https://myapp.example.com/callback
&scope=factuarea.read invoices.write
&state=<opaque>
&code_challenge=<base64url(sha256(verifier))>
&code_challenge_method=S256Esto renderiza la pantalla de consentimiento, donde el usuario:
- Elige la cuenta y la empresa (NIF) a la que dar acceso. La pantalla lista tus
empresas y las de las cuentas de tus clientes, cada una con la cuenta a la que pertenece y
tu nivel de acceso (Ver u Operar). El token queda vinculado a esa única empresa y
X-Company-Idno puede ampliarlo. - Elige el entorno — Producción (la empresa real,
live) o Pruebas (un sandbox aislado,test), al estilo Stripe. Pruebas es opt-in; si está ausente ⇒ producción. - Revisa y selecciona los scopes a conceder. Los scopes sensibles se marcan y
no vienen pre-seleccionados. Con nivel Ver solo se pueden conceder los de lectura (los
que terminan en
.ready el macrofactuarea.read), y en Pruebas no se conceden los de los módulos que el sandbox no ofrece; consulta qué concede la pantalla según el nivel y el entorno.
La solicitud de consentimiento caduca a los 5 minutos de abrirse y la pantalla dice cuántos minutos quedan. Si caduca, vuelve a lanzar la autorización desde tu app.
Al aprobar, el servidor redirige de vuelta con un code de un solo uso (y tu
state). Las operaciones sensibles se filtran del catálogo que el usuario puede
aprobar — consulta la política de canal.
3. Intercambio de token
Intercambia el code por un access token, enviando el verificador PKCE:
curl -X POST https://mcp.factuarea.com/api/oauth/token \
-d grant_type=authorization_code \
-d code=<code> \
-d redirect_uri=https://myapp.example.com/callback \
-d client_id=<client_id> \
-d code_verifier=<verifier>{
"access_token": "<opaque>",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "<opaque>",
"scope": "profile.read contacts.read invoices.read invoices.write"
}El token persiste los scopes expandidos y de grano fino (las macros como
factuarea.read se expanden en el momento de la emisión). Usa el access token como
credencial Bearer. El endpoint token tiene un rate limit de 60 por minuto por (cliente,
IP) y requiere autenticación de cliente (HTTP Basic para clientes confidenciales,
client_id en el body para los públicos).
4. Rotación de refresh-token
Los refresh tokens rotan por familia: cada refresh emite un nuevo access token y un nuevo refresh token, e invalida el que usaste.
curl -X POST https://mcp.factuarea.com/api/oauth/token \
-d grant_type=refresh_token \
-d refresh_token=<refresh_token> \
-d client_id=<client_id>Si un refresh token se reutiliza (usado tras la rotación — la señal clásica de una fuga), el servidor detecta la reutilización, revoca toda la familia de tokens y lanza una alerta de seguridad. Almacena y usa siempre solo el último refresh token.
Revocación e introspección
| Endpoint | RFC | Propósito |
|---|---|---|
POST /api/oauth/revoke | 7009 | Revoca un access o refresh token. |
POST /api/oauth/introspect | 7662 | Comprueba si un token está activo y lee sus scopes/metadata. |
Ambos requieren autenticación de cliente. Los usuarios también pueden revisar y revocar apps conectadas desde el dashboard de Factuarea, y a un administrador de empresa que pierde el acceso se le revocan sus tokens automáticamente en la siguiente llamada.
Elegir el NIF
Una credencial alcanza un NIF o varios. El ámbito de una API key se fija cuando la emites, y
puede incluir NIF de tu propia cuenta, NIF que otra cuenta cede a la tuya
(acceso cedido) y NIF de las cuentas gestionadas que aprovisionas
(cuentas gestionadas). La API REST nombra el NIF en el segmento
{company} de la ruta
(alcanzar un NIF por el segmento de empresa). Una llamada
JSON-RPC no tiene ruta, así que la sesión lo nombra con una cabecera HTTP:
X-Company-Id: 01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8b11El valor es el id público del NIF, el UUID v7 que acepta el segmento {company} y que
GET /v1/me lista para cada NIF que
alcanza la credencial. Decláralo una vez en la configuración de tu servidor, junto a
Authorization: nunca es un argumento de una tool. Una cabecera presente pero vacía cuenta
como ausente.
| Credencial | X-Company-Id | Qué ocurre |
|---|---|---|
| API key que alcanza un NIF | ausente | La sesión trabaja sobre ese NIF. |
| API key que alcanza varios | ausente | La llamada falla antes de ejecutar ninguna tool, con un error -32008 cuyo data.code es company_selection_required y cuyo data.http_status es 400. data.companies lista los NIF del ámbito con su id y su name (los 20 primeros, y el hint dice cuántos más). |
| API key | nombra un NIF de su ámbito | La sesión trabaja sobre ese NIF. |
| API key | nombra un NIF fuera de su ámbito, uno que no existe o uno del otro entorno | 404 profile_not_found, la misma respuesta para los tres, así que nunca confirma que un NIF exista. |
| Conexión OAuth | ausente, o nombra el NIF elegido en el consentimiento | La sesión trabaja sobre ese NIF. |
| Conexión OAuth | nombra otro NIF | 404 profile_not_found, con una pista para autorizar de nuevo eligiendo ese NIF. |
Un NIF del ámbito que está desactivado o archivado responde
403 company_inactive, con o sin cabecera. Todos son errores
-32008 (consulta Errores y límites de peticiones): data.code lleva el
código y data.http_status el status, y todos los códigos salvo company_selection_required
son los mismos que en la API REST.
Lo que la sesión puede hacer sobre el NIF depende del nivel que tu credencial tiene en él. Con
view, toda tool que escribe en el NIF responde
403 company_read_only, y con operate escribe, como en la API
REST (niveles). El nivel es el menor entre el guardado en la
clave para ese NIF y el vigente de la persona que emitió la clave; una conexión OAuth toma el
nivel de la persona.
Clientes identificados por un documento de metadatos
Algunos clientes, como ChatGPT o Claude, no se registran en /register: su client_id es una URL HTTPS que apunta a un documento de metadatos público (CIMD). Estos clientes pueden autenticarse en /api/oauth/token, /api/oauth/revoke y /api/oauth/introspect con private_key_jwt (RFC 7523): en lugar de un secreto, envían un JWT firmado con una clave publicada en el jwks_uri de su documento.
curl -X POST https://mcp.factuarea.com/api/oauth/token \
-d grant_type=authorization_code \
-d code=<code> \
-d redirect_uri=https://myapp.example.com/callback \
-d client_id=https://myapp.example.com/oauth/client.json \
-d code_verifier=<verifier> \
-d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
-d client_assertion=<JWT firmado>La aserción debe cumplir estas condiciones:
| Requisito | Detalle |
|---|---|
| Algoritmo | RS256 o ES256. |
iss y sub | Ambos iguales al client_id. |
aud | La URL del endpoint de token (o el issuer). |
exp | Como máximo a 300 segundos vista; admitimos 60 segundos de desfase de reloj. |
jti | Único: cada valor se acepta una sola vez. |
Si el kid de la aserción no está en el JWKS en caché, lo recargamos una vez antes de rechazarla. Una petición no puede combinar client_secret con client_assertion: se responde con 400 invalid_request. Si el documento declara un método que no admitimos pero también incluye none en token_endpoint_auth_methods_supported, el cliente se trata como público y PKCE pasa a ser obligatorio.
/.well-known/oauth-authorization-server anuncia private_key_jwt en token_endpoint_auth_methods_supported, revocation_endpoint_auth_methods_supported e introspection_endpoint_auth_methods_supported (["none", "client_secret_basic", "private_key_jwt"]), y token_endpoint_auth_signing_alg_values_supported es ["RS256", "ES256"].
Cuando /api/oauth/authorize no puede aceptar el documento de un cliente, responde 400 con error: "invalid_client" y un error_reason legible:
error_reason | Significado |
|---|---|
client_metadata_unsupported | El documento no es compatible con lo que admite el servidor. |
client_metadata_unavailable | No hemos podido descargar el documento. |
client_metadata_disabled | Los clientes con documento de metadatos están desactivados. |
En un navegador, al usuario se le redirige en su lugar a nuestra propia pantalla, /oauth/authorize?authorize_error=<motivo>&client_id=…, que explica el problema en español, inglés y catalán.
Modo de prueba
El servidor MCP funciona contra los mismos dos entornos que la API REST — live (tu empresa real) y test (un sandbox aislado) — para que puedas construir y validar una integración de agente sin tocar datos de producción, la AEAT ni las bandejas de entrada de tus clientes.
Cómo seleccionas el modo de prueba depende del canal:
| Canal | Cómo usar el modo de prueba |
|---|---|
| API key | Autentícate con una clave fact_test_. El prefijo es la fuente de verdad — un token fact_test_ siempre opera sobre el sandbox. |
| OAuth 2.1 | En la pantalla de consentimiento, elige el entorno Pruebas (test, al estilo Stripe). Si está ausente ⇒ live. El token emitido queda vinculado a ese entorno. |
Una credencial de test opera sobre una empresa sandbox dedicada — un gemelo técnico de tu empresa real, aprovisionado automáticamente y que hereda su plan, para que el gating de módulo/plan se comporte fielmente. El aislamiento es estructural (los datos de test y live viven en empresas separadas), y los efectos externos están desactivados:
| Efecto | En live | En test |
|---|---|---|
| VeriFactu | El registro de Alta se crea y se transmite a la AEAT. | Se crea localmente, pero nunca se transmite a la AEAT. |
| Los emails de documentos llegan a los destinatarios reales. | No se entregan a los destinatarios reales. | |
| Webhooks | Los eventos suscritos se entregan a tus endpoints. | Se registran con livemode: false, pero no se entregan. |
| FACe (FacturaE) | Los envíos se presentan al web service real de FACe. | Simulados — ninguna llamada SOAP sale de Factuarea; el número de registro es sintético (FACE-SANDBOX-*). |
Una sesión de prueba solo alcanza su sandbox. Una clave fact_test_, o una conexión que eligió
el entorno Test, no puede nombrar un NIF real con X-Company-Id, y una live
no puede nombrar un sandbox: las dos responden 404 profile_not_found.
Todo lo demás se comporta exactamente igual que en producción, y el conjunto completo de 686 tools
está disponible en ambos entornos (sujeto a tus scopes y plan), con una excepción: las tools
que escriben en la propia cuenta —equipo, titularidad, traspasos de NIF, cartera, claves y
webhooks— responden 403 live_credential_required cuando una clave de pruebas las llama sobre
una cuenta live. Probar cuentas gestionadas con clave fact_test_
muestra qué hace una clave de pruebas con las cuentas. Cuando tu flujo funcione de extremo a
extremo, cambia a live: crea una clave fact_live_, o vuelve a ejecutar el flujo de
consentimiento y selecciona el entorno Producción (live).
Este es el mismo mecanismo de sandbox que la API REST. Consulta la guía canónica Modo de prueba & sandbox para saber cómo se aprovisiona y consulta la empresa sandbox.