Brancher son API métier
Exposer l'API interne de son entreprise à un agent via MCP : trois outils, en lecture seule, qui ne renvoient que l'utile.
Ton entreprise a déjà une API : le CRM, la facturation, le stock, le suivi de production. L'agent, lui, ne la voit pas. Il répond « je n'ai pas accès à ces informations » pendant que la réponse existe à deux appels HTTP de là.
Le réflexe naturel est de tout exposer d'un coup : cinquante endpoints, un serveur MCP généré depuis le schéma OpenAPI, et on verra bien. C'est le meilleur moyen d'obtenir un agent qui choisit mal ses outils, sature son contexte avec du JSON inutile, et finit par modifier une fiche client par erreur.
Ce guide fait l'inverse. Trois outils, en lecture seule, qui renvoient dix lignes chacun. On étend après, quand on sait ce qui sert vraiment.
Le périmètre minimal
Avant d'écrire une ligne, note les questions que les gens posent réellement à l'équipe qui a accès à l'outil. Pas les fonctions de l'API : les questions. « Où en est la commande de Dupont ? », « Combien de tickets ouverts sur le module paiement ? », « Ce client est-il à jour de ses factures ? »
Trois questions, trois outils. Un endpoint qui ne répond à aucune question formulée par un humain n'a rien à faire dans le serveur.
Un outil n'est pas un endpoint
GET /customers/{id} est un endpoint. statut_commande(numero) est un outil.
Le second peut appeler trois endpoints et n'en restituer que le résultat utile.
Traduire, c'est tout l'intérêt de la couche MCP.
Avant de commencer
- Node.js 20 ou plus récent
- Une API interne joignable, et un jeton de lecture
- Trois questions métier écrites noir sur blanc
1Créer le serveur
mkdir mcp-metier && cd mcp-metier
npm init -y
npm install @modelcontextprotocol/server zod
Ajoute "type": "module" dans package.json. Le paquet @modelcontextprotocol/server
est la version 2 du SDK TypeScript ; les exemples qui importent
@modelcontextprotocol/sdk datent de la v1.
2Un outil, en lecture seule
import { McpServer } from "@modelcontextprotocol/server";
import { serveStdio } from "@modelcontextprotocol/server/stdio";
import * as z from "zod/v4";
const BASE = process.env.API_BASE_URL;
const JETON = process.env.API_TOKEN;
serveStdio(() => {
const server = new McpServer({ name: "metier", version: "1.0.0" });
server.registerTool(
"statut_commande",
{
description:
"Renvoie l'etat d'avancement d'une commande a partir de son numero. " +
"A utiliser des qu'on demande ou en est une commande.",
inputSchema: z.object({
numero: z.string().describe("Numero de commande, format CMD-12345"),
}),
annotations: { readOnlyHint: true },
},
async ({ numero }) => {
const r = await fetch(`${BASE}/orders/${encodeURIComponent(numero)}`, {
headers: { Authorization: `Bearer ${JETON}` },
});
if (r.status === 404) {
return {
content: [{ type: "text", text: `Commande introuvable : ${numero}` }],
isError: true,
};
}
const c = await r.json();
return {
content: [
{
type: "text",
text: [
`Commande ${c.reference}`,
`Statut : ${c.status}`,
`Expedition prevue : ${c.shipping_date ?? "non planifiee"}`,
`Montant : ${c.total_amount} EUR`,
].join("\n"),
},
],
};
}
);
return server;
});
Deux choses méritent d'être notées. annotations: { readOnlyHint: true } déclare
que l'outil ne modifie rien : c'est une indication de comportement, pas une
garantie technique, la vraie garantie vient du jeton de lecture seule. Et
isError: true renvoie une erreur que le modèle peut lire et corriger, au lieu
d'une exception qui le laisse deviner.
3Filtrer ce qui sort, pas ce qui entre
C'est le point que presque tout le monde rate. Un return JSON.stringify(c) sur
une fiche client, c'est deux cents champs envoyés au modèle : identifiants
internes, horodatages techniques, adresse, RIB, historique complet. Coût en
jetons, bruit dans la réponse, et fuite de données qui n'avaient rien à faire là.
La règle : le serveur choisit les champs, un par un, explicitement. Si tu ne peux pas nommer les cinq champs utiles, c'est que l'outil n'est pas assez précis.
Le filtrage côté modèle n'existe pas
« Le modèle ne montrera que ce qui est pertinent » est faux : tout ce que l'outil renvoie entre dans le contexte, donc dans les journaux du fournisseur, et peut ressortir dans une réponse. Ce qui ne doit pas sortir de ton système ne doit pas sortir de ton serveur.
4Brancher sans mettre le jeton dans un prompt
Les identifiants passent par l'environnement du processus, jamais dans le texte de la conversation :
claude mcp add --env API_BASE_URL=https://api.interne.local \
--env API_TOKEN=xxxxx \
--transport stdio metier -- node /chemin/absolu/serveur.js
Vérifie ensuite :
claude mcp list
Le serveur doit apparaître comme connecté. Dans un fichier .mcp.json partagé
avec l'équipe, référence les variables au lieu des valeurs, avec la syntaxe
${API_TOKEN} : chacun fournit son propre jeton, et le fichier reste
commitable.
5Passer en écriture, plus tard, et jamais en silence
Quand un outil d'écriture devient nécessaire, trois règles.
- Une action précise, pas une action générique.
annuler_commande(numero, motif)plutôt quemodifier_commande(numero, champs). - Un compte de service dédié, avec les seuls droits de cette action, distinct du compte de lecture.
- Une trace, côté API : qui a appelé, quand, avec quels arguments. Les journaux du modèle ne suffisent pas.
Et tant que possible, garde l'écriture sur un environnement de test le temps de vérifier que le modèle appelle l'outil au bon moment.
Quelques prompts pour aller plus loin
| Objectif | Prompt à adapter |
|---|---|
| Définir le périmètre | Voici 30 questions posees a notre equipe support ce mois-ci. Regroupe-les et propose au maximum 5 outils qui couvriraient 80 % des cas. Pour chacun : nom, arguments, champs a renvoyer. |
| Traduire un endpoint | Voici la reponse JSON de [ENDPOINT]. Selectionne les 5 champs utiles pour repondre a « [QUESTION] » et ecris la mise en forme texte. Justifie chaque champ ecarte. |
| Écrire les descriptions | Voici mes 4 outils. Reecris chaque description pour qu'elle dise quand l'utiliser et quand ne pas l'utiliser, en une ou deux phrases. |
| Chasser les fuites | Relis ce serveur MCP. Pour chaque outil, liste les champs renvoyes au modele et signale ceux qui sont des donnees personnelles ou des identifiants internes. |
| Tester le déclenchement | Voici la liste de mes outils avec leurs descriptions. Pour chacune de ces 10 questions, dis quel outil tu appellerais, ou aucun. Ne les appelle pas. |
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.