# Serveur MCP BastaCompta

BastaCompta expose un serveur **MCP (Model Context Protocol)** pour que Claude,
ChatGPT, Cursor ou tout agent compatible tiennent la comptabilité d’une société
directement depuis la conversation. Transport *Streamable HTTP* sans état,
JSON-RPC 2.0, protocole `2025-06-18`.

- Endpoint : `POST https://bastacompta.com/mcp` (`GET /mcp` renvoie la fiche de découverte)
- Authentification : OAuth 2.1 avec PKCE ou `Authorization: Bearer <clé d’agent>` ;
  la clé, créée dans BastaCompta → Connexions ou par consentement OAuth, est
  limitée à **une société** et à des **scopes**
- Même piste d’audit que l’interface : chaque brouillon porte l’acteur `agent:<nom>`
- Limite : 60 requêtes API par minute et par clé (un outil MCP consomme 1 à 3 requêtes)

## Principe : l’agent prépare, la personne valide

| L’agent peut | L’agent ne peut pas |
|---|---|
| lire le régime, les règles, les demandes de pièces, les pièces et les opérations importées | initialiser un exercice |
| déposer des pièces (statut « à vérifier ») | vérifier une pièce |
| préparer et retirer des brouillons d’écritures | **valider** une écriture |
| lire journal, balance, synthèse 2072 / 2065 | contrepasser, verrouiller, clôturer, exporter le FEC |

Ces actes sont refusés (403) même en appelant l’API REST directement : ce n’est
pas une limite de l’outil MCP, c’est le serveur qui l’impose.

## Scopes

| Scope | Autorise |
|---|---|
| `requests:read` | `get_company`, `list_document_requests` |
| `documents:read` | `list_documents` (métadonnées et statut, sans contenu du fichier) |
| `documents:write` | `upload_document` |
| `operations:read` | `list_bank_operations` (opérations déjà importées ; aucun consentement ou import) |
| `requests:reply` | `reply_to_request` |
| `ledger:read` | `list_books`, `get_ledger`, `get_fiscal_summary` |
| `ledger:draft` | `save_draft_entry`, `cancel_draft_entry` |

`list_rubriques_2072` est toujours disponible.

## Outils

| Outil | Rôle |
|---|---|
| `get_company` | Identité, forme, régime (`SCI_IR_EUR` ou `PCG_IS_EUR`), profil, règles, étape du dossier |
| `list_document_requests` | Demandes de pièces et révision |
| `list_documents` | Pièces existantes, statut de revue, rattachements, empreinte SHA-256 et référence de provenance |
| `list_bank_operations` | Opérations bancaires importées, banque, contrepartie, devise connue ou nulle, catégorie, statut et pièces rattachées ; montants dans l’unité mineure de la devise d’origine |
| `upload_document` | Dépôt PDF/PNG/JPEG en base64 (5 Mo), `request_id`, `operation_id` et `source_ref` facultatifs ; renvoie `document_id` |
| `reply_to_request` | Réponse écrite à une demande |
| `list_books` | Exercices (clôture `AAAA-MM-JJ`) |
| `get_ledger` | Config, brouillons, dernières écritures (`posted_limit`), balance, synthèse fiscale |
| `get_fiscal_summary` | Revenu foncier par rubrique 2072 (IR) ou produits/charges/résultat (IS) |
| `list_rubriques_2072` | Rubriques acceptées en IR |
| `save_draft_entry` | Brouillon en partie double, centimes, pièce existante, rubriques en IR ; la pièce doit être vérifiée avant validation humaine de l’écriture |
| `cancel_draft_entry` | Retrait d’un brouillon avec motif |

Les règles métier (équilibre, dates dans l’exercice, période verrouillée,
rubrique 2072, encaissement en IR, TVA hors option) sont renvoyées comme
résultat `isError: true` avec le message exact du serveur, pour que l’agent
corrige et réessaie.

