Votre premier plugin
Sur cette page
Leçon réécrite le 3 novembre 2026 d’après le Quickstart, le MCP server and UI quickstart et la page Connect and test your plugin de la documentation Plugins d’OpenAI. L’ancienne « app météo en 30 minutes » utilisait un SDK inexistant. Les versions des paquets npm ont été vérifiées le 3 novembre 2026 :
@modelcontextprotocol/ext-apps2.x s’appuie sur la v2 du SDK MCP, alors que les exemples d’OpenAI utilisent la v1. Sources : Quickstart · MCP server and UI quickstart · Connect and test.
Deux étapes
Vous allez d’abord brancher un serveur MCP existant, pour voir le parcours complet côté ChatGPT sans écrire une ligne de code. Vous écrirez ensuite votre propre serveur, le testerez en local et le connecterez de la même façon.
Étape 1 : un plugin personnel en cinq minutes
OpenAI met à disposition un serveur MCP d’exemple, public et sans authentification, qui expose un seul outil en lecture : roll_dice, un lancer de dés.
- Ouvrez ChatGPT Plugins, sélectionnez le bouton +, puis Add custom MCP server.
- Donnez un nom, et indiquez
https://tinymcp.dev/api/moldy-aloof-zettabyte/mcpcomme Server URL. Choisissez No authentication. - Lisez l’avertissement de risque, sélectionnez I understand and want to continue, puis Create as a plugin.
- Le plugin apparaît dans vos plugins personnels : ouvrez-le et installez-le avec le bouton +.
- Revenez à l’accueil de ChatGPT, passez de l’onglet Chat à Work, ouvrez une nouvelle conversation, tapez
@et sélectionnez votre plugin. - Demandez-lui de lancer un dé à vingt faces : il doit appeler
roll_diceune seule fois avecsidesà 20, et renvoyer une valeur entre 1 et 20.
Ne vous arrêtez pas à ce cas nominal. Essayez d’autres tailles de dé, des valeurs invalides, et des demandes qui ne devraient appeler aucun outil. C’est le réflexe de toute la suite du cours : quand le mauvais outil est choisi ou que ses arguments varient, c’est la description et le schéma de l’outil qu’il faut retravailler. Notez enfin que les politiques de votre compte ou de votre espace de travail encadrent l’ajout de serveurs MCP personnalisés : dans une organisation, l’option peut être restreinte.
Étape 2 : votre propre serveur
Installer les SDK officiels
Les SDK officiels du protocole existent en TypeScript (@modelcontextprotocol/sdk) et en Python (mcp). Ce cours suit les exemples d’OpenAI, écrits en TypeScript avec la version 1 du SDK. Épinglez les versions : depuis septembre 2026, @modelcontextprotocol/ext-apps 2.x — le paquet d’interface que vous ajouterez à la leçon 6 — s’appuie sur les nouveaux paquets de la v2 du SDK (@modelcontextprotocol/server…), incompatibles avec les imports de la v1.
mkdir notes-plugin && cd notes-plugin
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/sdk@^1.29 @modelcontextprotocol/ext-apps@^1.7 zod
npm pkg set type=module ajoute "type": "module" au package.json, nécessaire pour les import du serveur.
Écrire le serveur
Ce serveur tient en un fichier, server.js. Il garde des notes en mémoire et expose deux outils : une lecture et une écriture, séparées comme le recommande la leçon 3.
import { createServer } from "node:http";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod";
const notes = []; // données d'exemple en mémoire : remplacez-les par votre stockage
const schemaNotes = {
notes: z.array(z.object({ id: z.string(), text: z.string() })),
};
function creerServeur() {
const server = new McpServer({ name: "notes-demo", version: "0.1.0" });
server.registerTool(
"list_notes",
{
title: "Lister les notes",
description: "À utiliser quand l'utilisateur veut revoir les notes enregistrées avec ce plugin.",
inputSchema: {},
outputSchema: schemaNotes,
annotations: { readOnlyHint: true, openWorldHint: false, destructiveHint: false },
},
async () => ({
structuredContent: { notes },
content: [{ type: "text", text: `${notes.length} note(s) enregistrée(s).` }],
})
);
server.registerTool(
"add_note",
{
title: "Ajouter une note",
description: "À utiliser quand l'utilisateur demande d'enregistrer une courte note.",
inputSchema: { text: z.string().min(1).max(500) },
outputSchema: schemaNotes,
annotations: { readOnlyHint: false, openWorldHint: false, destructiveHint: false },
},
async ({ text }) => {
notes.push({ id: `note-${notes.length + 1}`, text });
return {
structuredContent: { notes },
content: [{ type: "text", text: "Note enregistrée." }],
};
}
);
return server;
}
const PORT = Number(process.env.PORT ?? 8787);
createServer(async (req, res) => {
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
if (url.pathname !== "/mcp") {
res.writeHead(404).end("Not Found");
return;
}
// Mode sans état : un serveur et un transport neufs par requête
const server = creerServeur();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true,
});
res.on("close", () => {
transport.close();
server.close();
});
try {
await server.connect(transport);
await transport.handleRequest(req, res);
} catch (error) {
console.error("Erreur MCP :", error);
if (!res.headersSent) res.writeHead(500).end("Internal server error");
}
}).listen(PORT, () => console.log(`Serveur MCP prêt sur http://localhost:${PORT}/mcp`));
Trois détails comptent. Chaque outil déclare un titre, une description d’intention, des schémas et des annotations exactes : c’est sa « fiche » pour le modèle. Chaque résultat combine structuredContent, réutilisable par le modèle, et content, un texte court. Et toute adresse autre que /mcp répond 404 : l’exemple d’OpenAI le fait aussi pour les routes de découverte OAuth que vous n’utilisez pas encore, ce qui évite à ChatGPT d’afficher des erreurs 502 pendant vos essais sans authentification. Le même exemple ajoute une réponse à la requête préalable CORS (OPTIONS) et un GET / pour les sondes de santé ; reprenez-les si votre hébergement les attend.
Tester en local avec MCP Inspector
node server.js
npx @modelcontextprotocol/inspector@latest
Dans l’Inspector, choisissez Streamable HTTP, saisissez http://localhost:8787/mcp et connectez-vous. Vérifiez que l’initialisation réussit, que les deux outils apparaissent avec leurs schémas et leurs annotations, puis appelez-les avec des entrées valides et invalides — un texte vide doit être refusé par le schéma.
Exposer le serveur et le connecter
ChatGPT doit pouvoir joindre votre serveur depuis Internet. Pendant le développement, un tunnel suffit — la documentation cite ngrok :
ngrok http 8787
Vous obtenez une adresse HTTPS publique. Connectez-la comme à l’étape 1, avec le chemin /mcp (par exemple https://<sous-domaine>.ngrok.app/mcp), installez le plugin obtenu, puis invoquez-le par @ : « Enregistre une note : appeler le fournisseur lundi ». ChatGPT affiche les données échangées avec l’outil, ce qui vous permet de contrôler entrées et sorties.
Un tunnel de développement ne convient qu’aux essais : la publication exige un point d’accès HTTPS stable et public (leçon 5).
Recharger après chaque modification
ChatGPT garde en mémoire les métadonnées de votre serveur. Après tout changement de nom, de description, de schéma, d’annotation, d’authentification ou de ressource d’interface : redéployez ou relancez le serveur, ouvrez la connexion sur chatgpt.com/plugins, sélectionnez Refresh, vérifiez que les métadonnées annoncées ont changé, puis ouvrez une nouvelle conversation pour rejouer vos tests. Oublier cette étape, c’est tester l’ancienne version sans le savoir.
Points clés à retenir
- Un plugin personnel se crée depuis Add custom MCP server, puis s’invoque par
@dans ChatGPT - Les SDK officiels :
@modelcontextprotocol/sdk(TypeScript) etmcp(Python) ; épinglez la v1 avecext-apps1.x pour suivre les exemples d’OpenAI - Un serveur minimal :
McpServer,registerTool, transport streamable HTTP sur/mcp - MCP Inspector pour tester en local, un tunnel pour les essais dans ChatGPT
- Après toute modification de métadonnées : Refresh, puis nouvelle conversation