# Conectar un agente (/docs/mcp)

El mismo motor que la API REST, otra puerta de entrada: en vez de que una persona escriba `curl`, un
agente de IA de terceros llama herramientas tipadas.

```
https://mcp.facturia.cl
```

Un solo endpoint MCP (`/mcp`) hablando **Streamable HTTP**, y un solo conjunto de endpoints OAuth en
el mismo origen. No hay subdominios por empresa ni por cuenta: la tenencia es un claim del token, no
un hostname.

## El hecho arquitectónico [#el-hecho-arquitectónico]

> **El servidor MCP es un cliente REST de /v1**
>
> No tiene lógica de negocio ni credenciales del motor: ni certificado del SII, ni llave de firma, ni
> acceso al esquema del motor. Cada pregunta de dominio — cuáles son los totales de un documento, si
> hay folios disponibles, qué dice el F29 — es un request a `/v1`, nunca una consulta.
>
> Eso significa que todo lo que vale en esta documentación sobre estados, errores y garantías vale
> igual desde un agente.

## Por qué el agente no es un desarrollador [#por-qué-el-agente-no-es-un-desarrollador]

Un desarrollador lee la documentación antes de escribir la primera línea; un agente decide qué
llamar en el mismo turno en que se lo piden. Este servidor publica sus páginas como **recursos MCP**
—las devuelve enteras, no como un enlace, así que un cliente sin capacidad de fetch también las
lee— y aun así cada herramienta tiene que describirse sola: la documentación explica el modelo, la
descripción de la herramienta es lo único que el modelo tiene a mano cuando decide. De ahí salen
tres compromisos de diseño de esta capa:

**Suite completa, no un wrapper delgado.** Emisión, recepción y la capa tributaria están todas aquí.

**Ejecución directa, con una salida de emergencia para quien la quiera.** El paso de aprobación de
herramientas del propio cliente MCP es el humano en el ciclo por defecto. Las empresas que quieran
más pueden encender un doble paso borrador-y-confirmación.

**Ser AI-first es resolver ambigüedad, no rechazarla.** `draft_document` acepta un receptor por
nombre, porque así le habla una persona a un agente. Los ítems van estructurados
(`[{description, quantity, unit_price}]`): la prosa la convierte el modelo, que es quien la entiende
mejor y más barato, y `draft_document` devuelve los totales para que la persona los vea **antes** de
que se firme nada. No hay un parser de prosa en la API, y no lo va a haber: un parser escondido
detrás de un endpoint convierte una cantidad mal leída en un documento tributario.

## Agregar a tu cliente [#agregar-a-tu-cliente]

Una sola dirección, `https://mcp.facturia.cl/mcp`, y ningún comando que instalar: el transporte es
**Streamable HTTP**, así que no hay paquete que bajar ni proceso local que levantar. Tampoco hay un
secreto que pegar en el archivo de configuración — el cliente descubre el OAuth solo (RFC 9728), se
identifica con su documento de metadatos o se registra solo (RFC 7591), y te abre la pantalla de
consentimiento en el navegador, donde tú decides qué puede hacer el agente y sobre qué empresas.

#### Claude (mcp.json)

En Claude Desktop: **Settings → Connectors → Add custom connector**, con la URL
`https://mcp.facturia.cl/mcp`. Si prefieres editar el archivo a mano
(`claude_desktop_config.json`), es una línea:

```json
{
  "mcpServers": {
    "facturia": { "url": "https://mcp.facturia.cl/mcp" }
  }
}
```

#### Claude Code

```bash
claude mcp add --transport http facturia https://mcp.facturia.cl/mcp
```

