Créer un serveur MCP pour Claude Code et GitHub Copilot
Créez un seul serveur MCP en TypeScript et enregistrez-le dans Claude Code comme dans GitHub Copilot. Vous obtenez le code fonctionnel des outils, la configuration exacte de chaque client, une routine de débogage avec le MCP Inspector et un outil d’image qui appelle l’API PicassoIA.
Vous avez écrit un script qui vous fait gagner dix minutes par jour, et vous voulez maintenant que votre assistant d’IA l’exécute sans passer par un copier-coller. Créez-le une fois sous forme de serveur Model Context Protocol, et Claude Code et GitHub Copilot peuvent appeler les mêmes outils, car MCP est le langage commun qu’ils utilisent pour communiquer avec tout ce qui se trouve en dehors de l’éditeur. Ce guide pas à pas construit un petit serveur TypeScript, l’enregistre dans Claude Code, l’enregistre dans Copilot sous VS Code, et se termine par un véritable outil de génération d’images qui appelle l’API de PicassoIA. Prévoyez environ 40 minutes et à peu près 100 lignes de code.
Pourquoi un seul serveur vaut mieux que deux
Avant MCP, chaque assistant exigeait son propre format de plugin, son propre manifeste et ses propres règles de packaging. Un serveur MCP remplace tout cela par un processus unique qui annonce ce qu’il sait faire. Le client lance le processus, demande la liste de ses capacités et transmet cette liste au modèle. Le modèle décide ensuite, au cours de la conversation, quand un appel vaut la peine d’être fait.
Le même protocole, deux clients
Un serveur peut exposer trois types de capacités :
Outils : des fonctions que le modèle peut appeler, comme add_note ou generate_image.
Ressources : des données en lecture seule que le client peut joindre à une conversation, comme un fichier journal ou un schéma.
Prompts : des modèles réutilisables que l’utilisateur déclenche volontairement.
Les outils concentrent aujourd’hui presque toute la valeur, c’est pourquoi cet article s’y limite. Claude Code et Copilot parlent tous deux les mêmes messages JSON-RPC sur les mêmes transports, ce qui signifie qu’un serveur qui fonctionne avec un client fonctionnera avec l’autre avec presque aucune modification.
Là où les configurations diffèrent
Le serveur est identique. L’enregistrement, lui, ne l’est pas. Voici toute la différence dans un seul tableau :
Paramètre
Claude Code
GitHub Copilot dans VS Code
Fichier de configuration
.mcp.json dans le projet, ou ~/.claude.json
.vscode/mcp.json, ou votre profil utilisateur
Propriété racine
mcpServers
servers
Ajout depuis le terminal
claude mcp add
Palette de commandes : MCP: Add Server
Champ de transport
type (stdio, http, sse)
type est obligatoire (stdio ou http)
Secrets
Option --env ou expansion ${VAR}
Bloc inputs avec ${input:id}
Où les outils s’exécutent
Toute session
Mode Agent uniquement
💡 Astuce : La propriété racine est le piège classique. Si vous collez une configuration Claude Code telle quelle dans VS Code, rien ne se charge, car Copilot cherche servers et non mcpServers.
Préparer le projet
Choisissez un dossier en dehors de votre dépôt principal afin que le serveur puisse servir plusieurs projets plus tard. Il vous faut Node.js 20 ou une version plus récente, ainsi qu’un terminal.
Les exemples utilisent l’API @modelcontextprotocol/sdk 1.x avec McpServer et registerTool. Si une nouvelle version majeure modifie un chemin d’import, les concepts ci-dessous restent les mêmes.
Commencer avec stdio
MCP définit deux transports principaux. stdio signifie que le client lance votre serveur comme processus enfant et échange des messages via l’entrée et la sortie standard. Streamable HTTP signifie que le serveur tourne de son côté et que les clients se connectent par une URL. Commencez par stdio. Il ne nécessite ni port, ni couche d’authentification, ni hébergement, et les deux clients le prennent en charge d’emblée. Passez à HTTP uniquement lorsque plusieurs personnes doivent partager une même instance en cours d’exécution.
Écrire le serveur
Notre exemple est un petit serveur de notes d’équipe avec deux outils : l’un enregistre une note, l’autre les recherche. Il est assez court pour être lu en une minute et assez concret pour être utile.
Enregistrer un outil
Créez src/index.ts :
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { promises as fs } from "node:fs";
import path from "node:path";
const NOTES_FILE = path.join(process.env.NOTES_DIR ?? process.cwd(), "notes.json");
type Note = { id: number; text: string; tags: string[]; createdAt: string };
async function readNotes(): Promise<Note[]> {
try {
return JSON.parse(await fs.readFile(NOTES_FILE, "utf8"));
} catch {
return [];
}
}
const server = new McpServer({ name: "team-notes", version: "1.0.0" });
server.registerTool(
"add_note",
{
title: "Add note",
description:
"Save a short engineering note with optional tags. Use it when the user asks to remember a decision, a command or a bug.",
inputSchema: {
text: z.string().min(3).max(2000),
tags: z.array(z.string()).default([]),
},
},
async ({ text, tags }) => {
const notes = await readNotes();
const note: Note = {
id: notes.length + 1,
text,
tags,
createdAt: new Date().toISOString(),
};
await fs.writeFile(NOTES_FILE, JSON.stringify([...notes, note], null, 2));
return { content: [{ type: "text", text: `Saved note #${note.id}` }] };
}
);
server.registerTool(
"search_notes",
{
title: "Search notes",
description: "Find saved notes whose text or tags contain the query.",
inputSchema: { query: z.string().min(1) },
},
async ({ query }) => {
const q = query.toLowerCase();
const hits = (await readNotes()).filter(
(n) =>
n.text.toLowerCase().includes(q) ||
n.tags.some((t) => t.toLowerCase().includes(q))
);
const text = hits.length
? hits.map((n) => `#${n.id} [${n.tags.join(", ")}] ${n.text}`).join("\n")
: "No notes matched.";
return { content: [{ type: "text", text }] };
}
);
await server.connect(new StdioServerTransport());
console.error("team-notes MCP server running on stdio");
Exécutez npm run build. Vous disposez maintenant de dist/index.js, et ce fichier est la seule chose que les deux clients ont besoin de connaître.
Renvoyer des résultats propres
Le modèle lit tout ce que vous renvoyez, traitez donc la valeur de retour comme une interface. Gardez des résultats courts, structurés et honnêtes. Lorsqu’une erreur survient, ne levez pas une exception qui fait tomber la couche de transport. Renvoyez une erreur que le modèle peut lire et à laquelle il peut réagir :
return {
isError: true,
content: [{ type: "text", text: "notes.json is not valid JSON. Fix or delete it." }],
};
Un résultat isError permet à l’assistant de vous expliquer le problème ou de réessayer avec une autre entrée. Un plantage, lui, n’affiche qu’un vague bandeau « server disconnected ».
Garder stdout silencieux
C’est la cause la plus fréquente d’échec d’un premier serveur. Avec stdio, la sortie standard appartient au protocole. Un seul console.log perdu injecte du texte brut dans le flux JSON-RPC, et le client coupe la connexion. Écrivez vos journaux avec console.error, qui écrit sur stderr : les deux clients les captureront alors comme sortie de diagnostic.
💡 Astuce : Rédigez les descriptions d’outils comme des instructions destinées au modèle, et non comme de la documentation pour des humains. « Utilisez-le quand l’utilisateur demande de retenir une décision » fait choisir l’outil au bon moment. « Utilitaire de notes » ne le fait pas.
Connecter Claude Code
Ajouter le serveur avec la CLI
Une seule commande enregistre le serveur. Les options vont avant le nom, et un double tiret sépare le nom de la commande que Claude Code lancera :
claude mcp add --transport stdio --scope user \
--env NOTES_DIR=/home/dev/notes \
team-notes -- node /absolute/path/to/team-notes-mcp/dist/index.js
Utilisez un chemin absolu. Claude Code démarre le processus depuis le répertoire utilisé par votre session, donc les chemins relatifs cassent dès que vous ouvrez un autre projet. Vérifiez ensuite :
claude mcp list
claude mcp get team-notes
Dans une session, tapez /mcp pour afficher l’état de la connexion et la liste des outils. Posez une question naturelle, par exemple « Retenez que nous déployons le jeudi, ajoutez le tag release », et observez Claude Code demander l’autorisation d’appeler add_note.
Partager le serveur via .mcp.json
L’option de portée détermine qui accède au serveur. local le réserve à vous seul dans un projet, user le rend disponible dans tous les projets, et project écrit un fichier .mcp.json que vous pouvez committer afin que toute l’équipe en bénéficie. Voici une configuration partagée qui évite les chemins codés en dur :
Chaque membre de l’équipe définit TEAM_NOTES_PATH une seule fois dans son shell. La forme ${NOTES_DIR:-.notes} fournit une valeur par défaut lorsque la variable est absente. Claude Code demande une approbation la première fois qu’il rencontre un serveur de portée projet, une protection judicieuse pour tout ce qui provient d’un dépôt.
Connecter GitHub Copilot
Écrire .vscode/mcp.json
Créez .vscode/mcp.json dans votre espace de travail. Retenez la propriété racine différente et le type obligatoire :
${workspaceFolder} rend le fichier portable, vous pouvez donc le committer. Pour les secrets, ajoutez un tableau inputs. VS Code demande la valeur une fois, la stocke de manière sécurisée et l’injecte :
Copilot Chat s’ouvre par défaut en mode Ask, et les outils MCP ne se déclenchent qu’en mode Agent. Changez de mode dans le panneau de chat, ouvrez le sélecteur d’outils et vérifiez que team-notes apparaît avec ses deux outils cochés. S’il n’apparaît pas, exécutez MCP: List Servers depuis la palette de commandes, sélectionnez le serveur et lisez sa sortie. Redémarrez-le depuis le même menu après chaque reconstruction.
Copilot vous permet aussi de choisir parmi les modèles proposés par votre forfait, ce qui permet de tester le même serveur avec différents modèles. C’est un moyen peu coûteux de vérifier si vos descriptions d’outils sont assez claires pour chacun d’eux.
Tester et déboguer avant la mise en production
Lancer le MCP Inspector
Avant d’accuser l’un ou l’autre client, testez le serveur seul. Le MCP Inspector officiel ouvre une page web locale où vous pouvez lister les outils, renseigner les arguments et voir les réponses brutes :
Appelez add_note avec un text vide. Votre schéma Zod doit le rejeter avec un message de validation lisible. Appelez ensuite search_notes avec un tag que vous venez d’enregistrer. Si les deux se comportent correctement ici, tout problème restant se situe dans la configuration du client et non dans votre code.
Corriger les pannes courantes
Symptôme
Cause probable
Correctif
Le serveur ne se connecte jamais
Un console.log a écrit sur stdout
Passer à console.error
« Command not found »
Chemin relatif ou build manquant
Utiliser un chemin absolu et exécuter npm run build
Outils absents dans Copilot
Le chat est en mode Ask
Passer en mode Agent
L’outil existe mais n’est jamais choisi
Description vague
La réécrire avec des formulations déclencheuses
Variable d’environnement vide
Non déclarée dans la configuration
L’ajouter au bloc env
Les appels d’image échouent sous charge
Plus de 5 tâches simultanées
Mettre les appels en file d’attente dans l’outil
Donner à votre serveur un outil d’image
Les notes, c’est bien, mais la meilleure démonstration de MCP est un outil qui fait ce que l’assistant ne peut pas faire seul. La génération d’images convient bien : le modèle rédige un prompt précis, votre serveur le transforme en fichier, et l’URL revient directement dans la conversation.
Appeler l’API PicassoIA
L’API développeur de PicassoIA se trouve à https://api.picassoia.com/v1 et utilise un jeton Bearer qui commence par pia_sk_. Les prédictions sont asynchrones, sur le modèle de Replicate : vous en créez une, puis vous l’interrogez jusqu’à ce que son statut indique succeeded. Le modèle PicassoIA Image accepte un prompt pouvant atteindre 4 000 caractères et un aspect_ratio, et renvoie une liste d’URL d’images. Ajoutez cet outil avant la ligne server.connect :
Un compte autorise 5 prédictions simultanées, partagées entre tous les jetons et toutes les connexions. Une boucle qui lance dix images d’un coup atteindra donc ce plafond. Générez-les les unes après les autres dans l’outil, ou gardez une petite file d’attente.
💡 Astuce : Vérifiez les tarifs actuels et les conditions d’accès des forfaits sur la page de l’API PicassoIA avant de publier un serveur pour d’autres personnes. La documentation et la page tarifaire décrivent l’accès différemment : confirmez donc ce que votre propre forfait inclut.
Comment utiliser Sonnet 5 sur PicassoIA
Vos descriptions d’outils sont des prompts, et un modèle de langage est le meilleur éditeur pour les rédiger. Claude Sonnet 5 est un excellent choix pour cette tâche, et vous pouvez l’utiliser sur PicassoIA sans quitter le navigateur.
Ouvrez la page Claude Sonnet 5 dans la collection de grands modèles de langage.
Collez vos définitions d’outils, avec les noms, les descriptions et les schémas, dans le prompt.
Demandez-lui de réécrire chaque description sous forme de courte instruction qui précise quand appeler l’outil et ce qu’il renvoie.
Demandez dix entrées limites par outil, comme des chaînes vides, des textes très longs et des tags inhabituels.
Passez ces entrées dans le MCP Inspector, corrigez chaque échec et recollez les descriptions améliorées dans votre code.
Pour un second avis sur une logique délicate, Claude Fable 5 et GPT 5.6 Sol figurent tous deux parmi les modèles proposés pour les tâches de programmation. Comparer leurs réécritures d’une même description révèle souvent quelle formulation est ambiguë.
Essayez par vous-même sur PicassoIA
Vous disposez désormais d’un serveur unique qui tourne dans deux assistants : des notes pour la mémoire, un outil d’image pour les résultats, et une routine de test qui garde les deux honnêtes. Le même schéma s’adapte à tout ce que vous pouvez encapsuler dans une fonction, des scripts de déploiement aux recherches en base de données.
Commencez par l’outil d’image, car il donne un retour immédiat. Rédigez un prompt, appelez generate_image depuis Claude Code ou Copilot, et voyez le résultat en quelques secondes. Ensuite, ouvrez la page PicassoIA Image pour expérimenter directement avec les formats et les styles de prompt, ou parcourez tous les modèles disponibles sur picassoia.com/en/all-models. Votre première image n’est qu’à un prompt de distance.