Créer un serveur MCP pour vos outils

Besoin de parler avec un expert ?

Contactez un expert

Créer un serveur MCP pour vos outils

4 septembre 2026
Temps de lecture : 12 min

Écrire un serveur MCP prend une soirée. Le faire servir à quelque chose prend une semaine. Sur les projets où nous branchons un agent sur l'ERP ou le CRM d'un client, le code du serveur occupe environ une journée, et tout le reste part dans les allers-retours sur la définition des outils : comment les nommer, quoi renvoyer, ce qu'on refuse d'exposer.

La bascule du 28 juillet 2026 ajoute une raison de s'y remettre sérieusement. Un serveur écrit en 2025 s'appuie sur des mécanismes que le protocole vient de retirer ou de déprécier, et les tutoriels qui traînent en ligne décrivent encore un protocole à sessions qui n'existe plus.

Ce que la révision 2026-07-28 change pour votre serveur

MCP est devenu un protocole sans état. Le journal officiel des changements acte la disparition du handshake initialize et de l'en-tête Mcp-Session-Id : chaque requête transporte désormais sa version de protocole et les capacités du client dans le champ _meta. La révision précédente, 2025-11-25, gardait une connexion vivante entre client et serveur ; la nouvelle ressemble à une API HTTP ordinaire, où deux appels successifs ne partagent rien.

La liste des changements de la révision détaille des retraits qui touchent directement le code d'un serveur. Les méthodes ping et logging/setLevel n'existent plus. Le point d'entrée HTTP GET et le couple resources/subscribe / resources/unsubscribe sont remplacés par un unique subscriptions/listen. Une nouvelle méthode server/discover devient obligatoire : le serveur y annonce les versions de protocole qu'il accepte, ses capacités et son identité. Et les requêtes initiées par le serveur, comme sampling/createMessage ou elicitation/create, cèdent la place au motif Multi Round-Trip Requests : le serveur renvoie un résultat de type input_required, le client rejoue la même requête en y joignant sa réponse.

Quatre fonctionnalités passent en dépréciation formelle : Roots, Sampling, Logging et l'enregistrement dynamique de client OAuth. Le transport HTTP+SSE, déprécié depuis 2025-03-26, entre dans le même registre. Les calendriers, eux, diffèrent. Le registre officiel des fonctionnalités dépréciées fixe le retrait au plus tôt des quatre premières à la première révision publiée à partir du 28 juillet 2027, soit douze mois, tandis que HTTP+SSE devient éligible au retrait trois mois après le passage de sa proposition SEP-2596 à l'état final. Ce registre précise aussi que la date ouvre seulement l'éligibilité, le retrait effectif restant une décision des mainteneurs prise en préparation de version. Les migrations conseillées sont simples : passer les répertoires en paramètres d'outil plutôt que par Roots, appeler directement l'API du fournisseur de modèle plutôt que Sampling, et écrire ses logs sur stderr ou en OpenTelemetry plutôt que via le canal de logging du protocole.

Si le vocabulaire du protocole vous manque, notre article sur le rôle du Model Context Protocol pose les bases avant d'attaquer le code ci-dessous.

Schéma d'un serveur MCP placé entre une application IA et les outils internes d'une entreprise
Le serveur MCP expose vos systèmes internes sous forme d'outils appelables par un modèle

Un serveur Python, du dossier vide au premier outil

Le socle Python demande le SDK officiel mcp, en version 2.1.1 depuis le 25 août 2026 d'après sa page PyPI, et Python 3.10 au minimum. La documentation officielle exige la version 2.0.0 ou supérieure : les serveurs qui importaient FastMCP depuis le SDK 1.x ne se contentent pas d'une mise à jour de dépendance.

uv init crm-mcp
cd crm-mcp
uv venv && source .venv/bin/activate
uv add "mcp[cli]"

La même page décrit le mécanisme central du SDK : la classe MCPServer lit vos annotations de type et vos docstrings pour fabriquer le schéma de chaque outil. Vous n'écrivez donc pas de JSON Schema à la main, mais vos docstrings deviennent du prompt : elles partent telles quelles dans le contexte du modèle.

from typing import Any

import httpx2
from mcp.server import MCPServer

mcp = MCPServer("crm")

API_BASE = "https://interne.exemple.fr/api"


@mcp.tool()
async def factures_impayees(client_id: str, jours_min: int = 30) -> str:
    """Liste les factures impayées d'un client au-delà d'un certain retard.

    Args:
        client_id: identifiant interne du client, format CLI-00000
        jours_min: retard minimum en jours, 30 par défaut
    """
    async with httpx2.AsyncClient() as http:
        reponse = await http.get(
            f"{API_BASE}/factures",
            params={"client": client_id, "retard_min": jours_min},
            timeout=20.0,
        )
        reponse.raise_for_status()
        factures = reponse.json()["resultats"][:20]

    if not factures:
        return "Aucune facture impayée pour ce client."

    return "\n".join(
        f'{f["numero"]} | {f["montant_ht"]} EUR HT | {f["jours_retard"]} jours de retard'
        for f in factures
    )