La primera llamada a una herramienta abre el navegador para autorizar. Después,
`claude mcp list` muestra la conexión y `claude mcp remove facturia` la saca de este cliente
(para cortar el acceso de verdad, revoca desde el panel — ver [Revocación](#revocación)).

#### claude.ai

En la web: **Settings → Connectors → Add custom connector**, con la misma URL
`https://mcp.facturia.cl/mcp`. No hay archivo que editar; la autorización se abre ahí mismo.

#### Cursor

En `~/.cursor/mcp.json` (global) o `.cursor/mcp.json` (por proyecto), el mismo objeto:

```json
{
  "mcpServers": {
    "facturia": { "url": "https://mcp.facturia.cl/mcp" }
  }
}
```

Cursor muestra el servidor en **Settings → MCP**; la autorización se abre en el navegador la primera
vez que el agente llama una herramienta.

#### VS Code

Con GitHub Copilot en modo agente, en `.vscode/mcp.json` del proyecto (o en la configuración de
usuario), el mismo servidor remoto:

```json
{
  "servers": {
    "facturia": { "type": "http", "url": "https://mcp.facturia.cl/mcp" }
  }
}
```

VS Code abre el navegador para autorizar la primera vez que el agente usa una herramienta.

#### Windsurf

En `~/.codeium/windsurf/mcp_config.json`, el mismo objeto que Claude y Cursor:

```json
{
  "mcpServers": {
    "facturia": { "serverUrl": "https://mcp.facturia.cl/mcp" }
  }
}
```

#### ChatGPT

En ChatGPT los servidores MCP se agregan como **conector**: ajustes → conectores → agregar,
apuntando a `https://mcp.facturia.cl/mcp`. La disponibilidad depende del plan y del modo (los
conectores personalizados están en los planes de pago y en modo desarrollador).

Cualquier cliente que hable MCP sobre Streamable HTTP con OAuth sirve igual: no hay nada específico
de un cliente en este servidor.

> **¿Nunca has creado una cuenta?**
>
> La pantalla de consentimiento te deja iniciar sesión ahí mismo y crear la llave del grant por ti —
> no tienes que traerla lista. Y si todavía no tienes cuenta, el enlace de esa pantalla te lleva a
> crearla y **te devuelve a la misma autorización** cuando terminas: no hay que reconectar el agente.
>
> ¿Tampoco tienes empresas todavía? Tampoco es un problema: marca «Todas las empresas de esta cuenta,
> incluidas las que cree más adelante» junto con `empresas:write`, y el agente crea la primera con
> `create_empresa`.

Escribiendo `https://mcp.facturia.cl` en el navegador sale la misma información en una página:
qué es, el snippet y el enlace a esta documentación.

**Cómo se identifica el cliente.** La especificación de MCP fija un orden de prioridad para que un
cliente diga quién es: un cliente pre-registrado primero, después un documento de metadatos del
`client_id` (CIMD), y el registro dinámico al final, como respaldo.

* **Documento de metadatos del `client_id` (CIMD).** El cliente puede usar una URL https como
  `client_id` — por ejemplo `https://claude.ai/.well-known/oauth-client.json` — en vez de registrarse
  antes de conectarse. El servidor la obtiene al momento de la conexión (GET, 3 s de plazo, 32 KB
  máximo, sin redirecciones, nunca hacia una dirección privada o loopback) y valida el JSON: el
  `client_id` que declara el documento tiene que ser exactamente esa URL, `client_name` y
  `redirect_uris` son obligatorios, y solo se aceptan clientes públicos (`token_endpoint_auth_method`
  ausente o `"none"`, PKCE S256 como siempre). El resultado se cachea respetando
  `Cache-Control: max-age`, acotado entre 1 y 24 horas — un error nunca se cachea. El `redirect_uri`
  de cada autorización tiene que calzar exacto con uno de los que declara el documento, la misma
  regla que un cliente registrado — con una sola excepción, la de RFC 8252 §7.3: si el documento
  declara `http://localhost/…` o `http://127.0.0.1/…`, el puerto no se compara, porque un cliente
  nativo como Claude Code abre uno distinto en cada sesión.
* **Registro dinámico de cliente (RFC 7591).** El respaldo para el cliente que todavía no publica un
  CIMD — Cursor, VS Code, Windsurf, ChatGPT y similares siguen aquí. Cualquier cliente MCP se
  registra en la primera conexión, sin aprobación previa ni lista blanca de aplicaciones. El registro
  devuelve un `client_id` público; no hay `client_secret`, porque PKCE es el mecanismo de
  confidencialidad de un cliente público que no puede guardar un secreto.

```
POST https://mcp.facturia.cl/register        (RFC 7591)
GET  https://mcp.facturia.cl/.well-known/oauth-authorization-server
```

Ese mismo `GET` declara si el servidor acepta CIMD:

```json
{ "client_id_metadata_document_supported": true }
```

**Qué ve la persona en la pantalla de consentimiento.** Con un cliente CIMD la pantalla muestra su
nombre y el origen de su documento de metadatos («Claude · claude.ai») — ese origen es verificable,
porque ahí vive el documento. Un cliente que se registró dinámicamente sigue con la advertencia «Esta
aplicación se registró sola y eligió su propio nombre — nosotros no lo verificamos». En los dos casos
la pantalla sigue mostrando el host al que va el código de autorización («A dónde va esto»), con una
advertencia extra si ese host es `localhost` o `127.0.0.1`.

**Autorización y consentimiento.** Flujo estándar de authorization-code + PKCE. La pantalla de
consentimiento no es un timbre de goma: ahí decides qué credencial de Facturia queda envuelta en el
grant.

```
GET https://mcp.facturia.cl/authorize
  ?client_id=mcpc_01K2SC9X4M
  &redirect_uri=https://claude.ai/api/mcp/auth_callback
  &response_type=code
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256
  &scope=documents:read documents:write tax:read
  &state=af0ifjsldkj
```

**Token.** `POST /token` cambia el código (con el verificador PKCE) por un access token de vida corta
y un refresh token de vida más larga, revocable individualmente.

```json
{
  "access_token": "mcpat_9lQpV3fA2mKcRt7wZxYb1Nde",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "mcprt_qT8vN2wLmR5xKpJ7hYdCbA3s",
  "scope": "documents:read documents:write tax:read"
}
```

## Qué carga el token [#qué-carga-el-token]

El mismo modelo de autorización que una llave de API, porque debe comportarse igual una vez emitido:

* una **cuenta**,
* un **modo** — el token puede ser solo-sandbox, live o mixto, según lo que se concedió;
* un **conjunto de scopes**, con el mismo vocabulario que los scopes REST: `documents:read`,
  `documents:write`, `received:read`, `received:write`, `tax:read`, `tax:write`, `empresas:read`,
  `usage:read` y, para la puesta en marcha, `onboarding:write`, `mandato:write` y `empresas:write`;
* una **lista blanca de empresas** — RUT explícitos, o `"*"`.

> **Los dos scopes de atestación no existen en esta superficie**
>
> `/v1` también define `mandato:attest` y `onboarding:attest`: aceptar el mandato con un cuerpo, y
> autorizar la puesta en vivo con uno. Esos son el momento en que una **persona** dice «sí, acepto» y
> «sí, este mes», y quedan registrados como evidencia de esa decisión.
>
> Un agente no puede pedirlos acá, y no es una omisión: las herramientas MCP acuñan el enlace y se
> detienen; la persona decide en la página. Si el agente pudiera atestar, el registro de tu aprobación
> sería la palabra del agente.

## Nombrar la empresa en una llamada de herramienta [#nombrar-la-empresa-en-una-llamada-de-herramienta]

La lista de un token puede abarcar varias empresas (el agente de un contador que lleva varios
clientes, digamos), así que cada herramienta con alcance de empresa toma un argumento `empresa`
explícito — el análogo directo del header `Facturia-Empresa`.

| Situación                                                               | Comportamiento                                           |
| ----------------------------------------------------------------------- | -------------------------------------------------------- |
| Omitido donde se requiere                                               | La herramienta devuelve `empresa_required`               |
| Presente pero fuera de la lista                                         | `empresa_not_allowed` — nunca un «no encontrado» a secas |
| Un token con exactamente una empresa                                    | Puedes omitirlo; el servidor lo rellena                  |
| Herramientas de nivel cuenta (`list_empresas`, `get_usage` sin empresa) | Rechazan el argumento                                    |

## El límite del sandbox [#el-límite-del-sandbox]

> **Trampa — Un grant de sandbox es una capacidad, no una política**
>
> Un token con alcance de sandbox **no puede** alcanzar los datos de una empresa live ni emitir nada
> con efecto legal. No es una preferencia que se pueda cambiar en caliente: es un límite estructural.
>
> Por eso es el default correcto para cualquier cosa exploratoria o de cara al público. El registro
> dinámico de cliente significa que el software que llama **no está verificado por construcción**, y el
> alcance del token es la defensa principal contra un agente comprometido o demasiado confiado.

La pantalla de consentimiento defiende lo mismo desde el otro lado: no viene nada premarcado. Eliges
explícitamente qué empresas puede tocar este agente. Conceder `"*"` es posible pero exige un clic
extra de confirmación — la asimetría es intencional, porque un grant a un agente de terceros
demasiado confiado es un riesgo materialmente distinto de una llave de API de primera parte guardada
en tu propio backend.

## Revocación [#revocación]

En el panel, **Agentes conectados** lista cada grant MCP de la cuenta: qué cliente se conectó, con
qué scopes, sobre qué empresas, en qué modo, cuándo y cuándo lo usó por última vez. Revocar uno
invalida de inmediato sus access y refresh tokens.

Hay un segundo camino que sirve igual: cada grant envuelve una llave de API tuya, así que revocar
esa llave en **Llaves** mata el grant en el mismo instante.

## Límites de tasa [#límites-de-tasa]

Las llamadas MCP pegan a los mismos servicios de backend que las REST y comparten los mismos buckets
publicados. No hay cuota MCP separada, porque no hay un pipeline de emisión separado por debajo.

| Bucket                                   | Límite                 | Qué herramientas                                                                                                                                                                                                     |
| ---------------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| General                                  | 300 req/min            | `draft_document`, todas las `list_*` / `get_*`, `query_rcv`, `get_periodo_summary`, y las escrituras que no emiten ni declaran (`update_*`, `set_*`, `characterize_purchases`, `send_document`, `request_folios`, …) |
| Emisión                                  | 10 req/s, ráfaga 30    | `emit_document`, `emit_tipo46`, `emit_credit_note`                                                                                                                                                                   |
| Declaración F29 y sincronización del RCV | 10 req/min por empresa | `file_f29`, `sync_rcv` (una llamada por período; un rango de diez meses gasta el bucket entero)                                                                                                                      |
| Artefactos                               | 60 req/min             | `get_document_file` (el minteo de la URL firmada, no la descarga posterior, que es pública y no medida)                                                                                                              |

Una llamada limitada devuelve el tipo `rate_limit_error` con un campo `retry_after_seconds` sobre el
que un agente puede actuar directamente.

- [Catálogo de herramientas](/docs/mcp/herramientas): Todas las herramientas del servidor, con sus anotaciones y cuáles pasan por confirmación.

- [Confirmaciones](/docs/mcp/confirmaciones): El doble paso para emitir, declarar y reclamar.
