# MCP pour les devs : relier tes LLM au monde réel

MCP (Model Context Protocol) évite d'écrire un connecteur par client LLM et par source de données. Son architecture, puis un premier serveur MCP en TypeScript.

Source : https://simotech.dev/blog/mcp-pour-les-developpeurs

Dans l'[article précédent](/blog/agents-autonomes-llm), on a construit un agent capable de raisonner et d'agir via des tools. Il cherchait sur le web, lisait des pages, et synthétisait les résultats. Mais tous ses tools étaient codés en dur dans le projet : `web_search`, `read_url`, chacun avec son schéma Zod, son implémentation, son entrée dans le registre.

Le jour où tu veux que ce même agent accède aussi à tes fichiers, à ton calendrier ou à ta base de données, tu recodes chaque intégration à la main. Et tu recommences dans le projet suivant. C'est exactement ce qui m'a poussé à regarder MCP de près.

Cet article explique ce qu'est MCP à un dev qui connaît le function calling, puis on écrit un premier serveur en TypeScript, on le teste dans Claude Desktop et on y branche l'agent de la partie 2.

## Sans protocole commun, il faut N×M connecteurs

Avec 5 clients LLM (Claude, Cursor, ton app, ChatGPT, Gemini) et 10 sources de données (filesystem, PostgreSQL, GitHub, Slack...), ça fait 50 connecteurs à écrire et maintenir.

```
Sans standard :
Claude  ──custom──▶ GitHub
Claude  ──custom──▶ Slack
Cursor  ──custom──▶ GitHub     (recodé depuis zéro)
Cursor  ──custom──▶ Slack      (recodé depuis zéro)
...
```

MCP règle ça : chaque source de données est exposée par un serveur MCP, et chaque client parle le même protocole. Tu codes l'intégration une fois, elle sert partout.

```
Avec MCP :
Claude  ─┐
Cursor  ─┤─── MCP ───▶ Serveur GitHub
Ton app ─┘              Serveur Slack
                        Serveur PostgreSQL
```

N + M au lieu de N × M. Anthropic a publié MCP fin 2024, et en un an, OpenAI, Google et Microsoft l'ont adopté.

## Un client découvre les tools qu'un serveur expose

Le **Model Context Protocol** définit comment un client LLM communique avec un serveur qui expose des outils.

```mermaid
flowchart TD
    C["Client MCP<br/>(Claude, Cursor, ton agent)"] -- MCP --> S["Serveur MCP<br/>(expose des tools)"]
    S --> B["Backend<br/>(fichiers, API, base de données)"]
```

Le client se connecte au serveur, lui demande la liste de ses tools avec leurs descriptions et leurs schémas, et peut les appeler aussitôt. Rien n'est déclaré à la main côté client : c'est ce qu'on appelle la **discovery**.

### MCP sort les tools de ton application

Dans la [partie 1](/blog/initiation-llm-function-calling), tu définissais des tools dans ton code, le LLM les appelait, et ton code les exécutait. MCP déplace tout ça dans un serveur séparé.

| | Function calling | MCP |
|---|---|---|
| Qui définit les tools | Toi, dans ton code | Le serveur MCP |
| Qui les exécute | Ton code | Le serveur MCP |
| Discovery | Manuelle (tu listes les tools) | Automatique |
| Réutilisabilité | Copier-coller entre projets | Un serveur sert tous les clients |
| Transport | Intégré à ton app | Protocole séparé (stdio ou HTTP) |

Sous le capot, MCP repose sur le function calling : quand Claude est connecté à un serveur MCP, les tools du serveur apparaissent comme des functions dans son contexte. Ils viennent d'un processus externe au lieu d'être déclarés dans le code de l'application, donc n'importe quel client MCP peut les réutiliser.

### Un serveur expose des tools, des resources et des prompts

Les **tools**, d'abord : des fonctions que le LLM appelle, `read_file`, `search_database`, `send_email`. C'est la primitive principale, et beaucoup de serveurs n'exposent que ça.

Les **resources** ensuite : des données en lecture seule que le client charge dans le contexte, comme un schéma de base de données ou un fichier de config. C'est le client qui les récupère, souvent à l'initiative de l'utilisateur, alors qu'un tool est appelé par le LLM.

Et les **prompts** : des templates fournis par le serveur pour guider le LLM. Un serveur SQL pourrait proposer un prompt "Analyse cette table" qui structure la requête.

Pour la suite, on se concentre sur les tools.

