# Protocole MCP et découverte

Transport, contexte, schémas, résultats et limites.

## Transport

Le serveur est exposé en `POST /api/mcp`, en Streamable HTTP sans session serveur persistante. Chaque requête est authentifiée. Les réponses sont JSON ; GET et DELETE répondent 405 et il n'y a pas de flux SSE durable.

Un client MCP doit négocier la connexion selon le protocole qu'il prend en charge. Utilisez de préférence un SDK compatible et les métadonnées de découverte du serveur. Ne supposez pas qu'un test HTTP sans authentification décrit les capacités réelles.

## OAuth et découverte

Le 401 du MCP fournit un challenge avec la ressource protégée. L'organisation sert `/.well-known/oauth-protected-resource/api/mcp` et les métadonnées du serveur d'autorisation. Le flux personnel utilise un client public, un code d'autorisation avec PKCE S256 et le rafraîchissement autorisé.

La ressource doit correspondre à l'hôte de l'organisation. Ne choisissez pas une URL de retour arbitraire et ne désactivez pas la vérification d'état ou de PKCE pour débloquer une connexion.

## Ordre de travail d'un assistant

1. Lisez `/llms.txt` sur l'instance pour l'orientation générale.
2. Appelez `load_context` pour le vocabulaire, le porteur du jeton, les capacités, les quotas et le travail ouvert.
3. Appelez `describe_schema` avant d'inventer une structure ou d'écrire un champ.
4. Consultez `tools/list` pour les outils et leurs schémas effectivement autorisés.
5. Recherchez les objets existants, puis appelez l'outil métier adapté.
6. Vérifiez le résultat, y compris les validations et demandes d'approbation.

Les ressources `cercla://context` et `cercla://schema` donnent les équivalents des deux outils de découverte. `load_context` accepte `ifNoneMatch` pour éviter de retransmettre un contexte inchangé.

## Périmètre et schémas

La référence de ce site recense les définitions du dépôt. Le serveur filtre le catalogue selon l'identité et les scopes. Des gardes complémentaires s'appliquent à l'exécution, notamment pour les outils composés et les documents rattachés.

Les schémas de `tools/list` sont ceux de la version installée. `describe_schema` ajoute les noms personnalisés et les champs de l'organisation. Ne réutilisez pas le schéma d'une autre organisation sans vérification.

## Résultats et erreurs

Les erreurs de transport sont JSON-RPC avec les statuts HTTP appropriés. Un outil peut aussi retourner une erreur métier dans son résultat. Vérifiez le contenu et l'indicateur d'erreur du SDK ; un HTTP 200 ne prouve pas qu'une action a abouti.

