- 6 minutes

Model Context Protocol (MCP) - Les IA discutent avec votre application

AI
Typescript
Markdown logo

Comme indiqué dans la documentation, MCP est un standard open-source qui permet de connecter des application IA avec des systèmes externes.

Concrètement, cela signifie que si vous ou votre entreprise disposez d’un serveur MCP, alors une application IA peut y faire appel pour récupérer (ou écrire) des données directement exposées par votre système. Cela peut permettre à des utilisateurs d’effectuer des actions (comme des recherches) directement depuis leur chat, pendant que l’IA contacte en direct des services externes.

On peut alors imaginer un utilisateur qui recherche un hébergement pour son prochain voyage, et l’IA qui, en conséquence, contacte les serveurs MCP de différents services en ligne pour lui présenter les options disponibles.

Un serveur MCP expose des capacités à ses clients. Les clients pourront alors appeler des outils présents sur le serveur, ou bien récupérer de la donnée en lecture seule en appelant des ressources.

Pour créer un serveur MCP, on utilisera le Typescript SDK (Documentation).

Projet

Dans un dossier fraîchement créé :

npm init -y
# Modifier le type du projet pour utiliser les modules ES
npm pkg set type="module"
# Installer les dépendances de développement
npm i --save-dev tsx typescript @types/node

Fichier package.json

Dans le fichier package.json, ajouter un script pour exécuter le fichier src/index.ts :

{
  "name": "mcp-server-intro",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "dev": "tsx src/index.ts"
    //...
  }
  //...
}

Configuration Typescript

Concernant le fichier tsconfig.json, on peut soit le créer manuellement, soit le générer avec npx tsc --init, puis changer le contenu :

{
  "compilerOptions": {
    "module": "nodenext",
    "target": "esnext",
    "types": ["node"],

    "sourceMap": true,
    "declaration": true,
    "declarationMap": true,

    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,

    "strict": true,
    "jsx": "react-jsx",
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "noUncheckedSideEffectImports": true,
    "moduleDetection": "force",
    "skipLibCheck": true
  }
}

Typescript MCP SDK

Pour pouvoir créer notre serveur MCP, on va utiliser le SDK de @modelcontextprotocol :

npm i @modelcontextprotocol/sdk

Créer ensuite le fichier src/index.ts, c’est dans ce fichier qu’on va créer le serveur MCP et les outils qu’il va exposer.

Serveur & Transport

Dans le SDK se trouve une classe McpServer. On va donc instancier un objet de ce type, le constructeur nous donne la possibilité de définir des propriétés : nom, titre, version, capacités, et même des instructions qui indiquent les fonctionnalités du serveur et comment l’utiliser.

Dans la fonction principale, on va créer un Transport, de type StdioServerTransport. Le transport STDIO permet d’utiliser un serveur MCP en local : il utilise l’entrée/sortie standard (Standard Input/Output, STDIO) entre un client et un serveur MCP locaux. Si on a besoin d’un serveur disponible en ligne, et auquel pourraient se connecter plusieurs clients, on utiliserait un SSEServerTransport. Ce transport-là supporte les Server Sent Events (SSE), donc peut envoyer des notifications aux clients connectés.

On va ensuite connecter le serveur MCP au transport : await mcpServer.connect(transport).

Exemples d’implémentation sur le dépôt Github : exemples

// src/index.ts

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const mcpServer = new McpServer(
  {
    name: "ld-web",
    title: "LD-WEB",
    version: "1.0.0",
  },
  {
    instructions: "Fetch informations about web development",
  }
);

async function main() {
  const transport = new StdioServerTransport();
  await mcpServer.connect(transport);

  console.error("Server running...");
}

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

Pour lancer le serveur, utiliser le script qui lance tsx avec le fichier src/index.ts :

npm run dev

Outils (Tools)

Pour un serveur donné, on peut enregistrer des outils et les rendre disponibles aux clients :

mcpServer.registerTool(
  // nom de l'outil
  "my-tool",
  // Propriétés découvrables par les clients
  {
    title: "My tool",
    description: "My first MCP tool !",
  },
  // Logique d'exécution de l'outil
  async () => {
    return {
      content: [{ type: "text", text: "Output of my tool !" }],
    };
  }
);

