Terminez d'abord "Fondamentaux et architecture de MCP" pour débloquer cette leçon.Aller à la leçon précédente

Objectif et architecture

Dans cette leçon, vous allez créer un serveur MCP local qui expose une capacité structurée : calculer un devis d’expédition à partir du poids d’un colis, de la zone de destination et du niveau de service. Cette capacité est implémentée sous forme d’outil MCP, car un client l’invoque activement avec des arguments et reçoit un résultat. À la fin de la leçon, un client compatible avec MCP pourra découvrir l’outil, examiner son schéma d’entrée, valider un appel et recevoir à la fois un texte lisible par un humain et des données structurées.

Le serveur utilisera TypeScript, Node.js 20 ou une version ultérieure, ainsi que le package officiel du serveur MCP pour TypeScript. Il communiquera via le transport stdio. Dans ce modèle, un client MCP démarre le serveur en tant que processus enfant et échange des messages JSON-RPC via l’entrée standard et la sortie standard. Ce transport convient aux applications d’IA locales, aux hôtes en ligne de commande et aux outils de développement. Un serveur partagé déployé à distance utiliserait normalement Streamable HTTP à la place.

Le flux des requêtes est simple. Le client démarre le processus, termine la négociation d’initialisation MCP, répertorie les outils disponibles, puis appelle l’outil sélectionné. Le SDK valide les arguments fournis avant d’exécuter votre gestionnaire. Celui-ci effectue le calcul métier et renvoie un résultat MCP. Cette séparation est importante : MCP définit la découverte et la communication, tandis que le code de votre application gère les règles d’expédition.

schéma de flux montrant une application d’IA contenant un client MCP, une connexion JSON-RPC via stdio, un serveur MCP, la validation du schéma d’entrée, le gestionnaire de devis d’expédition et un résultat structuré renvoyé à l’application

Configuration du projet

Créez un nouveau projet et installez le SDK du serveur, Zod, TypeScript et l’exécuteur tsx. Le package est configuré comme module ES, car le SDK actuel utilise des imports de modules ES. L’exécuteur tsx exécute directement le point d’entrée TypeScript, ce qui permet de concentrer ce premier exercice sur MCP plutôt que sur les outils de build.

bash
mkdir shipping-mcp-server
cd shipping-mcp-server
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/server zod
npm install --save-dev typescript tsx @types/node
mkdir src

Ajoutez des scripts à package.json afin que la commande de lancement soit stable. Le script du serveur exécute le processus stdio, tandis que le script d’inspection démarre MCP Inspector et lui indique comment lancer ce même processus.

json
{
"name": "shipping-mcp-server",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "tsx src/index.ts",
"inspect": "npx @modelcontextprotocol/inspector npx tsx src/index.ts"
},
"dependencies": {
"@modelcontextprotocol/server": "^2.0.0",
"zod": "^4.0.0"
},
"devDependencies": {
"@types/node": "^24.0.0",
"tsx": "^4.0.0",
"typescript": "^6.0.0"
}
}

Les versions exactes des dépendances résolues peuvent être plus récentes au moment de leur installation. Le point architectural important est que les nouveaux projets de serveur utilisent le package dédié au serveur plutôt que l’ancien package SDK monolithique. Si l’installation du package échoue, vérifiez que Node.js et npm sont disponibles et que votre version de Node.js est au moins la version 20.

Implémentation du serveur

Créez src/index.ts avec l’implémentation suivante. La fonction createServer construit un nouveau serveur MCP et enregistre l’outil calculate-shipping. Le schéma d’entrée accepte un poids de colis positif, l’une des trois zones de destination et l’un des deux niveaux de service. Le schéma de sortie décrit le résultat structuré que les clients peuvent exploiter sans analyser de texte libre.

