Brancher Claude à tes outils avec le MCP
Exposer un premier outil en une trentaine de lignes, le tester, puis le brancher à Claude Code.
Un modèle de langage, seul, ne sait rien faire d'autre que produire du texte. Le MCP (Model Context Protocol) est la prise standard qui lui donne des mains : lire une base, appeler une API interne, écrire un fichier. Une fois le serveur branché, Claude voit tes outils comme s'ils faisaient partie de lui.
Ce guide monte un serveur minimal, le teste, puis le branche à Claude Code. Compte une vingtaine de minutes.
Ce que fait un serveur MCP
Un serveur MCP expose trois choses :
- des outils, que le modèle peut appeler (
chercher_client,créer_ticket) ; - des ressources, qu'il peut lire (un fichier, une table) ;
- des prompts, des modèles de conversation prêts à l'emploi.
En pratique, 90 % des serveurs n'exposent que des outils. C'est ce qu'on fait ici.
Le MCP ne remplace pas une API
Le serveur MCP est une couche de traduction devant ce que tu as déjà. Si ton outil est une API REST, le serveur se contente de l'appeler et de mettre en forme la réponse pour le modèle.
Avant de commencer
- Node.js 20 ou plus récent (
node --version) - Claude Code installé et fonctionnel
- Un dossier vide pour le projet
1Installer le SDK
mkdir mcp-meteo && cd mcp-meteo
npm init -y
npm install @modelcontextprotocol/server zod
Le paquet s'appelle @modelcontextprotocol/server depuis la version 2. Si tu
tombes sur des exemples qui importent @modelcontextprotocol/sdk, ils datent de
la v1 et utilisent server.tool(...) au lieu de server.registerTool(...).
2Écrire le serveur
Crée serveur.js. Un seul outil, qui renvoie la température d'une ville :
import { McpServer } from "@modelcontextprotocol/server";
import { serveStdio } from "@modelcontextprotocol/server/stdio";
import * as z from "zod/v4";
serveStdio(() => {
const server = new McpServer({ name: "meteo", version: "1.0.0" });
server.registerTool(
"temperature_ville",
{
description:
"Renvoie la temperature actuelle d'une ville, en degres Celsius.",
inputSchema: z.object({
ville: z.string().describe("Nom de la ville, par exemple Lyon"),
}),
},
async ({ ville }) => {
const geo = await fetch(
`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(ville)}&count=1`
).then((r) => r.json());
const lieu = geo.results?.[0];
if (!lieu) {
return {
content: [{ type: "text", text: `Ville introuvable : ${ville}` }],
isError: true,
};
}
const meteo = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${lieu.latitude}&longitude=${lieu.longitude}¤t=temperature_2m`
).then((r) => r.json());
return {
content: [
{
type: "text",
text: `${lieu.name} : ${meteo.current.temperature_2m} degres`,
},
],
};
}
);
return server;
});
Ajoute "type": "module" dans ton package.json, sinon Node refuse les
import.
La description est la partie qui compte
Le modèle choisit un outil sur la seule foi de sa description. « Renvoie la température actuelle d'une ville » se déclenche au bon moment. « Outil météo » ne se déclenchera jamais, ou n'importe quand.
3Tester sans Claude
Un serveur stdio parle du JSON-RPC sur l'entrée standard. L'inspecteur officiel évite d'avoir à le faire à la main :
npx @modelcontextprotocol/inspector node serveur.js
Une interface s'ouvre dans le navigateur. Onglet Tools, tu vois
temperature_ville, tu l'appelles avec {"ville": "Lyon"} et tu lis la réponse.
Tant que ça ne marche pas ici, inutile de brancher Claude : le problème est dans ton serveur, pas dans la connexion.
4Brancher à Claude Code
claude mcp add meteo -- node /chemin/absolu/vers/serveur.js
Relance Claude Code, puis vérifie :
claude mcp list
Ton serveur doit apparaître avec le statut connecté. Demande ensuite quelque chose qui a besoin de l'outil :
Quelle temperature fait-il a Lyon en ce moment ?
Claude appelle temperature_ville, te montre l'appel, et répond avec la vraie
valeur.
Les erreurs qui coûtent une heure
| Symptôme | Cause presque toujours |
|---|---|
Le serveur n'apparaît pas dans claude mcp list | Chemin relatif au lieu d'absolu dans claude mcp add |
| « Unexpected token » au démarrage | "type": "module" manquant dans package.json |
| L'outil existe mais Claude ne l'appelle jamais | Description trop vague, ou qui ne dit pas quand s'en servir |
| Le serveur se coupe tout seul | Un console.log dans le code : stdout est réservé au protocole, écris sur console.error |
Le réflexe de débogage
Un serveur stdio ne doit rien écrire sur la sortie standard en dehors du
protocole. Tous tes logs passent par console.error, qui part sur stderr et
n'interfère pas.
Aller plus loin
Une fois ce squelette en place, la suite est mécanique : chaque nouvel outil est
un registerTool de plus. Les questions qui deviennent intéressantes sont
ailleurs.
- Découper. Un serveur avec quarante outils sature le contexte du modèle. Mieux vaut plusieurs serveurs thématiques, activés selon le projet.
- Renvoyer peu. Un outil qui renvoie 3 000 lignes de JSON coûte cher et noie la réponse. Filtre côté serveur, pas côté modèle.
- Échouer clairement.
isError: trueavec un message explicite permet au modèle de se corriger. Une exception silencieuse le laisse deviner.
Tu as d’autres ressources comme ça ?
Ce que tu viens de lire est entièrement gratuit. Une nouvelle ressource part chaque semaine dans la newsletter : guides, modèles à copier et liens triés, sans compte à créer.
Me contacter
Une question, un projet, une envie de collaborer ? Écris-moi.
Cette page est en accès libre, n’hésite pas à la partager.