MCP Inspector

Pour tester simplement le serveur MCP, on peut utiliser le MCP Inspector fourni par l’équipe du Model Context Protocol sur Github.

Lancement

Depuis la racine du projet, sans avoir lancé le serveur MCP, on va exécuter le MCP inspector qui nous permettra de nous connecter au serveur, puis d’explorer et exécuter ses outils :

npx @modelcontextprotocol/inspector tsx src/index.ts

L’outil lance une interface web sur le port 6274 :

MCP Inspector home

Connexion

À gauche de l’interface, la commande tsx et son argument src/index.ts sont déjà pré-remplis. Le transport, lui, est bien défini à STDIO, on peut cliquer sur “Connecter”.

Automatiquement, l’inspecteur nous place sur l’onglet “Tools” et nous indique sur la gauche de l’interface que nous sommes connectés au serveur “ld-web” :

MCP Inspector connected

Les informations ont été récupérées à partir d’un premier échange avec le serveur. Dans l’historique (en bas, un peu sur la gauche, “History”), on retrouve un élément initialize, qui, si on le déplie en cliquant dessus, nous indique que l’inspecteur a fait une requête “initialize”, à laquelle le serveur a répondu en fournissant des détails sur lui-même.

MCP Inspector initialize

Cette requête d’initialisation est en fait la première du cycle de vie d’un échange défini par le protocole MCP.

Exécution de notre outil

Maintenant que nous sommes connectés, nous allons exécuter l’outil que nous avons défini pour notre serveur. Le but est de recevoir, côté client, le message Output of my tool !.

Nous pouvons lister les outils en cliquant sur le bouton “List tools” :

MCP Inspector tools list

Dans la liste des outils, on retrouve notre outil “my-tool” avec sa description telle qu’on l’a enregistrée dans le code.

On peut alors cliquer sur l’outil, puis sur le bouton “Run tool” : nous obtenons côté client le résultat de l’exécution de l’outil :

MCP Inspector run tool

Arguments

Il est possible de passer des arguments en entrée de l’outil. Pour ce faire, dans le code, on va ajouter la propriété inputSchema à la déclaration de notre outil.

Le schéma, lui, sera compatible avec la librairie de validation zod :

mcpServer.registerTool(
  "my-tool",
  {
    title: "My tool",
    description: "My first MCP tool !",
    inputSchema: {
      name: z.string().describe("Name of the person to greet !"),
    },
  },
  async ({ name }) => {
    if (!name) {
      throw new Error("Name is required");
    }
    return {
      content: [{ type: "text", text: `Output of my tool, hello ${name} !` }],
    };
  }
);

Dans la fonction qui s’exécute, on récupère en entrée le paramètre déclaré dans la propriété inputSchema : on peut à présent utiliser un argument fourni par un client.

Dans l’inspecteur MCP :

MCP Inspector run tool arg

Notre outil pourrait faire bien plus que dire bonjour à un nom passé en paramètre : communication avec une base de données (en lecture et/ou écriture), appels API, accès au système de fichiers…

Installation dans Cursor

Dans un IDE comme Cursor, on peut déclarer un nouveau serveur MCP qui pointe vers notre fichier local. Dans la configuration, section “Tools & MCP”, on peut ajouter un nouveau serveur MCP personnalisé :

{
  "mcpServers": {
    "ld-web": {
      "type": "stdio",
      "command": "npx",
      "args": ["tsx", "/home/lucas/js/mcp-server-intro/src/index.ts"]
    }
  }
}

Une fois le serveur MCP installé et activé, l’IDE pourra l’invoquer directement lors de nos échanges avec le chat :

MCP Cursor integration

Tout comme dans l’avant-dernière image, l’outil a été exécuté avec le paramètre “Bobby”. Dans la dernière image (IDE Cursor), l’agent IA extrait tout seul le nom “Bobby” de notre prompt, et l’utilise pour invoquer l’outil MCP. Ainsi, on peut interagir avec nos outils en langage naturel.