## Ton premier serveur MCP

On écrit un serveur qui expose un seul tool, `get_weather`. Il te faut Node.js 20 ou plus récent.

```bash
mkdir weather-server && cd weather-server
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/server zod
npm install -D tsx
```

```ts title="src/server.ts"
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";

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

server.registerTool(
  "get_weather",
  {
    description: "Retourne la météo actuelle pour une ville donnée",
    inputSchema: z.object({
      city: z.string().describe("Nom de la ville"),
    }),
  },
  async ({ city }) => {
    // En vrai, tu appellerais une API météo ici
    const weather = {
      city,
      temperature: Math.round(Math.random() * 30),
      condition: ["Ensoleillé", "Nuageux", "Pluvieux"][Math.floor(Math.random() * 3)],
    };

    return {
      content: [{ type: "text", text: JSON.stringify(weather, null, 2) }],
    };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);
```

Le SDK gère le protocole, la sérialisation des messages (du JSON-RPC) et la discovery. Toi, tu définis le nom, la description, le schéma Zod et l'implémentation. C'est le même principe que le `roll_dice` de la partie 1, sauf que le tool vit dans son propre processus. La première fois, ça fait bizarre de voir Claude appeler du code qui tourne dans un terminal à côté.

La dernière ligne branche le serveur sur le transport **stdio** : le client lance le serveur comme un sous-processus et lui parle par l'entrée et la sortie standard.

> [!WARNING]
> En stdio, la sortie standard est réservée au protocole. Un `console.log` dans ton serveur corrompt les échanges : pour débugger, écris sur `console.error`.