typescript
import { McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';
const inputSchema = z.object({
weightKg: z.number().positive().max(50),
zone: z.enum(['domestic', 'regional', 'international']),
service: z.enum(['standard', 'express'])
});
const outputSchema = z.object({
currency: z.literal('USD'),
baseCost: z.number(),
serviceSurcharge: z.number(),
totalCost: z.number(),
estimatedDays: z.number().int()
});
function createServer(): McpServer {
const server = new McpServer({
name: 'shipping-quote-server',
version: '1.0.0'
});
server.registerTool(
'calculate-shipping',
{
title: 'Calculate Shipping Quote',
description: 'Calculate a shipping price and delivery estimate for a package',
inputSchema,
outputSchema
},
async ({ weightKg, zone, service }) => {
const ratePerKg = {
domestic: 2.5,
regional: 5,
international: 9
}[zone];
const standardDays = {
domestic: 4,
regional: 7,
international: 14
}[zone];
const baseCost = Number((5 + weightKg * ratePerKg).toFixed(2));
const serviceSurcharge = service === 'express'
? Number((baseCost * 0.6).toFixed(2))
: 0;
const totalCost = Number((baseCost + serviceSurcharge).toFixed(2));
const estimatedDays = service === 'express'
? Math.max(1, Math.ceil(standardDays / 2))
: standardDays;
const result = {
currency: 'USD' as const,
baseCost,
serviceSurcharge,
totalCost,
estimatedDays
};
return {
content: [
{
type: 'text',
text: `Shipping costs USD ${totalCost.toFixed(2)} and takes approximately ${estimatedDays} days.`
}
],
structuredContent: result
};
}
);
return server;
}
void serveStdio(createServer);
console.error('Shipping MCP server is running on stdio');

La description de l’outil doit indiquer ce que fait cette fonctionnalité, car un agent d’IA s’appuie sur le nom, la description et le schéma JSON généré pour décider s’il doit l’appeler. Le schéma constitue également une limite exécutable. Un poids négatif, une zone non prise en charge ou un service inconnu sont rejetés avant l’exécution du gestionnaire de calcul.

Comprendre le résultat

Le gestionnaire renvoie deux représentations du même résultat. Le tableau content contient un bloc de texte qu’un modèle ou une interface utilisateur peut lire directement. La valeur structuredContent contient des champs prévisibles pour les applications qui doivent afficher un prix, comparer des options, enregistrer le résultat ou le transmettre à un autre outil. Puisqu’un schéma de sortie est enregistré, la valeur structurée doit lui correspondre.

Pour une entrée indiquant un poids de 3 kilogrammes, la zone régionale et le service express, le coût de base est de 20 dollars. Le supplément express est de 12 dollars, ce qui porte le total à 32 dollars, avec un délai de livraison estimé à quatre jours. Un résultat réussi présente cette structure logique.

json
{
"content": [
{
"type": "text",
"text": "Shipping costs USD 32.00 and takes approximately 4 days."
}
],
"structuredContent": {
"currency": "USD",
"baseCost": 20,
"serviceSurcharge": 12,
"totalCost": 32,
"estimatedDays": 4
}
}

Une sortie structurée est préférable à l’intégration de toutes les informations dans une seule phrase. Un client ne devrait pas avoir besoin d’expressions régulières pour retrouver le prix total ou le délai de livraison estimé. Le texte reste utile pour le contexte du modèle, tandis que l’objet structuré fournit une interface applicative stable.

Exécution et tests

Démarrez directement le serveur pour vérifier que le processus se lance sans erreur de syntaxe ni d’importation. Il doit afficher son message d’état et continuer à attendre, car un serveur stdio a besoin d’un client MCP pour lui envoyer des requêtes. Après avoir confirmé qu’il reste actif, arrêtez-le à l’aide de la commande d’interruption du terminal.

bash
npm start

Lancez ensuite MCP Inspector. Inspector démarre votre serveur, effectue la négociation de connexion et fournit une interface navigateur pour répertorier et appeler les outils. Connectez-vous au serveur, ouvrez la vue Tools, sélectionnez calculate-shipping et soumettez des valeurs valides telles que 3, regional et express.

bash
npm run inspect

Vérifiez trois comportements au lieu de vous limiter au cas nominal. Premièrement, confirmez que l’outil apparaît avec la description et les champs d’entrée attendus. Deuxièmement, appelez-le avec des données valides et comparez le résultat structuré aux règles de calcul. Troisièmement, essayez un poids invalide, comme moins 2, ou une zone non prise en charge. La requête invalide doit échouer à la validation du schéma au lieu d’atteindre le gestionnaire.

Séquence de test montrant la découverte de l’outil, un appel valide à calculate-shipping avec weightKg égal à 3, la zone regional et le service express, le résultat structuré de 32 USD, ainsi qu’un appel avec un poids négatif invalide bloqué par la validation du schéma

Échecs fréquents et prochaines étapes

L’erreur stdio la plus destructive consiste à écrire les journaux de l’application sur la sortie standard. La sortie standard est réservée aux messages du protocole ; une simple ligne de journal peut donc corrompre le flux JSON-RPC et déconnecter le client. Utilisez la sortie d’erreur standard pour les diagnostics, comme l’illustre le message d’état. Cette restriction s’applique à votre propre code ainsi qu’aux bibliothèques susceptibles d’afficher une sortie inattendue.

Si l’Inspector ne parvient pas à démarrer le processus, exécutez directement la commande de démarrage depuis la racine du projet. Les erreurs d’importation indiquent généralement des noms de paquets incorrects, une version obsolète de Node.js ou une configuration manquante pour les modules ES. Si l’outil est visible mais que les appels échouent, vérifiez les noms et les types des arguments transmis. Le schéma attend un poids numérique et des valeurs d’énumération exactes ; une chaîne numérique n’est pas automatiquement équivalente à un nombre.

Un serveur qui démarre correctement n’est pas automatiquement un serveur MCP utile. Son outil doit disposer d’une description précise, d’entrées contraintes, d’un comportement déterministe et de résultats faciles à exploiter aussi bien par les modèles que par les applications. Vous disposez maintenant du parcours local complet : créer un serveur, enregistrer un outil structuré, le servir via stdio et le tester avec un véritable client MCP. L’étape pratique suivante consiste à connecter la même commande de lancement à un hôte d’IA ou à remplacer le calcul local par une base de données ou un service externe, tout en préservant le contrat de l’outil.

Entraînement

Point de contrôle de la leçon

Révisez la leçon et recevez des conseils sur votre travail.

0/1
Répondu · 0/1