MCP server
MCP is Escouade Mail’s only interface. The agents’ address is https://api.subagent.fr/mcp, over Streamable HTTP. Without a key, the server only serves sign-up there. With a key, it serves the tools its permissions open. Online assistants connect to the same server through a second address, https://mail.escouade.io/mcp.
In short
| Property | Value |
|---|---|
| Agents’ address | https://api.subagent.fr/mcp |
| Online assistants’ address | https://mail.escouade.io/mcp: OAuth sign-in, no key to copy. See Connector. |
| Transport | Streamable HTTP: JSON-RPC 2.0 requests over POST, responses as application/json |
| Authentication | Authorization: Bearer sak_… header. Without a header, at the agents’ address: sign-up only. |
| Tools | Those of the reference, filtered by the key’s permissions |
| Views | One workbench per conversation, opened by open_workspace then driven by navigate_workspace: see Views |
| Rules given at connection | The instructions field of the response to initialize: under 2,000 characters |
Connecting a client
The server is declared by its address, and the key by a header.
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}"
}
}
}
}Other client · .mcp.json
{
"mcpServers": {
"subagent": {
"type": "http",
"url": "https://api.subagent.fr/mcp",
"headers": {
"Authorization": "Bearer ${SUBAGENT_KEY}"
}
}
}
}No key yet: declare the address without a header, let the agent call sign_up, then add the header. See Getting started.
An online assistant (Claude, ChatGPT) declares no header: it connects to https://mail.escouade.io/mcp, where the human signs in with their Escouade account. See Connector. Client by client: Connect my agent.
Without a key
At https://api.subagent.fr/mcp, without an authorisation header, the server still answers, for sign-up. At https://mail.escouade.io/mcp, it answers 401: the assistant then opens the sign-in page.
- It serves
initialize,ping,tools/listandtools/call. tools/listreturns a single tool:sign_up.- It opens no session and serves no views.
- Calling another tool is refused with the code
unauthorized. Any other method gets the HTTP status 401.
The server reads the header on every request. After sign_up, the connection opened without a key still only shows sign_up: the key must be presented. See Keys and permissions.
With a key
tools/listreturns the tools the key’s permissions open.sign_upis no longer among them.- Once the space is verified,
verifyandattach_humanleave the list. - A key that is presented but unknown or revoked gets the HTTP status 401.
A single workbench
open_workspace is the visual entry point. Once it is open, the model reuses the instance_id from this conversation’s context and asks for a page with navigate_workspace: audiences, contacts, campaigns or performance. Business tools keep returning their data without asking for a new card.
The view receives requests with workspace_events, reserved for the app by _meta.ui.visibility: ["app"]. It waits 18 seconds at most per call and keeps a single wait in progress. The model must not call this tool to keep the link alive. navigate_workspace remains visible to the model and to the app.
A pending receipt means the navigation has been requested. Input in progress can delay or prevent its display: the view lets the human choose. See Workbench navigation.
A tool’s result
A successful call returns three things.
{
"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: a readable text in French. It says who, what, when, and the remaining quota.structuredContent: the tool’s object, whose schema is published inhttps://api.subagent.fr/openapi.json. It is what a program reads.isError:false.
The result also carries _meta["subagent/requestId"], a diagnostic identifier. The space’s context metadata can accompany the business object; it serves to partition and refresh the view’s data.
The readable text cuts a message’s body at 2,000 characters. The whole body is in structuredContent.
A refusal
A refusal by the service is not a protocol error. It is a tool result marked isError, with a text: the message, the code in brackets, then the lead after an arrow.
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.
Over MCP, the same refusal also appears in structuredContent.error: code, message and, when there is one, the lead hint. A client should read these fields; the text remains available for the human. Over HTTP, the object is {"error": {"code", "message", "hint"}}. See Errors.
What each tool announces
In tools/list, each tool carries:
name,titleanddescription, in French;inputSchema: its parameters. An unknown parameter is refused.tools/listcarries no output schema (outputSchema);annotations:readOnlyHint(the tool only reads),destructiveHint(its effect cannot be undone),idempotentHint;_meta.ui.resourceUriand_meta["openai/outputTemplate"]for the visual entry pointopen_workspace;_meta.ui.visibility:modelandappin general,apponly forworkspace_events;_meta["openai/widgetAccessible"]: truefor the view’s calls, including tools without a new card.
The schema of each tool’s structured object can be read in https://api.subagent.fr/openapi.json.
The workbench uses the resource ui://subagent/workspace-v12.html. The addresses of the old views remain readable for conversations already open. After a connector update, the host must read the tool descriptors and their resource address again; an old cached descriptor may still ask for the previous cards.
Protocol
| Topic | What the server does |
|---|---|
| Versions | 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26. Any other version in the Mcp-Protocol-Version header is refused. |
| Sessions | initialize opens a session and returns the Mcp-Session-Id header. It lasts 24 hours after the last request. The session is optional: a request without this header is served statelessly. An expired session is erased after 30 days. |
| Ending a session | DELETE on https://api.subagent.fr/mcp, with the Mcp-Session-Id header. |
GET | Refused with the status 405: the server opens no stream. |
| Batches | Refused: one JSON-RPC message per request. |
| Size | A request is 1 MiB at most. |
| Origin | A request coming from another site’s web page (Origin header) is refused. |
| Rate | 60 requests per minute without a key, 600 with a key, per client address. See Limits and quotas. |
Without MCP
Every tool also exists as POST https://api.subagent.fr/v1/{operation}, with the same arguments and the same object in return. See HTTP API.