Aller au contenu
Maildocs
Sommaire de la documentation

Documentation › Serveur MCP

Serveur MCP

Voir en .md

Le MCP est la seule interface d’Escouade Mail. L’adresse des agents est https://api.subagent.fr/mcp, en Streamable HTTP. Sans clé, le serveur n’y sert que l’inscription. Avec une clé, il sert les outils que ses permissions ouvrent. Les assistants en ligne se branchent au même serveur par une seconde adresse, https://mail.escouade.io/mcp.

En bref

PropriétéValeur
Adresse des agentshttps://api.subagent.fr/mcp
Adresse des assistants en lignehttps://mail.escouade.io/mcp : connexion OAuth, sans clé à copier. Voir Connecteur.
TransportStreamable HTTP : des requêtes JSON-RPC 2.0 en POST, des réponses en application/json
AuthentificationEn-tête Authorization: Bearer sak_…. Sans en-tête, à l’adresse des agents : l’inscription seulement.
OutilsCeux de la référence, filtrés par les permissions de la clé
VuesUn établi par conversation, ouvert par open_workspace puis piloté par navigate_workspace : voir Vues
Règles données à la connexionLe champ instructions de la réponse à initialize : moins de 2 000 caractères

Brancher un client

Le serveur se déclare par son adresse, et la clé par un en-tête.

Claude Code

claude mcp add \
  --transport http subagent \
  https://api.subagent.fr/mcp \
  --header "Authorization: Bearer $SUBAGENT_KEY"

Cursor · .cursor/mcp.json

{
  "mcpServers": {
    "subagent": {
      "url": "https://api.subagent.fr/mcp",
      "headers": {
        "Authorization": "Bearer ${env:SUBAGENT_KEY}"
      }
    }
  }
}

Autre client · .mcp.json

{
  "mcpServers": {
    "subagent": {
      "type": "http",
      "url": "https://api.subagent.fr/mcp",
      "headers": {
        "Authorization": "Bearer ${SUBAGENT_KEY}"
      }
    }
  }
}

Pas encore de clé : déclarez l’adresse sans en-tête, laissez l’agent appeler sign_up, puis ajoutez l’en-tête. Voir Démarrer.

Un assistant en ligne (Claude, ChatGPT) ne déclare pas d’en-tête : il se branche à https://mail.escouade.io/mcp, où l’humain se connecte avec son compte Escouade. Voir Connecteur. Client par client : Brancher mon agent.

Sans clé

À l’adresse https://api.subagent.fr/mcp, sans en-tête d’autorisation, le serveur répond quand même, pour l’inscription. À l’adresse https://mail.escouade.io/mcp, il répond 401 : l’assistant ouvre alors la page de connexion.

  • Il sert initialize, ping, tools/list et tools/call.
  • tools/list rend un seul outil : sign_up.
  • Il n’ouvre pas de session et ne sert pas les vues.
  • L’appel d’un autre outil est refusé avec le code unauthorized. Toute autre méthode reçoit le statut HTTP 401.

Le serveur lit l’en-tête à chaque requête. Après sign_up, la connexion ouverte sans clé ne montre toujours que sign_up : il faut présenter la clé. Voir Clés et permissions.

Avec une clé

  • tools/list rend les outils que les permissions de la clé ouvrent. sign_up n’y figure plus.
  • Une fois l’espace vérifié, verify et attach_human quittent la liste.
  • Une clé présentée mais inconnue ou révoquée reçoit le statut HTTP 401.

Un seul établi

open_workspace est l’entrée visuelle. Après son ouverture, le modèle reprend l’instance_id du contexte de cette conversation et demande une page avec navigate_workspace : audiences, contacts, campagnes ou performances. Les outils métier continuent à rendre leurs données sans demander une nouvelle carte.

La vue reçoit les demandes avec workspace_events, réservé à l’application par _meta.ui.visibility: ["app"]. Elle attend au plus 18 secondes par appel et garde une seule attente en cours. Le modèle ne doit pas appeler cet outil pour entretenir la liaison. navigate_workspace reste visible au modèle et à l’application.

Un reçu pending signifie que la navigation est demandée. Une saisie en cours peut retarder ou empêcher son affichage : la vue laisse l’humain choisir. Voir Navigation de l’établi.

