MCP pour les débutants : créer un serveur et un client, puis les mettre en œuvre dans un environnement d’entreprise.
MCP pour débutants : le problème N x M, les idées fondamentales expliquées simplement, un serveur et un client de helpdesk en TypeScript avec Node.js, ainsi que la transition vers des déploiements HTTP sécurisés.
Sources: MCP Tools Inside Enterprise Applications: A Beginner-Friendly Deep Dive
Tout ceux qui ont travaillé récemment avec des agents d’IA sont tombés sur le MCP, le Model Context Protocol. On le trouve dans Claude Desktop, dans des plugins d’IDE, dans des projets de « portail d’IA » internes aux entreprises, et de plus en plus dans des logiciels d’entreprise en production.
Ce guide s’adresse aux débutants et ne suppose aucune expérience préalable avec MCP. Il part du problème que résout le protocole, aborde son architecture, crée un serveur et un client réels en TypeScript sur Node.js, puis examine la sécurité, la gouvernance et l’observabilité que les entreprises mettent en place autour de tout cela. Les diagrammes sont en ASCII texte brut, ce qui permet de les coller directement dans des notes ou des documents de code.
1. Le problème que résout MCP
Au préalable de l’arrivée de MCP, pour faire effectuer à un agent une tâche concrète, telle que lire un enregistrement CRM, ouvrir un ticket ITSM ou interroger un entrepôt de données, il n’y avait qu’une seule solution : écrire du code d’intégration personnalisé et unique pour cet agent et ce système.
Cela est encore supportable tant qu’il n’y a que quelques agents (un bot de support, un assistant de codage, un outil d’aide interne) et quelques systèmes (Salesforce, Jira, une base de données RH, un entrepôt). Le résultat est alors une accumulation combinatoire de connecteurs conçus manuellement.
BEFORE MCP — the N x M integration problem
┌───────────┐ ┌─────────────┐
│ Agent A │───────▶│ CRM API │ (custom connector #1)
└───────────┘ └─────────────┘
┌───────────┐ ┌─────────────┐
│ Agent A │───────▶│ Ticketing │ (custom connector #2)
└───────────┘ └─────────────┘
┌───────────┐ ┌─────────────┐
│ Agent B │───────▶│ CRM API │ (custom connector #3 -
└───────────┘ └─────────────┘ yes, AGAIN, for a different agent)
┌───────────┐ ┌─────────────┐
│ Agent B │───────▶│ Data │ (custom connector #4)
└───────────┘ │ Warehouse │
└─────────────┘
N agents x M systems = N x M custom, non-reusable integrations.
Every new agent re-implements auth, retries, schemas, error handling.
USB a résolu le même type de problème pour les périphériques matériels, tandis qu’ODBC et JDBC l’ont fait pour les bases de données : trop de connecteurs personnalisés et absence de norme commune. MCP est la réponse d’Anthropic, publiée en tant que protocole ouvert et non pas une fonctionnalité réservée à Claude, permettant ainsi à n’importe quelle application d’IA et à n’importe quel backend de communiquer dans un langage commun.
AFTER MCP — one protocol, many servers, many clients
┌───────────┐ ┌───────────────────┐
│ Agent A │──┐ ┌─▶│ MCP Server: CRM │
└───────────┘ │ ┌───────────┐ │ └───────────────────┘
├───▶│ MCP │───┤ ┌───────────────────┐
┌───────────┐ │ │ (shared │ ├─▶│ MCP Server: Ticket │
│ Agent B │──┘ │ protocol)│ │ └───────────────────┘
└───────────┘ └───────────┘ │ ┌──────────────────┐
└─▶│ MCP Server: DW │
└──────────────────┘
Any MCP-compatible agent can now talk to any MCP server.
Build the connector once, reuse it everywhere.
La description tient en une phrase. MCP offre aux applications d’IA une méthode commune pour trouver des outils, les appeler, lire des données et utiliser des modèles de prompts, de sorte qu’une intégration est écrite une seule fois et peut être réutilisée par n’importe quel client conforme.
2. Concepts de base expliqués simplement
Quatre termes sont utilisés de manière vague, donc ils sont définis clairement avant tout code.
- Hôte. L’application alimentée par l’IA que l’utilisateur final interagit réellement, comme un assistant de chat interne, un plugin d’IDE ou un copilote de support, et c’est cette partie qui contient le LLM.
- Client MCP. Un composant à l’intérieur de l’hôte qui gère une seule connexion vers un seul serveur MCP. Un hôte qui communique avec trois backends exécute généralement trois clients, un par serveur.
En dessous, tous les messages utilisent JSON-RPC 2.0, un format simple et bien établi pour les requêtes, les réponses et les notifications. Le SDK le génère à votre place, mais connaître ce format explique pourquoi les outils de débogage MCP ressemblent aux outils API que vous utilisez déjà, avec un ID de requête, le nom de la méthode, des paramètres ainsi qu’un résultat ou une erreur.
Ce qu’un serveur peut offrir : trois primitives
- Outils sont des fonctions appelables qui permettent au modèle d’agir ou de récupérer des résultats calculés, et c’est le modèle qui choisit quand les invoquer, en fonction de la demande de l’utilisateur. Exemples :
create_support_ticket,get_employee_details. - Ressources sont des données en lecture seule que l’hôte peut associer en tant que contexte, comme des fichiers ou des enregistrements. L’application ou l’utilisateur décide de ce qui doit être associé. Exemples : un article de base de connaissances, un PDF de politique, un fichier de journal.
- Les prompts sont des modèles d’instructions réutilisables et paramétrables que l’utilisateur choisit, un peu comme une commande slash. Exemple : « Résumez cet incident pour un post-mortem. »
Dans la plupart des intégrations d’entreprise, les outils effectuent environ 90 % du travail important, c’est pourquoi ils sont développés en premier.
3. L’architecture, trois couches ensemble
┌─────────────────────────── HOST APPLICATION ───────────────────────────┐
│ e.g. an internal AI assistant, IDE plugin, support copilot │
│ │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ MCP Client 1 │ │ MCP Client 2 │ │ MCP Client 3 │ │
│ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘ │
└───────────┼────────────────────────┼───────────────────────┼───────────┘
│ JSON-RPC over │ JSON-RPC over │ JSON-RPC over
│ stdio / HTTPS │ stdio / HTTPS │ stdio / HTTPS
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ MCP Server │ │ MCP Server │ │ MCP Server │
│ wraps HR system │ │ wraps Ticketing │ │ wraps Data │
│ (tools: lookup, │ │ (tools: create, │ │ Warehouse │
│ update) │ │ status, close) │ │ (tools: query) │
└──────────────────┘ └──────────────────┘ └─────────────────┘
Chaque serveur MCP doit être petit et ciblé, avec un serveur distinct pour chaque système backend, sans un seul serveur géant qui couvrirait tout. Cette approche est inspirée des microservices, avec un petit rayon d’action, un déploiement indépendant, une mise à l’échelle indépendante, ainsi que la responsabilité du groupe qui gère ce backend.
4. Création d’un premier serveur MCP (Node.js et TypeScript)
Le exemple fonctionnel est un serveur MCP de service d’assistance informatique comprenant deux outils : l’un pour créer une demande de support et l’autre pour vérifier l’état d’une demande. Il reflète un modèle très courant dans les entreprises, qui consiste à utiliser une API interne de gestion des demandes ou d’ITSM.
4.1 Configuration du projet
mkdir helpdesk-mcp-server && cd helpdesk-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node
npx tsc --init
@modelcontextprotocol/sdk est le package officiel. Il gère le JSON-RPC sur le réseau, les transports ainsi que les primitives, de sorte que le JSON-RPC brut n’est jamais manipulé manuellement. zod est une bibliothèque de validation de schémas. Les entrées des outils MCP sont vérifiées contre un schéma avant même que le code de traitement ne s’exécute, ce qui est très important dans les environnements d’entreprise, car les appels mal formatés ou malveillants sont bloqués dès l’entrée.
4.2 Le code du serveur
// src/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// --- A stand-in for a real internal ticketing API client ---
// In a real enterprise server this would call your ITSM system
// (ServiceNow, Jira Service Management, Zendesk, an internal API, etc.)
const ticketStore = new Map<string, { status: string; subject: string }>();
let nextId = 1000;
// 1. Create the server instance.
// "name" and "version" identify this server to any client that connects.
const server = new McpServer({
name: "helpdesk-mcp-server",
version: "1.0.0",
});
// 2. Register a tool: create_support_ticket
server.registerTool(
"create_support_ticket",
{
title: "Create Support Ticket",
description:
"Creates a new IT helpdesk ticket for the requesting employee.",
inputSchema: {
subject: z.string().describe("Short summary of the issue"),
priority: z.enum(["low", "medium", "high", "urgent"]),
employeeId: z.string().describe("Requesting employee's ID"),
},
outputSchema: {
ticketId: z.string(),
status: z.string(),
},
},
async ({ subject, priority, employeeId }) => {
const ticketId = `TCK-${nextId++}`;
ticketStore.set(ticketId, { status: "open", subject });
const output = { ticketId, status: "open" };
// MCP tool results return a "content" array (what a human/LLM reads)
// and, optionally, "structuredContent" (typed data other code can use).
return {
content: [
{
type: "text",
text: `Created ticket ${ticketId} (priority: ${priority}) for employee ${employeeId}.`,
},
],
structuredContent: output,
};
}
);
// 3. Register a second tool: get_ticket_status
server.registerTool(
"get_ticket_status",
{
title: "Get Ticket Status",
description: "Looks up the current status of an existing support ticket.",
inputSchema: {
ticketId: z.string(),
},
outputSchema: {
status: z.string(),
},
},
async ({ ticketId }) => {
const ticket = ticketStore.get(ticketId);
if (!ticket) {
// Returning isError lets the model know the call failed
// WITHOUT crashing the whole conversation.
return {
content: [{ type: "text", text: `No ticket found with ID ${ticketId}.` }],
isError: true,
};
}
return {
content: [{ type: "text", text: `Ticket ${ticketId} is currently "${ticket.status}".` }],
structuredContent: { status: ticket.status },
};
}
);
// 4. Wire the server to a transport and start listening.
// stdio is perfect for local development and desktop-hosted tools.
const transport = new StdioServerTransport();
await server.connect(transport);
4.3 Fonction de chaque composant
- L’instance
McpServer. Cet objet constitue le cœur du serveur. Il enregistre chaque outil, ressource et instruction enregistrés, et gère la négociation des capacités (handshake MCP) lors de la connexion d’un client. Aucune liste d’outils n’est jamais rédigée manuellement, car le SDK la génère à partir des appelsregisterTool. inputSchemaavec Zod. Plus qu’une simple documentation. Le SDK l’utilise pour générer un JSON Schema qui indique au client et à l’LLM quels arguments sont valides, et pour vérifier chaque appel dès son arrivée. Un client qui passepriority: "banana"est rejeté avant même que le gestionnaire ne soit atteint. Pour une entreprise, cette validation basée sur un schéma en premier lieu représente un véritable contrôle de sécurité et non une simple commodité.
outputSchema et structuredContent. Un outil peut retourner un résumé lisible par un humain (content) ainsi qu’un objet lisible par une machine (structuredContent). Le texte apparaît dans la transcription d’une conversation, tandis qu’une application appelante peut analyser les données structurées pour alimenter une interface utilisateur ou une logique supplémentaire.isError: true. MCP intègre une notion de premier plan relative à une erreur au niveau de l’outil, distincte de celle au niveau du protocole. Cela signifie que l’opération a échoué mais que la conversation doit se poursuivre ; le modèle peut donc lire l’erreur et décider s’il faut réessayer, interroger l’utilisateur ou essayer un autre outil. On ne souhaite pas que toute une session d’agent échoue simplement parce qu’un identifiant de ticket n’existait pas.StdioServerTransport). C’est la composante qui transfère les octets. Pour les outils locaux tels qu’une IDE de développeur ou un assistant de bureau, l’approche stdio est la plus simple, le hôte lançant le serveur en tant que processus enfant. Tout ce qui fonctionne de manière centralisée au sein d’une entreprise utilisera le transport basé sur HTTP, abordé au Chapitre 6.4.4 Le lancement
npx tsx src/server.ts
Le serveur ne montre rien de lui-même, car il attend qu’un client se connecte via stdin et stdout. C’est au tour du client.
5. Un client au sein d’une application d’entreprise
Les débutants construisent souvent un serveur avant de se demander ce qui communique réellement avec lui. Voici un client minimal, imaginé comme faisant partie d’un backend Node.js interne qui alimente l’assistant IA d’une entreprise.
// src/client.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
async function main() {
// 1. Describe how to launch the server. Here we spawn it as a
// local subprocess - in production you'd more commonly point
// this at a remote HTTP-based server instead (see Section 6).
const transport = new StdioClientTransport({
command: "npx",
args: ["tsx", "src/server.ts"],
});
// 2. Create a client and connect. This performs the MCP
// handshake and capability negotiation automatically.
const client = new Client({ name: "internal-ai-assistant", version: "1.0.0" });
await client.connect(transport);
// 3. Discover what tools this server offers - this is the same
// mechanism an LLM uses to "learn" what it can do.
const { tools } = await client.listTools();
console.log("Available tools:", tools.map((t) => t.name));
// 4. Call a tool, just like the LLM would.
const result = await client.callTool({
name: "create_support_ticket",
arguments: {
subject: "VPN keeps disconnecting",
priority: "high",
employeeId: "E-4821",
},
});
console.log(result.content);
await client.close();
}
main();
Pourquoi c’est important sur le plan conceptuel
L’idée centrale est la découverte. Le client appelle listTools() et reçoit un catalogue lisible par la machine contenant tout ce que le serveur propose, chaque outil étant accompagné de son schéma. C’est ce qui rend MCP dynamique : un hôte alimenté par un LLM n’a pas besoin d’outils intégrés directement dans ses instructions. Au moment de la connexion, il demande au serveur ce qu’il peut faire, et la réponse fait partie de ce que le modèle est informé pouvoir faire au cours de cette conversation.
La gouvernance d’entreprise repose précisément sur ce mécanisme. Si un outil ne doit pas être accessible à un utilisateur ou à un environnement donné, le contrôle le plus simple consiste à l’exclure de ce que listTools() renvoie, plutôt que d’espérer que le modèle choisira de ne pas l’utiliser.
6. Passer d’un prototype local à un déploiement en entreprise
La configuration stdio convient à l’apprentissage et aux outils de développement locaux, mais elle ne correspond pas au mode de fonctionnement de l’infrastructure dans la plupart des entreprises : hébergée de manière centralisée, protégée par une authentification, avec un journalisation, et accessible simultanément par de nombreuses applications hôtes. C’est là que le transport Streamable HTTP et une passerelle API entrent en jeu.
Voici le même serveur de helpdesk adapté pour fonctionner via HTTP et être placé derrière une application Express simple :
// src/httpServer.ts
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
const app = express();
app.use(express.json());
app.post("/mcp", async (req, res) => {
// In a real enterprise deployment, authentication middleware would
// run BEFORE this point - verifying a bearer token, checking scopes,
// and attaching the caller's identity to the request.
const server = buildHelpdeskServer(); // same registerTool calls as before
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // stateless mode: simplest to scale horizontally
});
res.on("close", () => transport.close());
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(3000, () => console.log("MCP server listening on :3000"));
Le changement majeur concerne moins le code que l’endroit où réside la responsabilité. Trois aspects qui étaient implicites avec stdio deviennent des enjeux explicites au niveau de l’entreprise avec HTTP.
- L’authentification a lieu avant le serveur, généralement dans un middleware ou un gateway, ce qui permet au serveur de supposer que
reqcontient déjà une identité vérifiée. - Le caractère sans état est une décision consciente. Avec
sessionIdGenerator: undefined, le transport ne conserve aucune session par connexion, de sorte que n’importe quelle instance derrière le load balancer peut répondre à toute demande, et il est possible de l’élargir comme pour n’importe quelle API sans état. - L’observabilité cesse d’être optionnelle. Chaque appel à outil constitue une véritable requête réseau, il convient donc de la journaliser, de la tracer et de limiter son rythme comme pour n’importe quel appel API interne.
7. Liste de vérification des considérations d’entreprise
Faire fonctionner un serveur MCP n’est que la cinquième partie simple du travail. Les quatre cinquièmes restants consistent à le rendre sûr pour être intégré à des systèmes de production contenant des données réelles d’employés ou de clients, et c’est ce volet qui est décrit ci-dessous.
- Authentification et autorisation. Utilisez OAuth2 ou OIDC au niveau du gateway. Associez l’identité de l’utilisateur ou du compte de service appelant à des permissions spécifiques, afin que chaque outil ne puisse pas être utilisé par n’importe qui.
- Conception d’outils avec le principe du moindre privilège. Préférez des outils restreints tels que
get_ticket_statusplutôt que des outils larges commerun_sql_query. Un outil qui effectue une seule tâche bien définie est bien plus facile à comprendre et à auditer.
8. Où cela se retrouve dans les entreprises réelles
- INformatique et support interne. Exemple du service d’assistance : création et consultation de tickets, réinitialisation de mots de passe, vérification de l’état du système.
- Assistants pour les ventes et les CRM. Récupération de l’historique des comptes et rédaction d’e-mails de suivi basés sur des données CRM réelles et non sur des suppositions du modèle.
- Assistants pour les données et l’analyse. Un ensemble d’outils de consultation en lecture seule et préapprouvés pour accéder aux données, à la place d’un SQL non restreint.
- Flux de travail dans le secteur de la santé. Outils qui recherchent des créneaux de rendez-vous, vérifient l’éligibilité à l’assurance ou signalent des lacunes dans la coordination des soins, toujours soumis à un accès strict basé sur les rôles et à des traces d’audit en raison de la sensibilité des données. Ici, les principes de utilisation d’outils spécialisés, d’intervention humaine et de tenue d’un journal d’audit complet énoncés à la section 7 constituent souvent une exigence de conformité.
- Outils pour développeurs. Assistants d’IDE qui consultent la documentation interne, lancent des pipelines CI ou vérifient qui est de garde pour un service.
Ce qu’ils ont en commun : MCP ne rend pas un modèle plus intelligent. Ce qui change, c’est que l’IA obtient un accès contrôlé et auditable aux systèmes réels et n’a plus besoin d’inventer des réponses plausibles.
9. Pièges auxquels se heurtent les débutants
- Un seul serveur géant capable de tout faire. Il est divisé en systèmes backend ou par domaine. Cela reflète de bonnes frontières de microservices et limite l’impact des problèmes.
- Omission des champs
description. Le modèle s’appuie fortement sur les descriptions des outils et des paramètres pour décider quand et comment appeler une fonction. Des descriptions vagues entraînent l’utilisation du mauvais outil ou du bon outil avec les mauvais arguments. - Oubli de
isError. Une exception brute générée par un gestionnaire d’outil peut mettre fin à toute la connexion client, alors que le modèle devrait plutôt être informé que l’opération a échoué et devrait essayer autre chose. - Utilisation de stdio en production. Stdio convient bien au développement local et aux ordinateurs de bureau. La production multi-locataires nécessite un transport HTTP derrière un véritable gateway.
10. La version courte
Lorsque les termes techniques sont éliminés, l’idée fondamentale de MCP devient étonnamment simple : une méthode commune permettant aux applications d’IA de trouver et d’invoquer des outils, de récupérer des données et de réutiliser des modèles de prompts, ce qui signifie que le travail d’intégration effectué une seule fois sert tous les clients conformes. Pour les entreprises, c’est cette standardisation qui permet aux agents d’IA de passer de démonstrations spectaculaires à des outils pouvant être gérés, sécurisés et exécutés en production aux côtés de tout le reste.
Pour ceux qui commencent, une approche sensée est :
- Créer un petit serveur MCP localement en utilisant stdio, comme dans l’exemple du service d’assistance.
- Ajouter un client qui appelle ces outils, afin que tout le processus soit visible.
- Placer le serveur derrière un HTTP avec authentification ou un pare-feu en amont.
- Seul ensuite faut-il recourir à l’enregistrement des audits, aux limites de fréquence et à l’approbation humaine pour toute action destructrice.
Commencez petit et de manière locale, puis introduisez progressivement les exigences propres aux entreprises. Essayer de concevoir le serveur de production parfait dès le premier jour est une méthode bien plus risquée pour y parvenir.