MCP — Le protocole
Le Model Context Protocol est un standard JSON-RPC 2.0 publié par Anthropic en 2024. Il décrit comment un client LLM (Claude Desktop, Cursor, etc.) découvre et exécute des tools, lit des resources, et instancie des prompts exposés par un serveur tiers. Trackily implémente le transport "streamable HTTP" sur
/mcp.
Pourquoi un protocole standardisé
Avant MCP, chaque agent LLM avait son propre format pour exposer des capabilities :
- OpenAI : "function calling" JSON-Schema spécifique
- Anthropic : "tool use" Anthropic-flavored
- Google : "function declarations" Gemini-style
- LangChain : structured tools avec décorateurs Python
- Cursor / Zed / etc : intégrations ad-hoc
Conséquence : si tu écrivais un outil pour Cursor, tu devais le réécrire pour Claude Desktop, pour ton script Python, pour Zed… MCP unifie ça. Tu écris un serveur une fois, et tous les clients qui parlent MCP peuvent s'y connecter sans modification.
Côté serveur, MCP est explicite sur trois concepts :
- Resources — données read-only adressées par URI (
trackily://stats/dashboard,trackily://campaign/42). Le client peut les lire sans paramètre, comme un GET sur un fichier. - Tools — fonctions appelables, identifiées par un nom, avec un JSON-Schema décrivant les arguments. Peuvent muter l'état.
- Prompts — templates de conversation paramétrables, exposés comme des "slash commands" côté client (ex:
/brief,/scale-winners).
JSON-RPC 2.0 — les bases
JSON-RPC est un protocole RPC minimaliste sur JSON. Une requête :
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": {
"name": "list_campaigns",
"arguments": { "limit": 5, "is_active": true }
}
}
Une réponse réussie :
{
"jsonrpc": "2.0",
"id": 42,
"result": {
"content": [
{ "type": "text", "text": "{ \"campaigns\": [...] }" }
],
"isError": false
}
}
Une réponse en erreur :
{
"jsonrpc": "2.0",
"id": 42,
"error": {
"code": -32602,
"message": "Missing tool name"
}
}
Codes d'erreur utilisés par Trackily :
| Code | Sens |
|---|---|
-32700 |
Parse error (JSON invalide) |
-32600 |
Invalid request (pas du JSON-RPC 2.0) |
-32601 |
Method not found (méthode inconnue OU tool/resource inconnue) |
-32602 |
Invalid params (paramètres manquants / mal typés) |
-32603 |
Internal error (exception non gérée côté serveur) |
-32001 |
Unauthorized (token manquant / invalide / expiré) |
-32002 |
Forbidden — missing scope (le tool nécessite un scope que le token n'a pas) |
-32003 |
Rate limited |
Transport
Deux transports MCP existent :
- stdio — le client lance le serveur comme un sous-process et communique sur stdin/stdout. Pratique pour les serveurs locaux (filesystem, calculatrice…).
- streamable HTTP — le client POST sur un endpoint HTTP, le serveur répond en JSON. Optionnellement Server-Sent Events pour le streaming.
Trackily implémente streamable HTTP : ton instance est de toute façon déjà accessible sur le réseau, et tu ne veux pas que Claude Desktop spawn un sous-process pour t'y connecter.
Endpoint : POST /mcp sur ton domaine Trackily (ex: https://trackily.online/mcp).
Header d'auth : Authorization: Bearer tly_ap_<your_token>. Alternativement, ?token=tly_ap_… en query string (utile pour les clients qui ne savent pas set des headers en transport streamable).
Handshake initial
Le client commence toujours par initialize :
// Client → Server
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "claude-desktop", "version": "1.4.2" }
}
}
// Server → Client
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"resources": { "subscribe": false, "listChanged": false },
"tools": { "listChanged": false },
"prompts": { "listChanged": false },
"logging": {}
},
"serverInfo": {
"name": "Trackily Autopilot",
"version": "1.0.0"
},
"instructions": "Trackily Autopilot — AI copilot for a self-hosted affiliate marketing tracker.\nOperator is French-speaking (reply in French unless asked otherwise).\n\nQUICK ROUTING — When the operator says...\n \"mes stats\", \"KPIs\", \"dashboard\" → read resource trackily://stats/dashboard\n ..."
}
}
Le champ instructions est important : c'est un grand bloc de texte que le serveur envoie au client, qui le injecte directement dans le system prompt du LLM. C'est là que Trackily explique au LLM les patterns courants ("quand l'opérateur dit X, appelle Y"), le contexte (operateur francophone, Tier-2, etc.). Tu peux le voir dans autopilot.js autour de la ligne 600.
Puis le client envoie la notification initialized (pas de réponse attendue) :
{ "jsonrpc": "2.0", "method": "notifications/initialized" }
À partir de là, le client peut appeler les méthodes du protocole.
Méthodes du protocole
Resources
// List
{ "jsonrpc": "2.0", "id": 2, "method": "resources/list" }
// Read
{ "jsonrpc": "2.0", "id": 3, "method": "resources/read",
"params": { "uri": "trackily://stats/dashboard" } }
Les resources Trackily disponibles (chaque token voit seulement celles dont il a le scope) :
trackily://stats/dashboard— KPIs globauxtrackily://campaign/<id>— détail campagnetrackily://offer/<id>— détail offretrackily://landing/<id>— détail landingtrackily://flow/<id>— détail flowtrackily://settings— config publique- et d'autres listées dans
autopilot-resources.js
Tools
// List
{ "jsonrpc": "2.0", "id": 4, "method": "tools/list" }
// Call
{ "jsonrpc": "2.0", "id": 5, "method": "tools/call",
"params": {
"name": "list_campaigns",
"arguments": { "limit": 5 }
}
}
tools/list retourne les 181 tools (filtrés par scope du token), chacun avec son name, description, inputSchema. Le LLM utilise ces métadonnées pour décider quel tool appeler en fonction de ce que dit l'utilisateur.
Prompts
// List
{ "jsonrpc": "2.0", "id": 6, "method": "prompts/list" }
// Get (instancie le prompt avec des arguments → retourne un dialogue à injecter)
{ "jsonrpc": "2.0", "id": 7, "method": "prompts/get",
"params": {
"name": "brief",
"arguments": { "window_hours": 24 }
}
}
Trackily expose ~12 prompts, vus comme des slash commands côté Claude Desktop : /brief, /check, /pause-losers, /scale-winners, etc. Chacun chaîne plusieurs tools avec les bons defaults pour un workflow opérationnel donné.
Batched requests
JSON-RPC supporte les batches (array de requests). Trackily les gère :
[
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": { "name": "list_campaigns", "arguments": {} } },
{ "jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": { "name": "list_offers", "arguments": {} } }
]
Réponse : array dans le même ordre. Utile pour réduire le RTT quand un client a plusieurs reads indépendants à faire d'un coup.
Probing l'endpoint
Un GET /mcp (sans le POST JSON-RPC) retourne un payload de santé :
{
"name": "Trackily Autopilot",
"version": "1.0.0",
"protocolVersion": "2025-06-18",
"status": "ok",
"docs": "/admin#autopilot"
}
Utile pour curl https://trackily.online/mcp et vérifier que l'endpoint est joignable, sans avoir besoin d'un token.
Différences entre MCP et REST classique
| Aspect | REST | MCP |
|---|---|---|
| Découverte | OpenAPI spec à parser séparément | tools/list retourne tout en JSON-Schema |
| Auth | varié (bearer, JWT, OAuth, etc.) | bearer simple, géré par le client |
| Erreurs | HTTP status + body custom | codes JSON-RPC unifiés |
| Streaming | SSE / WebSocket / poll | streamable HTTP natif |
| Composition | client compose à la main | LLM compose grâce aux descriptions |
| Confirmation | géré côté client | Tier-2 codifié dans le contrat |
MCP n'est pas "mieux" que REST en général — il est mieux pour les agents LLM. Trackily expose les deux : MCP pour les agents, REST/HTTP pour les intégrations classiques (cf. API).
Voir aussi
- Tokens — créer le bearer token
- Scopes — limiter ce qu'un token peut faire
- Tier System — confirm_token pour les destructives
- Tools Reference — les 181 tools listés
- Spec officielle MCP