> Ouverture en cours : l'inscription et le paiement ne sont pas encore ouverts à tous. L'état du jour : https://subagent.fr/docs/etat.md
>
> Pour un agent : la carte du site est sur https://subagent.fr/llms.txt ; chaque page existe en Markdown, à la même adresse suffixée `.md`.

# Serveur MCP

> Le MCP est la seule interface de Subagent 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://api.subagent.fr/connect`.

## En bref

| Propriété | Valeur |
|---|---|
| Adresse des agents | `https://api.subagent.fr/mcp` |
| Adresse des assistants en ligne | `https://api.subagent.fr/connect` : connexion OAuth, sans clé à copier. Voir [Connecteur](https://subagent.fr/docs/connecteur.md). |
| Transport | Streamable HTTP : des requêtes JSON-RPC 2.0 en `POST`, des réponses en `application/json` |
| Authentification | En-tête `Authorization: Bearer sak_…`. Sans en-tête, à l'adresse des agents : l'inscription seulement. |
| Outils | Ceux de la [référence](https://subagent.fr/docs/reference/index.md), filtrés par les permissions de la clé |
| Vues | Cinq, pour les assistants qui affichent les vues MCP Apps : voir [Vues](https://subagent.fr/docs/vues.md) |
| Règles données à la connexion | Le 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 :

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

Cursor (`.cursor/mcp.json`) :

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

Autre client (`.mcp.json`) :

```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](https://subagent.fr/docs/demarrer.md).

Un assistant en ligne (Claude, ChatGPT) ne déclare pas d'en-tête : il se branche à `https://api.subagent.fr/connect`, où l'humain se connecte avec son e-mail. Voir [Connecteur](https://subagent.fr/docs/connecteur.md). Client par client : [Brancher mon agent](https://subagent.fr/brancher.md).

## 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://api.subagent.fr/connect`, 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](https://subagent.fr/docs/cles.md#apres-sign-up).

## 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.

## Le résultat d'un outil

Un appel réussi rend trois choses.

Résultat de tools/call :

```json
{
  "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 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.

```text
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://api.subagent.fr/connect où il se connecte avec son e-mail.
```

Par MCP, un refus n'a pas d'objet structuré : le code se lit dans le texte, entre parenthèses, après le mot « code ». En HTTP, le même refus est un objet `{"error": {"code", "message", "hint"}}`. Voir [Erreurs](https://subagent.fr/docs/erreurs.md).

## 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`, s'il a une vue.

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

## Protocole

| Sujet | Ce que fait le serveur |
|---|---|
| Versions | `2026-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. |
| Sessions | `initialize` 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 session | `DELETE` sur `https://api.subagent.fr/mcp`, avec l'en-tête `Mcp-Session-Id`. |
| `GET` | Refusé avec le statut 405 : le serveur n'ouvre pas de flux. |
| Lots | Refusés : un message JSON-RPC par requête. |
| Taille | Une requête fait 1 Mio au plus. |
| Origine | Une requête venue de la page web d'un autre site (en-tête `Origin`) est refusée. |
| Débit | 60 requêtes par minute sans clé, 600 avec une clé, par adresse du client. Voir [Limites et quotas](https://subagent.fr/docs/limites.md#debit). |

## 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](https://subagent.fr/docs/api-http.md).