if __name__ == "__main__":
    mcp.run(transport="stdio")

Une règle prime sur toutes les autres en transport stdio : n'écrivez jamais sur la sortie standard. Un print() égaré corrompt le flux JSON-RPC et coupe le serveur, sans message d'erreur exploitable. Le module logging de la bibliothèque standard écrit sur stderr, utilisez-le partout, avec un logger par module obtenu par logging.getLogger(__name__). Cette erreur revient sur presque tous les premiers serveurs que nous relisons.

Notez aussi le [:20] et le retour en texte compact plutôt qu'en JSON brut. Un outil qui déverse deux mille lignes dans le contexte fait payer chaque appel suivant, et le modèle lit moins bien un tableau JSON complet qu'une liste de vingt lignes lisibles.

La version TypeScript et le changement de paquet

En TypeScript, le paquet à installer n'est plus @modelcontextprotocol/sdk : la bibliothèque a été scindée, et la documentation officielle fait désormais installer @modelcontextprotocol/server, avec Node.js 20 ou supérieur et zod pour décrire les entrées. Le protocole a pris une place que ces renommages ne laissent pas deviner : dans l'annonce de la révision, les mainteneurs évoquent près d'un demi-milliard de téléchargements par mois sur l'ensemble des SDK de premier rang, les SDK TypeScript et Python ayant chacun franchi le milliard de téléchargements cumulés.

npm install @modelcontextprotocol/server zod
npm install -D @types/node typescript

La déclaration d'un outil passe par registerTool, avec un schéma zod qui sert à la fois de validation et de documentation pour le modèle. Les contraintes que vous posez sur les champs, longueur, bornes numériques, énumérations, réduisent d'autant les appels invalides que l'agent tentera.

import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";

const server = new McpServer({ name: "crm", version: "1.0.0" });

server.registerTool(
  "factures_impayees",
  {
    description: "Liste les factures impayées d'un client au-delà d'un retard donné",
    inputSchema: z.object({
      clientId: z.string().regex(/^CLI-\d{5}$/).describe("Identifiant interne du client"),
      joursMin: z.number().int().min(0).max(365).default(30),
    }),
  },
  async ({ clientId, joursMin }) => {
    const lignes = await chercherFactures(clientId, joursMin);
    return { content: [{ type: "text", text: lignes.join("\n") }] };
  },
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Serveur CRM MCP démarré sur stdio");
}

main().catch((error) => {
  console.error("Erreur fatale :", error);
  process.exit(1);
});

Le piège du print() Python a son équivalent ici : console.log() écrit sur la sortie standard et casse le protocole. Tout ce qui relève de la trace passe par console.error(). Pensez également à compiler avant de tester, un serveur déclaré sur un build/index.js absent échoue au démarrage sans rien dire au client.

Brancher le serveur sur un client, puis le déboguer

Un client déclare un serveur MCP dans un fichier JSON, sous la clé mcpServers, et le lance lui-même au démarrage. La procédure officielle situe ce fichier, pour Claude Desktop, dans ~/Library/Application Support/Claude/claude_desktop_config.json sur macOS et dans %APPDATA%\Claude\claude_desktop_config.json sur Windows.

{
  "mcpServers": {
    "crm": {
      "command": "uv",
      "args": ["--directory", "/chemin/absolu/vers/crm-mcp", "run", "serveur.py"]
    }
  }
}

Deux détails font échouer la moitié des premières tentatives. Les chemins doivent être absolus, sans raccourci ni variable d'environnement. Et le binaire lui-même mérite souvent son chemin complet, celui que renvoie which uv, parce que le client ne démarre pas avec le PATH de votre terminal. Après modification, quittez complètement l'application avant de la relancer.

Quand un serveur refuse de se connecter, les journaux répondent plus vite que la lecture du code. La même page indique où les trouver sur macOS : ~/Library/Logs/Claude/mcp.log retrace les connexions, et un fichier mcp-server-crm.log reprend tout ce que votre serveur a écrit sur stderr. L'autre outil à garder sous la main est l'Inspector officiel : lancé par npx @modelcontextprotocol/inspector, il ouvre une interface qui appelle vos outils un par un, sans passer par un modèle. Tester un outil qui échoue face à un agent bavard coûte dix fois plus de temps.

Les clients en ligne de commande suivent la même logique de déclaration. Si vous travaillez avec Claude Code, un serveur MCP maison devient rapidement le moyen le plus court de donner à l'agent l'accès à vos bases internes sans lui ouvrir un shell entier.

Cinq outils bien nommés valent mieux que quarante endpoints