Le résultat d’un outil

Un appel réussi rend trois choses.

Résultat de tools/call
{
  "content": [{ "type": "text", "text": "Message msg_… en file dans le fil thr_… : …" }],
  "structuredContent": { "message": {}, "thread": {}, "inbox": {}, "replayed": false, "estimated_send_at": "2026-10-13T14:31:00+02:00" },
  "isError": false
}
  • content : un texte lisible en français. Il dit qui, quoi, quand, et le quota restant.
  • structuredContent : l’objet de l’outil, dont le schéma est publié dans https://api.subagent.fr/openapi.json. C’est lui qu’un programme lit.
  • isError : false.

Le résultat porte aussi _meta["subagent/requestId"], un identifiant de diagnostic. Les métadonnées de contexte de l’espace peuvent accompagner l’objet métier ; elles servent au cloisonnement et à l’actualisation des données de la vue.

Le texte lisible coupe le corps d’un message à 2 000 caractères. Le corps entier est dans structuredContent.

Un refus

Un refus du service n’est pas une erreur de protocole. C’est un résultat d’outil marqué isError, avec un texte : le message, le code entre parenthèses, puis la piste après une flèche.

approve_messagerefusé
approve_message demande la permission approve, que cette clé n’a pas. (code forbidden)
→ La validation est un geste humain : votre humain l’accomplit depuis son assistant (Claude, ChatGPT…), branché à l’adresse https://mail.escouade.io/mcp où il se connecte avec son e-mail.

Par MCP, le même refus figure aussi dans structuredContent.error : code, message et, lorsqu’elle existe, la piste hint. Un client doit lire ces champs ; le texte reste disponible pour l’humain. En HTTP, l’objet est {"error": {"code", "message", "hint"}}. Voir Erreurs.

Ce que chaque outil annonce

Dans tools/list, chaque outil porte :

  • name, title et description, en français ;
  • inputSchema : ses paramètres. Un paramètre inconnu est refusé. tools/list ne porte pas de schéma de sortie (outputSchema) ;
  • annotations : readOnlyHint (l’outil ne fait que lire), destructiveHint (son effet ne se défait pas), idempotentHint ;
  • _meta.ui.resourceUri et _meta["openai/outputTemplate"] pour l’entrée visuelle open_workspace ;
  • _meta.ui.visibility : model et app en général, app uniquement pour workspace_events ;
  • _meta["openai/widgetAccessible"]: true pour les appels de la vue, y compris les outils sans nouvelle carte.

Le schéma de l’objet structuré de chaque outil se lit dans https://api.subagent.fr/openapi.json.

L’établi utilise la ressource ui://subagent/workspace-v12.html. Les adresses des anciennes vues restent lisibles pour les conversations déjà ouvertes. Après une mise à jour du connecteur, l’hôte doit relire les descripteurs d’outils et leur adresse de ressource ; un ancien descripteur en cache peut encore demander les cartes précédentes.

Protocole

SujetCe que fait le serveur
Versions2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26. Une autre version dans l’en-tête Mcp-Protocol-Version est refusée.
Sessionsinitialize ouvre une session et rend l’en-tête Mcp-Session-Id. Elle vaut 24 heures après la dernière requête. La session est facultative : une requête sans cet en-tête est servie sans état. Une session expirée est effacée après 30 jours.
Fin de sessionDELETE sur https://api.subagent.fr/mcp, avec l’en-tête Mcp-Session-Id.
GETRefusé avec le statut 405 : le serveur n’ouvre pas de flux.
LotsRefusés : un message JSON-RPC par requête.
TailleUne requête fait 1 Mio au plus.
OrigineUne requête venue de la page web d’un autre site (en-tête Origin) est refusée.
Débit60 requêtes par minute sans clé, 600 avec une clé, par adresse du client. Voir Limites et quotas.

Sans MCP

Chaque outil existe aussi en POST https://api.subagent.fr/v1/{opération}, avec les mêmes arguments et le même objet en réponse. Voir API HTTP.

Cette page en Markdown : /mail/docs/mcp.md Toute la documentation pour un agent : /mail/docs/llms.txt Mentions légales · Conditions générales · Confidentialité · Signaler un abus