La section `capabilities` de `load_context` dit, par domaine, ce que le jeton peut lire ou modifier, les outils qui lui sont fermés et ceux qui passent par une approbation. Un refus porte sa cause et son remède : `scope_not_granted` (accès non coché au consentement, ou non accordé à l'application connectée), `profile` (le profil d'accès de la personne ne le permet pas, un administrateur doit agir), `module_off` (application arrêtée pour l'organisation), `record_read_only` (fiche partagée en lecture seule). Une fiche hors du périmètre reste introuvable. Des arguments invalides sont signalés champ par champ.

La pagination MCP utilise couramment 20 éléments par défaut et un plafond de 200. Les outils particuliers peuvent avoir leur propre limite. Respectez les curseurs et les refus de quota.

## Idempotence

Les outils d'écriture reçoivent `idempotency_key`. Conservez une clé par intention logique et réutilisez-la après une coupure ou pour retrouver le résultat d'une demande déjà soumise. Changer de clé à chaque reprise peut produire une seconde action.

Une réponse `pending_approval` impose d'attendre et de suivre l'approbation ; elle n'autorise pas à passer par un autre outil pour contourner le contrôle.

## Matrice de couverture

Générée depuis le catalogue des outils et le contrat OpenAPI v1 : chaque case objet:verbe a un outil, ou une décision (exclusion d'ADR-205 D1, ou travail à faire suivi par une issue).

119 cases : 87 couvertes par un outil, 22 à faire, 10 exclues (liste fermée d'ADR-205 D1).

| Objet:verbe | Outils | Décision |
|---|---|---|
| `abysign:autre` |  | à faire (#1702) : Signature électronique : enveloppes, relance, annulation |
| `activity:list` | `list_activity` |  |
| `affiliations:autre` |  | à faire (#1702) : Supprimer un lien saisi par erreur ; end_affiliation termine un lien réel |
| `affiliations:create` | `link_contact_to_company` |  |
| `affiliations:list` |  | à faire (#1702) : Lister les affiliations ; list_contacts (companyId) couvre l'usage courant |
| `affiliations:update` | `end_affiliation` |  |
| `approvals:autre` | `cancel_approval` |  |
| `approvals:get` | `get_approval` |  |
| `approvals:list` | `list_approvals` |  |
| `articles:search` | `search_articles` |  |
| `business-units:get` |  | à faire (#1702) : Lecture unitaire d'une entité ; list_business_units couvre la liste |
| `business-units:list` | `list_business_units` |  |
| `business-units:update` |  | à faire (#1702) : Identité légale et bancaire d'une entité (administration) |
| `campaigns:autre` | `get_campaign_report`, `send_test_campaign`, `archive_campaign`, `get_campaign_profitability` |  |
| `campaigns:create` | `create_campaign` |  |
| `campaigns:get` | `get_campaign` |  |
| `campaigns:list` | `list_campaigns` |  |
| `campaigns:transition` | `send_campaign`, `schedule_campaign`, `cancel_scheduled_campaign` |  |
| `campaigns:update` | `update_campaign` |  |
| `chat:autre` |  | à faire (#1702) : Messagerie interne |
| `comments:create` | `add_comment` |  |
| `comments:list` |  | à faire (#1702) : Lire le fil de discussion d'une fiche |
| `companies:autre` | `assign_record`, `claim_record`, `share_record`, `archive_company`, `restore_record`, `upsert_company_map`, `link_company_reference` |  |
| `companies:create` | `create_company` |  |
| `companies:get` | `get_company` |  |
| `companies:list` | `list_companies` |  |
| `companies:update` | `update_company` |  |
| `company-relations:list` |  | à faire (#1702) : Relations entre sociétés |
| `contacts:autre` | `list_hot_contacts`, `archive_contact` |  |
| `contacts:create` | `create_contact` |  |
| `contacts:get` | `get_contact` |  |
| `contacts:list` | `list_contacts` |  |
| `contacts:transition` | `tag_contacts`, `untag_contacts` |  |
| `contacts:update` | `update_contact` |  |
| `contracts:autre` | `set_external_ref`, `remove_external_ref` |  |
| `contracts:get` | `get_contract` |  |
| `contracts:list` | `list_contracts` |  |
| `contracts:transition` | `propose_renewal`, `terminate_contract` |  |
| `contracts:update` | `update_contract` |  |
| `cycles:list` | `list_cycles` |  |
| `devices:autre` |  | exclu : Jetons d'appareil de l'app mobile : authentification (ADR-205 D1) |
| `devices:create` |  | exclu : Jetons d'appareil de l'app mobile : authentification (ADR-205 D1) |
| `discovery:get` | `load_context` |  |
| `discovery:search` | `find_screen` |  |
| `documents:get` | `get_document_url` |  |
| `documents:list` | `list_documents` |  |
| `exports:autre` |  | à faire (#1702) : Téléchargement des exports de l'organisation |
| `exports:list` |  | à faire (#1702) : Liste des exports de l'organisation |
| `forms:autre` | `get_form_submissions` |  |
| `forms:list` | `list_forms` |  |
| `home:list` | `morning_briefing` |  |
| `insights:get` | `get_insight` |  |
| `invoices:autre` |  | à faire (#602) : Facturation par MCP (PDF, Factur-X, transmissions), backlog |
| `invoices:get` | `get_invoice` |  |
| `invoices:list` | `list_invoices` |  |
| `invoices:transition` |  | à faire (#602) : Encaissement d'une facture, backlog |
| `journeys:autre` | `simulate_journey` |  |
| `journeys:get` | `get_journey` |  |
| `journeys:list` | `list_journeys` |  |
| `journeys:transition` | `publish_journey` |  |
| `journeys:update` | `save_journey` |  |
| `kpis:list` | `get_kpis` |  |
| `meta:list` | `describe_schema` |  |
| `notifications:autre` |  | à faire (#1702) : Notifications de la personne |
| `notifications:list` |  | à faire (#1702) : Notifications de la personne |
| `notifications:transition` |  | à faire (#1702) : Notifications de la personne |
| `ping:list` |  | exclu : Santé du service : surface plateforme (ADR-205 D1) |
| `products:create` | `create_product` |  |
| `products:get` | `get_product` |  |
| `products:list` | `list_products` |  |
| `products:update` | `update_product` |  |
| `quality:get` | `get_data_quality`, `get_duplicate_pair`, `get_link_dossier` |  |
| `quality:list` | `list_duplicate_pairs`, `list_unlinked_companies`, `list_enrichment_suggestions` |  |
| `quality:transition` | `merge_duplicate_pair`, `dismiss_duplicate_pair`, `decide_enrichment_suggestion` |  |
| `quotes:autre` | `get_quote_cohort`, `retain_quote_options` |  |
| `quotes:create` | `create_quote` |  |
| `quotes:get` | `get_quote` |  |
| `quotes:list` | `list_quotes` |  |
| `quotes:transition` | `set_quote_tags`, `request_quote_approval`, `set_quote_forecast`, `transition_quote`, `propose_quote_alternative`, `set_quote_alternative_reference`, `leave_quote_alternatives`, `advance_quote` |  |
| `quotes:update` | `update_quote` |  |
| `recipes:create` | `prepare_recipe` |  |
| `recipes:get` | `preview_recipe` |  |
| `recipes:list` | `list_recipes` |  |
| `reply-templates:list` | `list_reply_templates` |  |
| `requests:create` | `create_request` |  |
| `requests:get` | `get_request` |  |
| `requests:list` | `list_requests` |  |
| `requests:transition` | `transition_request` |  |
| `requests:update` | `update_request` |  |
| `sales-activities:create` | `create_sales_activity`, `log_interaction` |  |
| `sales-activities:get` | `get_sales_activity` |  |
| `sales-activities:list` | `list_sales_activities` |  |
| `sales-activities:transition` | `transition_sales_activity` |  |
| `sales-activities:update` | `update_sales_activity` |  |
| `sales-targets:autre` |  | à faire (#1702) : Objectifs individuels (geste targets.manage) |
| `sales-targets:list` |  | à faire (#1702) : Objectifs individuels (geste targets.manage) |
| `sales-targets:update` |  | à faire (#1702) : Mode des objectifs (geste targets.manage) |
| `search:search` | `search` |  |
| `sectors:list` |  | à faire (#1702) : Référentiel des secteurs |
| `segments:autre` | `preview_segment` |  |
| `segments:create` | `create_segment` |  |
| `segments:list` | `list_segments` |  |
| `sync:autre` |  | exclu : Synchronisation hors ligne de l'app mobile : surface plateforme (ADR-205 D1) |
| `tags:create` |  | à faire (#1702) : Créer une étiquette ; tag_contacts pose les étiquettes existantes |
| `tags:list` | `list_tags` |  |
| `templates:create` | `create_email_template` |  |
| `templates:get` | `get_template` |  |
| `templates:list` | `list_templates` |  |
| `templates:update` | `update_email_template` |  |
| `users:admin` | `invite_user`, `list_org_users`, `update_user`, `deactivate_user`, `reactivate_user`, `resend_invitation` |  |
| `users:autre` |  | à faire (#1702) : Profil de la personne (users/me) ; load_context dit qui porte le jeton |
| `users:list` | `list_users` |  |
| `users:update` |  | à faire (#1702) : Modifier son propre profil (users/me) |
| `webhook-deliveries:list` |  | exclu : Webhooks : configuration des apps connectées (ADR-205 D1) |
| `webhook-deliveries:transition` |  | exclu : Webhooks : configuration des apps connectées (ADR-205 D1) |
| `webhook-subscriptions:autre` |  | exclu : Webhooks : configuration des apps connectées (ADR-205 D1) |
| `webhook-subscriptions:create` |  | exclu : Webhooks : configuration des apps connectées (ADR-205 D1) |
| `webhook-subscriptions:list` |  | exclu : Webhooks : configuration des apps connectées (ADR-205 D1) |
| `webhook-subscriptions:update` |  | exclu : Webhooks : configuration des apps connectées (ADR-205 D1) |