Un serveur qui recopie votre API REST endpoint par endpoint produit un agent lent, cher et imprécis. Les définitions d'outils occupent le contexte du modèle à chaque appel, et le coût grimpe vite : Anthropic a mesuré un passage de 150 000 tokens à 2 000 tokens, soit 98,7 % d'économie, en faisant découvrir les outils à la demande au lieu de tous les charger d'avance, dans son article Code execution with MCP du 4 novembre 2025. Ce chiffre vaut pour des agents branchés sur des dizaines de serveurs, mais la mécanique reste vraie à petite échelle.

La bonne unité de découpage est l'intention métier, pas la table de base. Un outil factures_impayees vaut mieux que lister_factures assorti de huit filtres optionnels : l'agent choisit correctement parmi cinq verbes clairs, il se trompe régulièrement en assemblant des paramètres. Dans nos projets, un serveur qui dépasse une dizaine d'outils signale presque toujours qu'on a exposé un schéma de données au lieu d'un usage.

Quatre décisions structurent la qualité d'un serveur, et aucune n'est technique.

  • Ce que l'outil renvoie : du texte court et déjà agrégé, avec une limite dure sur le nombre de lignes.
  • Ce qu'il refuse : les identifiants d'autres clients, les champs sensibles, les exports massifs.
  • Ce qui écrit : chaque outil qui modifie une donnée mérite une description qui le dit franchement, parce que le client affiche cette phrase à l'utilisateur avant de demander son accord.
  • Ce qui se répète : un appel rejoué deux fois ne doit pas créer deux commandes, l'agent réessaie plus souvent qu'un humain.

La description compte autant que le code. Elle est lue par le modèle à chaque tour, c'est donc le seul endroit où vous pouvez écrire qu'un identifiant suit le format CLI-00000 ou qu'une recherche vide signifie « client inconnu » et non « aucune facture ». Nos réflexes de conception d'outils rejoignent ceux que nous appliquons pour un agent IA sur mesure en entreprise.

Passer en HTTP : ce que le mode distant ajoute vraiment

Le passage en Streamable HTTP ouvre trois chantiers que stdio vous épargnait : l'authentification, le multi-utilisateur et la gestion explicite de l'état. Dans la définition du transport, le serveur expose un seul point d'entrée qui accepte des POST, chaque requête JSON-RPC part dans son propre POST, et la réponse arrive soit en JSON, soit en flux SSE limité à cette requête. La révision 2026-07-28 impose au passage les en-têtes Mcp-Method et Mcp-Name, pour qu'une passerelle ou un limiteur de débit route sans ouvrir le corps du message.

Comparaison des transports stdio et HTTP pour un serveur MCP
stdio pour un usage local, HTTP dès que plusieurs personnes se connectent
CritèrestdioStreamable HTTP
Qui l'utiliseUne personne, sur sa machinePlusieurs utilisateurs, à distance
AuthentificationAucune, le processus hérite de vos droitsOAuth, validation d'audience obligatoire
État entre appelsLe processus vitHandles explicites passés en paramètres
Reprise après coupureSans objetRequête à rejouer avec un nouvel identifiant
DéploiementUn fichier de configurationHébergement, TLS, supervision

Sources du tableau : la page des transports et la spécification d'autorisation, consultées le 4 septembre 2026.

La disparition des sessions a une conséquence concrète sur le code métier. Un serveur qui doit garder un panier, un brouillon ou un workflow entre deux appels émet lui-même un identifiant et le reçoit ensuite comme paramètre d'outil ordinaire. Les bonnes pratiques de sécurité de la spécification préviennent contre le détournement de ces identifiants : un serveur ne doit jamais traiter la possession d'un handle comme une authentification, et il lui faut lier chaque handle à l'utilisateur vérifié, par exemple en stockant l'état sous une clé identifiant_utilisateur:handle.

La règle la plus violée reste ailleurs. Un serveur MCP ne doit accepter aucun jeton qui n'a pas été émis pour lui : reprendre le jeton d'un client pour le transmettre tel quel à l'API en aval casse la frontière d'audience d'OAuth et transforme votre serveur en relais d'exfiltration. La spécification d'autorisation l'interdit en toutes lettres, en exigeant que le serveur vérifie que le jeton a bien été émis pour lui et qu'il n'accepte ni ne transmette aucun autre jeton. Dans le même esprit, exposez le minimum de scopes et élevez les droits par étapes, plutôt que d'ouvrir un scope global à la première connexion.

Notre recommandation tient en une phrase : restez en stdio tant qu'une seule personne utilise le serveur. Et quand le besoin se limite à relier des applications entre elles sans exposer de logique métier, un agent n8n branché sur vos outils métier vous évitera d'écrire et d'héberger quoi que ce soit.

Faire vivre un serveur MCP dans la durée