Avant un dépôt répété, lire `list_documents`. Un fichier déjà déposé avec le
même rattachement renvoie `duplicate: true` et son **statut actuel** (par exemple
`accepted` après revue). Un rattachement à une autre opération exige une
correction humaine plutôt qu’un dépôt silencieux. `source_ref` est une référence
externe courte et non secrète ; elle ne vaut pas validation de la pièce.
Une clé avec seulement `documents:write` peut fournir la révision connue du
dossier dans `upload_document.revision` ; si elle est absente, l’outil lit
`list_document_requests` et requiert aussi `requests:read`.

## Configuration côté client

Claude Code :
```sh
claude mcp add --transport http bastacompta https://bastacompta.com/mcp --header "Authorization: Bearer basta_…"
```

Claude Desktop / claude.ai (connecteur personnalisé) : URL `https://bastacompta.com/mcp`,
en-tête `Authorization`. Cursor / autres clients Streamable HTTP : même URL et
même en-tête.

ChatGPT (GPT Actions) : importer `https://bastacompta.com/agent-openapi.json`,
authentification *Bearer* avec la clé.

## Authentification OAuth 2.1 (recommandée pour claude.ai, Claude Desktop, ChatGPT)

BastaCompta est son propre serveur d’autorisation :

| Point de terminaison | Rôle |
|---|---|
| `GET /.well-known/oauth-protected-resource` | RFC 9728 — désigne le serveur d’autorisation (annoncé dans le `WWW-Authenticate` du 401) |
| `GET /.well-known/oauth-authorization-server` | RFC 8414 — métadonnées (PKCE S256 seul, clients publics, `authorization_code` + `refresh_token`) |
| `POST /oauth/register` | RFC 7591 — enregistrement dynamique (redirect_uris https, `http://localhost`, ou schéma applicatif) |
| `GET /oauth/authorize` | page de consentement : le compte BastaCompta authentifie l’utilisateur ; choix de la société et des scopes |
| `POST /oauth/token` | code + `code_verifier` → jeton d’accès (90 jours) + jeton de rafraîchissement (180 jours, rotation, scope réductible) |
| `POST /oauth/revoke` | RFC 7009 |

Le jeton d’accès **est une clé d’agent** (`basta_…`) : il apparaît dans
Connexions sous « OAuth : <client> » avec ses scopes, compte dans la limite de
10 accès actifs par société, et se révoque comme les autres. Un code
d’autorisation est à usage unique et expire après 10 minutes.

Flux côté client (automatique dans Claude / ChatGPT) : 401 → métadonnées →
enregistrement → redirection de l’utilisateur vers `/oauth/authorize` (il se
connecte à son espace BastaCompta, choisit la SCI, coche les droits) →
callback avec le code → échange PKCE → appels `/mcp` avec le Bearer.

## Si Cloudflare Access protège le site

La configuration d’Access doit laisser les clients MCP joindre les routes
suivantes. Si une politique Access les bloque, prévoir un bypass ciblé pour :

```
bastacompta.com/mcp
bastacompta.com/api/agent/*
bastacompta.com/.well-known/*
bastacompta.com/oauth/register
bastacompta.com/oauth/token
bastacompta.com/oauth/revoke
bastacompta.com/docs/mcp
bastacompta.com/llms.txt
bastacompta.com/robots.txt
bastacompta.com/agent-openapi.json
```

`/oauth/authorize` et `/oauth.js` ouvrent le parcours de consentement. Le
serveur exige une session BastaCompta et vérifie l’appartenance à la société
avant d’émettre un code. Si Access est utilisé comme couche supplémentaire,
sa politique doit aussi permettre au navigateur de joindre ces pages.

## Limites actuelles

- Le consentement exige un compte BastaCompta actif et une session vérifiée
  par son second facteur. Les comptes externes et les sociétés sont séparés
  par les appartenances enregistrées côté serveur.
- Pas de flux SSE serveur → client (pas de notifications) ; les réponses sont
  du JSON simple.
- Une seule société par clé ; pour plusieurs SCI, plusieurs clés ou plusieurs
  serveurs configurés.

Tests : `cargo test` (schémas d’outils, PKCE, redirect_uris, scopes) ;
`node tests/mcp.mjs` et `node tests/oauth.mjs` contre `wrangler dev`.
