Model Context Protocol (MCP) est une interface JSON-RPC entre une application IA et un programme côté. Le programme côté offre des outils que le modèle peut appeler, des fichiers ou des enregistrements que l’application peut attacher, et des modèles de prompt que l’utilisateur peut exécuter. Ce lab définit les pièces, câble un serveur Python minimal dans Claude Code et Cursor, et énumère les limites de sécurité que la spécification énonce réellement.
À quoi sert MCP
Avant MCP, chaque hôte inventait sa propre forme de plugin. Un assistant Kubernetes écrit pour un application de bureau n’a pas pu charger dans un autre. MCP divise le travail de la façon que le Language Server Protocol a divisé les smarts de langage de l’éditeur : l’hôte reste responsable du modèle et de l’interface utilisateur ; le serveur reste responsable des données et des effets côtés.
La base de la spécification est JSON-RPC, UTF-8, avec les mêmes significations de message sur chaque transport. Un transport ne cadre que les octets. Il ne change pas ce qu’est un outil.
Trois rôles, de la protocol et du SDK Python officiel :
| Rôle | Ce que c’est | Exemple |
|---|---|---|
| Hôte | L’application LLM avec laquelle la personne parle | Claude Code, Cursor, Claude Desktop |
| Client | Le locuteur MCP à l’intérieur de l’hôte, un par serveur | Le connecteur qui lance python server.py |
| Serveur | Le processus qui énumère les outils, les ressources et les prompts | Le court programme ci-dessous |
Le serveur ne parle jamais au modèle. L’hôte décide quand mettre une définition d’outil dans le prompt et quand montrer un modèle de prompt dans l’interface utilisateur.
Outils, ressources et prompts
La spécification groupe les fonctionnalités du serveur par qui décide de les utiliser :
| Primitive | Qui est en contrôle | Ce que c’est | Exemple DevOps |
|---|---|---|---|
| Outil | Le modèle | Une fonction que le modèle peut appeler, y compris les écritures et les appels API | Chercher un nom de service |
| Ressource | L’application | Les données que le client attache en tant que contexte | Une page de runbook enregistrée |
| Prompt | L’utilisateur | Un modèle que l’utilisateur invoque, souvent comme une commande slash ou un menu | « Résumer cette note d’incident » |
Les clients peuvent aussi exposer l’élicitation : le serveur demande à l’utilisateur plus d’information. C’est optionnel. Un serveur déclare les capacités quand un client se connecte. Le MCPServer Python déclare les outils, les ressources et les prompts pour vous, et un client prudent ne demande que les familles que le serveur a annoncées (tools/list, tools/call, resources/list, resources/read, prompts/list, prompts/get).
La page MCP de Cursor dit qu’elle supporte les outils, les prompts, les ressources, les racines, l’élicitation et l’extension MCP Apps UI. Si un hôte ne peut pas dessiner une vue d’application, le résultat de l’outil fonctionne toujours en tant que contenu MCP ordinaire.
Transports
La vue d’ensemble du transport 2026-07-28 nomme deux liaisons standard :
- stdio. Le client démarre le serveur en tant que sous-processus. Les messages sont JSON-RPC délimité par des sauts de ligne sur stdin et stdout. L’annulation est une notification
notifications/cancelled. Ne print pas les logs vers stdout pendant que le serveur sert ; le SDK Python déplace la stdout ordinaire vidée vers stderr pour qu’unprintne corrompe pas le flux. Utilisezlogging, qui écrit vers stderr. - Streamable HTTP. Un point de terminaison HTTP accepte POST. Chaque message JSON-RPC est son propre POST. La réponse est soit un objet JSON unique, soit un flux Server-Sent Events limité à cette demande. Ce transport a remplacé le plus ancien transport HTTP+SSE de la révision 2024-11-05, à partir de 2025-03-26. La révision 2026-07-28 a aussi supprimé le point de terminaison de flux GET et les sessions au niveau du protocole. Les clients doivent gérer les plus vieux serveurs intentionnellement.
Le SDK Python run() accepte stdio (par défaut), streamable-http et sse. Le guide run dit que SSE fonctionne toujours pour les clients qui n’ont pas bougé, et vous dit de ne rien construire de nouveau dessus. Claude Code a toujours --transport sse et un type de sse pour les serveurs qui existent déjà, plus http pour Streamable HTTP. En JSON config, streamable-http est un alias de http. Une entrée avec une url et pas de type est une erreur : Claude Code traite un type manquant comme stdio, saute le serveur, et vous demande de définir http, sse ou ws. Cursor documente trois transports dans sa table : stdio, SSE et Streamable HTTP.
Les transports personnalisés sont autorisés s’ils gardent JSON-RPC et les modèles de message. Un socket TCP ou Unix brut devrait réutiliser le frame stdio plutôt que d’inventer un nouveau.
Un serveur Python minimal
La ligne officielle du SDK documentée à py.sdk.modelcontextprotocol.io est v2. Il a besoin de Python 3.11 ou plus nouveau. FastMCP a été renommé en MCPServer ; le nouveau code ne devrait pas importer mcp.server.fastmcp.
pip install "mcp[cli]"
uv add "mcp[cli]" est la même install. L’extra [cli] fournit la commande mcp. mcp dev a besoin de npx sur PATH parce que l’Inspector est une app Node.
Enregistrez ceci comme server.py. C’est un catalogue fermé de notes de code de sortie, pas une copie de la démo arithmétique du SDK. Les règles de décorateur correspondent au SDK : le nom de la fonction est le nom de l’outil, la chaîne de docstring est la description, et les type hints sont le schéma d’entrée.
from mcp.server import MCPServer
mcp = MCPServer("exit-notes")
NOTES = {
0: "The process finished without a reported error.",
1: "A generic failure. Read the last log lines before you retry.",
137: "128 plus signal 9. Often an external kill, including an OOM killer.",
143: "128 plus signal 15. The process was asked to terminate.",
}
@mcp.tool()
def explain_exit_code(code: int) -> str:
"""Explain a process exit code from a fixed note table."""
return NOTES.get(code, f"No note for exit code {code}.")
@mcp.resource("note://{code}")
def note(code: str) -> str:
"""Return the same note as a resource the host can attach."""
try:
key = int(code)
except ValueError:
return "Code must be an integer."
return NOTES.get(key, f"No note for exit code {key}.")
@mcp.prompt()
def incident_blurb(code: int) -> str:
"""Build a user message that asks for a one-line incident blurb."""
return (
"Write one sentence an on-call engineer can paste. "
f"Exit code: {code}. Use only the tool result. "
"If the note says you lack evidence, say so."
)
if __name__ == "__main__":
mcp.run()
mcp.run() sans argument sert stdio et bloque. La garde if __name__ importe parce que mcp dev et mcp run importent le fichier. Vérifiez-le sans hôte :
uv run mcp dev server.py
Ouvrez l’URL que l’Inspector affiche. Appelez explain_exit_code avec 137. Vous devriez obtenir la note signal-9. Un non-entier est rejeté par le SDK à partir du type hint, avant que la fonction ne s’exécute. note://137 est un modèle de ressource (le {code} est un paramètre), donc il apparaît sous les modèles, pas comme une ressource statique simple. incident_blurb apparaît sous les prompts.
Pour un serveur déployé, la commande documentée est :
uv run mcp run server.py --transport streamable-http
La page first-steps du SDK se connecte ensuite à un client à http://localhost:8000/mcp. Ne pointez cela pas vers l’Internet public sans la section d’autorisation ci-dessous.
L’ajouter à Claude Code
Exécutez ceci dans un terminal, pas dans une session claude existante. Le -- est requis. Tout ce qui suit est la commande du serveur, pas un drapeau de Claude Code.
claude mcp add --transport stdio exit-notes -- python server.py
Cela écrit le scope local : seulement vous, seulement ce projet, sous l’entrée projet dans ~/.claude.json. Sur Windows ce fichier est %USERPROFILE%\.claude.json. Le scope local est par défaut.
| Scope | Drapeau | Fichier | Qui le voit |
|---|---|---|---|
| Local | --scope local (par défaut) |
~/.claude.json, sous ce projet |
Vous, ce projet |
| Projet | --scope project |
.mcp.json dans la racine du projet |
Quiconque clone le repo, après approbation |
| Utilisateur | --scope user |
~/.claude.json, top-level mcpServers |
Vous, tous les projets |
Scope projet :
claude mcp add --scope project --transport stdio exit-notes -- python server.py
La forme .mcp.json résultante, correspondant à l’exemple HTTP documenté et aux champs stdio utilisés pour claude mcp serve, est :
{
"mcpServers": {
"exit-notes": {
"type": "stdio",
"command": "python",
"args": ["server.py"]
}
}
}
Utilisez un chemin complet vers python ou vers server.py si le PATH de la session est plus fin que votre shell. Claude Code définit CLAUDE_PROJECT_DIR dans le processus serveur à la racine du projet. Dans un .mcp.json projet, développez-le comme ${CLAUDE_PROJECT_DIR:-.} si vous le référencez dans command ou args.
À l’intérieur d’une session, /mcp énumère les serveurs. Un serveur projet reste en attente jusqu’à ce que vous l’approuviez dans une session claude interactive. Réinitialisez ces choix avec claude mcp reset-project-choices. Vérifiez un serveur avec claude mcp get exit-notes.
Un serveur Streamable HTTP distant est l’autre forme documentée :
claude mcp add --transport http exit-notes https://example.com/mcp
{
"mcpServers": {
"exit-notes": {
"type": "http",
"url": "https://example.com/mcp"
}
}
}
${VAR} et ${VAR:-default} se développent dans command, args, env, url et headers. Ne commit pas un token actif. Dans une url de serveur distant et headers, Claude Code substitue un ensemble documenté de noms de variables d’identification avec une valeur vide, donc un fichier projet ne peut pas transférer ces secrets.
Quand le même nom existe dans plus d’un scope, l’entrée entière du scope le plus élevé gagne. Les champs ne sont pas fusionnés. Local bat projet, qui bat utilisateur, puis plugins, puis connecteurs claude.ai. Une entrée managedMcpServers d’organisation surclasse ceux-ci. Les commandes Claude Code au jour le jour sont sur le cheat sheet. Un hook peut quand même bloquer un outil après que MCP l’ait exposé ; voir le hooks guide.
L’ajouter à Cursor
Cursor lit la config MCP de .cursor/mcp.json (ce projet) ou ~/.cursor/mcp.json (tous les projets). Vous pouvez aussi installer depuis Customize, ou depuis un marché d’équipe qu’un admin a publié.
La table champ STDIO marque type comme requis. command est requis. args, env et envFile sont optionnels. envFile fonctionne pour stdio seulement. Les entrées distantes utilisent url et optionnel headers. Les valeurs dans command, args, env, url et headers peuvent utiliser ${env:NAME}, ${userHome}, ${workspaceFolder}, ${workspaceFolderBasename} et ${pathSeparator}.
{
"mcpServers": {
"exit-notes": {
"type": "stdio",
"command": "python",
"args": ["${workspaceFolder}/server.py"]
}
}
}
Un serveur distant :
{
"mcpServers": {
"exit-notes": {
"url": "https://example.com/mcp",
"headers": {
"Authorization": "Bearer ${env:EXIT_NOTES_TOKEN}"
}
}
}
}
Activez le serveur sous Customize. Cursor demande avant d’exécuter un outil MCP sauf si un Run Mode l’autorise. Si le processus échoue, ouvrez le panneau Output, choisissez MCP Logs et lisez l’erreur de démarrage. Un serveur tombé ne stoppe pas les autres. Le côté CLI du même fichier est agent mcp list et agent mcp list-tools <name>.
OAuth pour les serveurs distants utilise le flux authorization-code avec PKCE. Vous pouvez fournir un client fixe dans un objet auth (CLIENT_ID, optionnel CLIENT_SECRET, optionnel scopes) quand le fournisseur ne supporte pas l’enregistrement de client dynamique. Enregistrez les deux URLs de redirection que les docs énumèrent si les gens se connectent depuis le web et depuis l’app de bureau : https://www.cursor.com/agents/mcp/oauth/callback et http://localhost:8787/callback.
Sécurité
Le modèle de confiance de Stdio est celui à retenir. L’hôte lance le serveur. Les deux peuvent s’exécuter avec les mêmes privilèges du système d’exploitation. Le SDK ne défend aucun côté contre l’autre. Un dialogue de consentement aide seulement si vous lisez la commande. Les conséquences pratiques, des notes de sécurité du serveur local :
- Décidez avant le lancement : qui a publié le serveur, et si vous l’isolerez. Le transport ne le fera pas pour vous.
- Chaque serveur dans la session partage le contexte du modèle. Un mauvais serveur peut diriger comment l’agent utilise les autres outils.
- Passez les secrets sur l’
envde ce serveur (ou l’envde Cursor /${env:NAME}), pas depuis un profil shell que chaque processus hérite. - Préférez un token étroitement limité. Gardez les secrets de longue durée en dehors du JSON si le client peut les lire depuis le magasin secret du système d’exploitation.
- Un conteneur ou une VM est votre limite d’isolation si le serveur est tiers.
L’autorisation HTTP est optionnelle dans la spécification. Quand vous l’utilisez, suivez la spécification d’autorisation HTTP. Les implémentations Stdio ne devraient pas utiliser ce flux OAuth ; elles devraient prendre les identifiants de l’environnement. Sur tout serveur HTTP : HTTPS sauf localhost en développement, tokens d’accès de courte durée, scopes d’une moins grande privilège, et pas de tokens dans les logs.
Le .mcp.json projet de Claude Code est partagé intentionnellement. Traitez un args array commit comme un script commit. La propre liste de Cursor correspond à ceci : installer depuis une source que vous fiez, lire ce que le serveur peut atteindre, restreindre les clés API et examiner le code avant de le connecter aux systèmes de production.
Une Custom GPT Action est un connecteur différent : un document OpenAPI dans ChatGPT, pas un serveur MCP. n8n peut attacher un serveur MCP de registre comme outil sur un nœud AI Agent ; construisez d’abord le workflow, puis ajoutez l’agent, comme dans le first n8n workflow.
FAQ
Quel problème MCP résout-il ?
MCP est une façon standard pour une application IA d’accéder aux outils et au contexte. Vous écrivez un serveur une fois. Claude Code, Cursor et d’autres hôtes parlent le même protocole JSON-RPC au lieu d’un format de plugin privé pour chaque produit.
Quelle est la différence entre un hôte, un client et un serveur ?
L’hôte est l’application que la personne utilise, comme Claude Code ou Cursor. Le client est la pièce à l’intérieur de l’hôte qui parle MCP, un client par serveur. Le serveur est le programme que vous exécutez. Il expose des outils, des ressources et des prompts. Il n’appelle pas le modèle lui-même.
Quels transports MCP devrais-je utiliser ?
La spécification actuelle définit stdio et Streamable HTTP. Stdio est pour un serveur que l’hôte lance sur votre machine. Streamable HTTP est un seul point de terminaison POST pour un serveur que vous déployez. SSE est le plus ancien transport HTTP. Le SDK Python le lance toujours, et vous dit de ne rien construire de nouveau dessus.
Comment j’ajoute un serveur MCP à Claude Code et à Cursor ?
Claude Code : claude mcp add --transport stdio <name> -- <command> [args], ou un fichier .mcp.json avec une entrée mcpServers. Cursor : .cursor/mcp.json dans le projet, ou ~/.cursor/mcp.json pour chaque projet, avec command, args et env pour un processus local, ou url pour HTTP ou SSE.
Un serveur MCP local est-il en boîte à sable ?
Non. Stdio signifie que l’hôte démarre votre programme en tant que processus enfant. Les notes de sécurité officielles disent que le client et ce processus partagent un domaine de confiance. Le SDK n’isole pas le serveur. L’isolation, si vous la voulez, est un conteneur, une VM ou les propres invites d’approbation de l’hôte.
Comment ce guide a été vérifié
Rédigé à la main à partir de la documentation officielle, chaque commande revérifiée, puis relu une seconde fois avant publication. Dernière relecture le .
Sources officielles consultées pour cet article
- modelcontextprotocol.io/specification/2026-07-28
- modelcontextprotocol.io/specification/2026-07-28/server
- modelcontextprotocol.io/specification/2026-07-28/basic/transports
- modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http
- modelcontextprotocol.io/specification/2026-07-28/basic/authorization
- modelcontextprotocol.io/docs/2026-07-28/tutorials/security/local-server-security
- py.sdk.modelcontextprotocol.io/
- py.sdk.modelcontextprotocol.io/get-started/first-steps/
- py.sdk.modelcontextprotocol.io/run/
- code.claude.com/docs/en/mcp
- cursor.com/docs/mcp