Un serveur MCP se maintient comme une API publique, parce que ses outils forment un contrat que des agents appellent sans vous prévenir. Renommer factures_impayees en impayes casse silencieusement les conversations en cours et les prompts que vos utilisateurs ont fini par écrire autour du nom exact. Traitez les noms d'outils comme des routes versionnées, avec une période de coexistence quand vous en retirez un.

La révision 2026-07-28 apporte deux réglages qui pèsent sur la facture de tokens. D'après le détail de la révision, les résultats de tools/list, prompts/list, resources/list, resources/read et resources/templates/list portent désormais deux champs obligatoires, ttlMs et cacheScope, qui indiquent au client combien de temps il peut garder la réponse en cache et si un intermédiaire partagé a le droit de la conserver. La même révision recommande de renvoyer les outils dans un ordre déterministe, ce qui améliore le taux de succès du cache de prompt côté modèle. Un serveur qui trie ses outils par un dictionnaire non ordonné perd ce bénéfice sans que rien ne le signale.

La dépréciation du canal de logging pousse la supervision vers OpenTelemetry, et la spécification MCP documente elle-même comment propager le contexte de trace, via les clés traceparent, tracestate et baggage placées dans _meta. C'est le moment de brancher vos traces d'outils sur la même chaîne d'observabilité que le reste de votre système d'information, plutôt que d'inventer un canal parallèle.

Enfin, testez les outils sans modèle. Un jeu de tests qui appelle chaque outil avec des entrées valides, des entrées hors bornes et un identifiant inexistant attrape la quasi-totalité des régressions, pour un coût nul en tokens. Les agents révèlent les bugs de conception, pas les bugs de code. Si vous préférez déléguer cette partie, notre agence spécialisée en agents IA conçoit et opère ces serveurs, et l'équipe vous rendra un périmètre d'outils défendable avant d'écrire la première ligne.

FAQ

Mon serveur écrit en 2025 fonctionne-t-il encore ?

Oui, les fonctionnalités dépréciées restent pleinement fonctionnelles, et le registre officiel place le retrait au plus tôt de Roots, Sampling, Logging et de l'enregistrement dynamique de client à la première révision publiée à partir du 28 juillet 2027. En revanche, un serveur qui s'appuyait sur les sessions HTTP, sur ping ou sur logging/setLevel doit être repris : ces trois éléments ont été supprimés, pas dépréciés. Comptez la migration au moment où vous mettez à jour le SDK, les deux vont ensemble.

Faut-il un serveur MCP ou une simple API REST ?

Une API REST s'adresse à un développeur qui lit une documentation, un serveur MCP s'adresse à un modèle qui choisit seul l'outil à appeler. Si vos consommateurs sont des applications, gardez votre API. Si vous voulez qu'un assistant réponde à « quelles factures Dupont n'a pas payées », il lui faut des outils nommés par intention, avec des retours courts. Beaucoup d'équipes construisent le serveur MCP par-dessus l'API existante, ce qui reste la bonne architecture.

Combien d'outils exposer dans un même serveur ?

Cinq à dix couvrent la majorité des besoins métier. Au-delà, la précision de sélection baisse et le contexte se remplit de définitions inutiles à l'appel en cours. Deux options existent quand la liste s'allonge : découper en plusieurs serveurs thématiques que le client active selon le contexte, ou passer à une découverte d'outils à la demande. La seconde n'a de sens qu'à l'échelle de dizaines de serveurs connectés.

Peut-on héberger un serveur MCP en France ?

Oui, un serveur MCP est un processus applicatif ordinaire, hébergeable chez n'importe quel fournisseur français ou européen. La question sensible porte sur le modèle qui appelle vos outils, puisque les données transmises transitent par lui. Notre analyse de l'hébergement d'un assistant IA en France détaille les arbitrages, notamment le fait que le serveur peut rester chez vous alors que le modèle vit ailleurs.

Combien de temps pour mettre un serveur MCP en production ?

Une version stdio pour un usage interne demande deux à trois jours, dont l'essentiel passe dans la définition des outils. Une version distante multi-utilisateurs change d'échelle : authentification OAuth, validation d'audience des jetons, gestion des handles d'état, hébergement et supervision ajoutent facilement deux semaines. Cette différence explique notre conseil de démarrer en local et de ne passer en HTTP qu'une fois les outils stabilisés par l'usage.

Un serveur MCP branché sur vos outils métier ?

Nous concevons les outils que votre agent expose, l'authentification et l'hébergement, puis nous les faisons vivre au rythme du protocole. Trente minutes suffisent pour cadrer votre cas.

Planifier un appel
Par
L'équipe Noxcod

Noxcod

On cadre votre produit avant de le construire

Application métier, SaaS, agent IA ou automatisation : on vous aide à choisir la bonne stack, le bon périmètre et les prochaines étapes.

Stack Périmètre Plan d'action