> [!NOTE]
> Ce code utilise la v2 du SDK TypeScript, stable depuis juillet 2026 et découpée en deux paquets, `@modelcontextprotocol/server` et `@modelcontextprotocol/client`. Beaucoup de tutoriels, dont la première version de celui-ci, utilisent encore la v1 (`@modelcontextprotocol/sdk`), où le même tool se déclarait avec `server.tool(nom, description, schéma, handler)`. La v1 est toujours publiée, et le [guide de migration](https://ts.sdk.modelcontextprotocol.io/v2/migration/upgrade-to-v2) détaille le passage de l'une à l'autre.

### Tester avec Claude Desktop

Dans Claude Desktop, ouvre les réglages, onglet Developer, puis "Edit Config". Ça ouvre le fichier `claude_desktop_config.json`, qui se trouve dans `~/Library/Application Support/Claude/` sur macOS et dans `%APPDATA%\Claude\` sur Windows. Ajoutes-y ton serveur, avec un chemin absolu :

```json title="claude_desktop_config.json"
{
  "mcpServers": {
    "weather": {
      "command": "npx",
      "args": ["tsx", "/chemin/absolu/vers/weather-server/src/server.ts"]
    }
  }
}
```

Quitte complètement Claude Desktop, relance-le, puis demande "Quel temps fait-il à Lyon ?" :

```
Toi : "Quel temps fait-il à Lyon ?"

Claude : [appelle get_weather({ city: "Lyon" })]
         → { city: "Lyon", temperature: 22, condition: "Ensoleillé" }

Claude : "Il fait 22°C à Lyon et c'est ensoleillé."
```

Le même serveur fonctionne tel quel avec Cursor, Claude Code ou n'importe quel client MCP.

### Brancher l'agent de la partie 2 sur ce serveur

Claude Desktop est un client MCP parmi d'autres. L'agent qu'on a écrit dans la [partie 2](/blog/agents-autonomes-llm) peut en devenir un aussi. Sa boucle `runAgent` attend deux choses : une liste de tools et une fonction `executeTool`. Un client MCP fournit les deux, avec `listTools()` et `callTool()`.

Dans le projet de l'agent, installe le paquet client :

```bash
npm install @modelcontextprotocol/client
```

```ts title="src/mcp-agent.ts"
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { runAgent } from "./agent";

// 1. Lancer le serveur comme sous-processus et s'y connecter
const mcp = new Client({ name: "mon-agent", version: "1.0.0" });
await mcp.connect(
  new StdioClientTransport({
    command: "npx",
    args: ["tsx", "/chemin/absolu/vers/weather-server/src/server.ts"],
  })
);

// 2. Découvrir les tools et les traduire au format de l'API Mistral
const { tools: mcpTools } = await mcp.listTools();
const tools = mcpTools.map((tool) => ({
  type: "function" as const,
  function: {
    name: tool.name,
    description: tool.description ?? "",
    parameters: tool.inputSchema,
  },
}));

// 3. Même boucle qu'avant : seule l'exécution change de main
const answer = await runAgent("Quel temps fait-il à Lyon ?", {
  model: "mistral-small-latest",
  systemPrompt: "Tu réponds en français, en t'appuyant sur les outils disponibles.",
  tools,
  executeTool: (name, args) => mcp.callTool({ name, arguments: args }),
});

console.log(answer);
await mcp.close();
```

```bash
npx tsx src/mcp-agent.ts
```

Dans les logs de l'agent, tu dois voir passer une action `get_weather`, puis la réponse rédigée par le modèle.

La traduction de l'étape 2 est courte parce qu'un tool MCP et un tool de function calling décrivent la même chose : un nom, une description et un JSON Schema. Et l'agent ne contient plus une ligne de code météo. Remplace la commande par celle d'un autre serveur MCP, et il gagne d'autres tools sans que tu touches à la boucle. Les garde-fous de la partie 2 (budget, limite d'itérations, détection de boucle) continuent de s'appliquer, puisqu'ils vivent dans `runAgent`.

## Où en est MCP fin 2026

Côté clients, la [documentation officielle](https://modelcontextprotocol.io/docs/getting-started/intro) cite Claude et ChatGPT pour les assistants, Visual Studio Code et Cursor pour les outils de développement, et la liste est bien plus longue. Chez Anthropic, Claude Desktop, Claude.ai et Claude Code sont tous des clients MCP.

Côté serveurs, il en existe pour à peu près tout ce qu'un dev utilise : filesystem, bases de données, GitHub, Slack, Google Drive, Linear, Notion, Sentry... Le [registry officiel MCP](https://registry.modelcontextprotocol.io) les recense.

Côté gouvernance, Anthropic a confié MCP en décembre 2025 à l'Agentic AI Foundation, une fondation hébergée par la Linux Foundation et lancée avec Block et OpenAI. Ce n'est plus le protocole d'un seul acteur, et c'est ce qui m'a rassuré pour construire dessus.

Côté spec, la révision en vigueur date du [28 juillet 2026](https://modelcontextprotocol.io/specification/2026-07-28/changelog), et elle change le protocole en profondeur. Elle le rend sans état : le handshake `initialize` et les sessions disparaissent, chaque requête porte sa version de protocole et les capacités du client. Elle déprécie aussi trois fonctionnalités (Roots, Sampling, Logging) et, pour l'authentification, l'enregistrement dynamique des clients OAuth. Pour le serveur de cet article, ça ne change rien : le SDK s'occupe de la négociation, et le code d'un tool reste le même. Les effets se voient surtout sur les serveurs distants.

### Stdio pour le local, HTTP pour le distant

Le serveur qu'on vient d'écrire utilise stdio : le client le lance comme un sous-processus et communique via stdin/stdout. Simple, parfait pour le dev local.

Sauf que stdio implique que le serveur tourne sur ta machine. Pour y accéder depuis le web, depuis ton téléphone, ou le partager avec une équipe, il faut le transport **Streamable HTTP** : un serveur déployé derrière une URL HTTPS, avec une authentification OAuth.

```
stdio  → local, un seul client, dev/perso
HTTP   → distant, multi-clients, partageable
```

C'est le sujet de [MCP remote : quand stdio ne suffit plus](/blog/mcp-remote-serveurs-locaux-obsoletes), le premier article de la série [MCP en pratique](/series/mcp-en-pratique).

## La suite

Si tu as suivi la série depuis le début, tu as maintenant toute la chaîne : les LLM génèrent du texte, le function calling leur permet d'agir, les agents enchaînent les actions seuls, et MCP standardise leurs outils pour que tu n'aies plus à les réécrire.

Un serveur MCP en stdio, c'est une soirée de boulot. Le passage en remote, avec Streamable HTTP et OAuth, m'a demandé beaucoup plus : c'est là que j'ai passé le plus de temps sur Vaulken, et c'est tout l'objet de la série [MCP en pratique](/series/mcp-en-pratique). La suite de cette série-ci abordera l'évaluation des réponses LLM et la gestion des coûts.

## Pour aller plus loin

- [Spec MCP officielle](https://modelcontextprotocol.io/specification/2026-07-28)
- [SDK TypeScript](https://github.com/modelcontextprotocol/typescript-sdk)
- [Série "MCP en pratique"](/series/mcp-en-pratique)
