1. Pourquoi une leçon séparée sur le debug local
Dans les modules précédents, nous avons déjà vu comment sont structurés Apps SDK et MCP. Parlons maintenant de l’utilité d’une leçon séparée sur le debug local.
Beaucoup font ainsi : « Bon, j’ouvre ChatGPT, j’écris “utilise mon App”, puis je regarde ce qu’il dit. Si ça ne marche pas — je réécris du code au hasard ». C’est un peu comme réparer un backend en ne regardant que la page HTML dans le navigateur sans jamais ouvrir les logs du serveur.
Avec les ChatGPT Apps, il est particulièrement facile de basculer dans la magie : il y a GPT, il décide lui‑même d’appeler un tool ou non, il a sa propre logique d’erreurs. Si vous ne voyez pas ce qui se passe sous le capot, le debug se transforme en tambour chamanique.
Notre objectif : en faire un processus d’ingénierie normal :
- vous savez où regarder les logs de Next/MCP ;
- vous savez appeler manuellement le serveur MCP via l’inspector ;
- vous comprenez ce que vérifie Dev Mode et comment vous assurer que ChatGPT peut effectivement atteindre votre serveur.
Et surtout : vous cessez de débugger via la « devinette GPT » et commencez par vérifier les couches basses de la pile — le serveur et le protocole, puis seulement l’UI et le comportement du modèle.
2. Modèle mental : trois niveaux de debug
Pour ne pas se noyer dans le chaos, convenons de penser le debug en termes de trois niveaux. C’est notre petit « mille‑feuille » :
| Niveau | Ce qui y vit | Symptômes typiques | Outils de debug |
|---|---|---|---|
| UI (widget) | Composants React, state, window.openai | Widget vide/gris, rendu bancal, boutons inactifs | DevTools du navigateur |
| Backend / serveur MCP | tools, accès à la base de données/API | erreurs 500, « le tool a crashé », données étranges | logs du serveur, MCP Inspector |
| Protocole MCP | JSON‑RPC, tools/list, tools/call, schémas | GPT écrit « impossible d’appeler l’outil », invalid params | inspector + logs des requêtes |
Au deuxième niveau, on s’intéresse à ce que fait le serveur MCP lui‑même (tools, base de données, API), et au troisième — aux « fils » et au format des messages MCP (JSON‑RPC, schémas, etc.).
Ce trio constitue la base du plan de la leçon et du cours sur le debug.
Pour visualiser, regardons le flux d’une requête :
sequenceDiagram
participant User as Utilisateur
participant ChatGPT as ChatGPT (Dev Mode)
participant Tunnel as Tunnel (ngrok/CF)
participant Next as Next.js + MCP
User->>ChatGPT: "Choisis un cadeau jusqu’à 50 $"
ChatGPT->>Next: tools/call search_gifts (via tunnel)
Next->>Next: Appel du tool MCP, accès à la base de données/API
Next-->>ChatGPT: Résultat JSON-RPC + ToolOutput
ChatGPT-->>User: Réponse + rendu du widget
Une panne peut survenir à n’importe quel point : tunnel, endpoint, logique MCP, schéma JSON, widget React. Votre tâche lors du debug — identifier dans quelle couche se situe l’erreur, au lieu de tout réécrire d’emblée.
3. Logs Next.js et MCP : la base de tout
Commençons par le plus ennuyeux et le plus utile — les logs.
Où vivent les logs en développement local
Dans le template standard Apps SDK sur Next.js, le serveur MCP est généralement enveloppé dans une route API (/api/mcp ou similaire). Vous lancez npm run dev, et dans un terminal vous avez :
- le serveur de dev Next.js ;
- un handler pour l’endpoint MCP, qui reçoit les requêtes JSON‑RPC tools/list, tools/call, etc. ;
- l’impression de tout le fun via console.log/console.error.
Si vous avez isolé MCP dans un processus séparé, il y aura un deuxième terminal, mais l’idée reste la même : tout ce qui est intéressant est visible dans la console.
Important à distinguer :
- erreurs de build/démarrage — next dev ne démarre pas, TypeScript plante, import incorrect, etc. ;
- erreurs d’exécution — tout est lancé, mais une requête spécifique sur /api/mcp fait tomber un tool.
Next.js, en mode dev, affiche aussi les erreurs runtime avec un joli overlay et écrit la stack trace dans la console.
Que logger dans le serveur MCP
Bien que MCP utilise le protocole JSON‑RPC, pour le debug vous n’avez pas besoin d’imprimer tout le JSON. Des logs structurés mais courts sont bien plus utiles.
Bonne pratique pour les logs MCP — logger au minimum : timestamp, request_id/traceId, le nom du tool, les paramètres (anonymisés), le statut (ok/error) et le temps d’exécution.
Un logger.ts tout simple pour GiftGenius peut ressembler à ceci :
// src/lib/logger.ts
export function logToolEvent(
phase: "start" | "end" | "error",
data: Record<string, unknown>
) {
const ts = new Date().toISOString();
console.log(JSON.stringify({ ts, phase, ...data }));
}
Et dans le handler du tool :
// src/mcp/tools/searchGifts.ts
import { logToolEvent } from "@/lib/logger";
export async function searchGiftsTool(args: { q: string }) {
const traceId = crypto.randomUUID();
logToolEvent("start", { tool: "search_gifts", traceId, args });
try {
// ... recherche réelle de cadeaux ...
const results = []; // valeur factice
logToolEvent("end", { tool: "search_gifts", traceId, count: results.length });
return results;
} catch (err) {
logToolEvent("error", { tool: "search_gifts", traceId, error: String(err) });
throw err;
}
}
Deux subtilités importantes.
Premièrement, n’enregistrez pas d’e‑mails complets, numéros de téléphone, numéros de carte, tokens dans les logs. Ce n’est pas seulement inélégant, mais aussi contraire aux bonnes pratiques de sécurité MCP.
Deuxièmement, traceId — votre meilleur ami. Quand vous regardez ensemble les logs Next.js et MCP, il permet de corréler les événements : une requête précise tools/call, le rendu React correspondant et le log réseau du widget.
Comment comprendre via les logs où ça a cassé
Vous avez un terminal, il affiche des lignes JSON de logToolEvent. Scénario typique :
- arrive phase: "start" avec tool: "search_gifts" ;
- pas de phase: "end", mais un phase: "error" et une stack trace ;
- cela montre que le tool a bien atteint votre logique, mais a cassé à l’intérieur — par exemple, appel à une API externe, parsing, accès à la base de données.
Si vous ne voyez aucun log pour ce nom de tool — la requête n’est même pas arrivée au tool. Remontez alors dans la pile : tunnel, endpoint /mcp, requête JSON tools/call.
4. MCP Inspector : débugger MCP avant ChatGPT
Si les logs sont vos yeux, alors MCP Inspector (ou MCPJam Inspector) est votre microscope.
En savoir plus sur MCP Inspector et son utilité
Dans le module sur MCP, nous avons déjà branché l’Inspector pour vérifier un serveur « Hello, MCP ». Ici, on l’utilise comme outil de debug principal : on s’assure d’abord que MCP vit par lui‑même, puis seulement on passe au Dev Mode et à l’UI.
L’Inspector est une application séparée (le plus souvent un web‑UI plus CLI) qui joue le client MCP. Il se connecte à votre serveur via HTTP/SSE ou stdin/stdout, exécute tools/list, tools/call et affiche les messages JSON bruts, le handshake, la liste des tools, les ressources, etc.
L’idée principale : retirer ChatGPT de l’équation. Si votre tool ne fonctionne pas, vous voulez d’abord savoir si le serveur est vivant, si le protocole et le schéma sont corrects, avant d’accuser GPT.
Mini‑flux de travail avec l’Inspector
Scénario typique de debug local :
- Lancez npm run dev pour démarrer Next.js + l’endpoint MCP.
- Lancez MCP Inspector, par exemple :
npx @modelcontextprotocol/inspector
(la commande exacte dépend de l’outil utilisé).
- Dans l’Inspector, indiquez l’URL de votre endpoint MCP, par exemple http://localhost:3000/api/mcp (ou le tunnel HTTPS si vous voulez le vérifier aussi).
- Regardez si le handshake passe : le serveur doit répondre avec les capabilities supportées, la liste des tools, des ressources, etc.
- Appelez manuellement le tool qui vous intéresse : choisissez search_gifts, saisissez des arguments {"q": "pour une femme de moins de 30 ans"}, cliquez « Call tool » et vérifiez :
- si une réponse est arrivée ;
- s’il n’y a pas d’erreur JSON‑RPC ou MCP ;
- ce que le serveur écrit dans les logs pour cet appel.
Si dans l’Inspector tout plante déjà, pas la peine d’ouvrir ChatGPT : corrigez le serveur MCP.
Si dans l’Inspector tout est OK mais ChatGPT se plaint quand même — le problème est plus haut : URL du Dev Mode, autorisation, comportement du modèle.
Exemple « tool volontairement cassé »
Prenons notre search_gifts et cassons‑le volontairement :
export async function searchGiftsTool(args: { q: string }) {
if (args.q === "tombe_en_panne") {
throw new Error("Erreur pédagogique pour la démonstration du debug");
}
// ... logique normale ...
return [];
}
Ensuite :
- Dans l’Inspector, appelez search_gifts avec l’argument {"q": "tombe_en_panne"}.
- Dans les logs, voyez phase: "error" et la stack trace.
- Vérifiez que le serveur MCP renvoie bien une erreur.
Plus tard, quand vous brancherez tout cela au ChatGPT Dev Mode et demanderez au modèle « choisis un cadeau avec le mot "tombe_en_panne" », il tentera d’appeler le tool et affichera à l’utilisateur un message du type « I encountered an error running the tool ». On voit : l’erreur ne vient pas du modèle, mais de votre exception explicite.
Cette méthode entraîne bien la pensée : vous distinguez clairement l’erreur métier (nous avons nous‑mêmes lancé un Error) de l’erreur de protocole (JSON cassé, nom de tool incorrect, etc.).
5. Debug du widget : DevTools, state et « debug‑banner »
Quand le serveur MCP est à peu près clair, passons au frontend — le widget de l’Apps SDK.
Où et comment regarder les erreurs du widget
Votre widget est rendu dans ChatGPT dans un iframe sandbox. Bonne nouvelle : cet iframe a les mêmes DevTools de navigateur.
Mini‑procédure :
- Ouvrez ChatGPT dans votre navigateur (Chrome/Edge/Firefox).
- Ouvrez les DevTools (généralement F12 ou Ctrl+Shift+I).
- Onglet Console — choisissez le contexte du frame où vit votre widget (souvent le domaine web-sandbox.oaiusercontent.com).
- Rafraîchissez le chat/envoyez un message pour que GPT affiche votre App.
Si le widget :
- n’apparaît pas du tout ;
- apparaît gris/vide ;
- affiche une erreur rouge dans la console
— c’est presque certainement un problème de code React : propriété inaccessible, import incorrect, hook mal implémenté, etc.
L’onglet Network est aussi utile. Vous y verrez :
- le chargement du bundle JS de votre application (si 404/500 — problème côté serveur de dev/tunnel) ;
- les requêtes que votre widget émet via window.fetch, et les réponses 4xx/5xx.
Debug‑banner minimal
Astuce très pratique — ajouter dans le composant racine du widget une petite « debug‑banner » qui, en Dev Mode, affiche l’environnement et la version du build.
Par exemple :
// src/components/DebugBanner.tsx
export function DebugBanner() {
if (process.env.NODE_ENV !== "development") return null;
return (
<div style={{ padding: 4, background: "#222", color: "#0f0", fontSize: 10 }}>
ENV: dev | build: local | {new Date().toLocaleTimeString()}
</div>
);
}
Et dans le composant racine du widget :
// src/app/widget/page.tsx
import { DebugBanner } from "@/components/DebugBanner";
export default function GiftGeniusWidget() {
return (
<div>
<DebugBanner />
{/* reste de l'UI de recherche de cadeaux */}
</div>
);
}
Si vous avez ouvert ChatGPT, lancé l’App, mais ne voyez pas la bannière — c’est que votre JS n’est pas du tout arrivé au navigateur : erreur de build, problème d’endpoint, ou widget tout simplement non enregistré dans le serveur MCP.
State local et gestion des erreurs
Votre widget sait déjà afficher différents états : chargement, succès, erreur. Sinon — c’est le moment d’ajouter cela.
Mini‑pattern :
const [status, setStatus] = useState<"idle"|"loading"|"error"|"success">("idle");
async function handleSearch(query: string) {
try {
setStatus("loading");
// appel du tool MCP via window.openai.callTool ou le hook Apps SDK
setStatus("success");
} catch (e) {
console.error("Search failed", e);
setStatus("error");
}
}
Dans le JSX :
{status === "error" && (
<div style={{ color: "red" }}>Un problème est survenu, veuillez réessayer.</div>
)}
Pour le debug, il est crucial que :
- vous ne gobiez pas les exceptions (sinon la console est vide et l’UI « gèle ») ;
- vous reflétiez explicitement l’erreur dans l’UI, sinon l’utilisateur aura l’impression que l’App est morte.
6. Dev Mode comme partie du debug : ce qu’il fait et comment éviter de l’accuser à tort
Intégrons maintenant ChatGPT Dev Mode au tableau. Jusqu’ici, nous avons considéré uniquement votre code. Mais parfois tout fonctionne en local, tout est parfait dans l’Inspector, et ChatGPT répond quand même « Error talking to [AppName] » ou ne propose même pas votre App.
Ce que fait Dev Mode
Dev Mode — c’est le mode de ChatGPT où vous pouvez :
- créer et éditer vos Apps ;
- indiquer l’endpoint du serveur MCP (souvent https://votre-domaine/mcp ou /api/mcp) ;
- mettre à jour rapidement le manifeste et les métadonnées sans publication dans le Store.
Du point de vue du debug, Dev Mode n’est qu’une couche de configuration supplémentaire :
- si l’URL y est incorrecte ;
- si vous avez oublié /mcp à la fin ;
- si le tunnel a donné un nouveau domaine et que vous n’avez pas mis à jour les réglages
— ChatGPT ne peut tout simplement pas atteindre votre serveur.
Scénario typique de panne du Dev Mode
Classique :
- Vous avez monté un tunnel https://abcd.ngrok.io, vous l’avez indiqué dans Dev Mode, tout fonctionnait.
- Le lendemain, vous relancez ngrok et obtenez https://efgh.ngrok.io.
- Dans Dev Mode, c’est toujours https://abcd.ngrok.io/mcp.
- ChatGPT écrit « Error talking to GiftGenius ».
MCP Inspector, pointé sur http://localhost:3000/api/mcp, montre que tout va bien. Cela signifie que MCP vit, mais que ChatGPT regarde au mauvais endroit.
Solution : ouvrez les paramètres du Dev Mode, mettez à jour l’URL, sans oublier /mcp à la fin.
Dev Mode vs Store
Dans cette leçon, nous ne parlons que du Dev Mode — c’est votre bac à sable. Ici, il est normal de changer souvent l’URL, de reconnecter le tunnel, de modifier le schéma des tools.
Quand vous irez ensuite vers le Store, l’endpoint sera plus figé, et ces gymnastiques ne seront plus une bonne idée. Mais le Store, ce sera pour plus tard ; pour l’instant, cassez et réparez sereinement en Dev Mode.
7. Mini‑algorithme de debug : que faire quand « rien ne marche »
Assemblons maintenant tout cela en un algorithme pratique. En substance, ce sont les mêmes trois niveaux de debug du début de la leçon, mais écrits comme une séquence d’étapes.
Supposons que vous ouvrez ChatGPT, choisissez GiftGenius, et demandez « Choisis un cadeau jusqu’à 30 $ pour un ami geek », et :
- GPT ne dit rien à propos de votre App ;
- ou il écrit « Error talking to GiftGenius » ;
- ou le widget s’ouvre vide/gris.
Comment ne pas désespérer ?
Étape 1 (niveau MCP/serveur). Vérifier MCP via l’Inspector et les logs
Ignorez d’abord GPT et l’UI. Seul le serveur nous intéresse.
- Assurez‑vous que npm run dev est lancé et que l’endpoint (/api/mcp) répond.
- Connectez MCP Inspector à http://localhost:3000/api/mcp ou à votre tunnel.
- Vérifiez le handshake — la liste des tools doit s’afficher.
- Appelez manuellement le même tool que GPT est censé déclencher (par exemple, search_gifts), avec des arguments proches.
Si déjà ici tout s’écroule — corrigez MCP : schémas, logique métier, appels réseau. Utilisez les logs et le traceId pour comprendre ce qui casse.
Étape 2 (niveau protocole/Dev Mode). Vérifier Dev Mode et l’URL
Si dans l’Inspector tout est parfait mais que ChatGPT ne voit toujours pas votre App ou parle de problèmes de connexion :
- Ouvrez les paramètres Dev Mode de votre App.
- Regardez quelle URL y est indiquée pour MCP.
- Comparez avec ce que votre serveur/tunnel écoute réellement (et n’oubliez pas de vérifier qu’il y a /mcp à la fin si votre serveur l’exige).
Le problème se trouve souvent ici.
Étape 3 (niveau UI). Vérifier le widget via DevTools
Si ChatGPT appelle bien les tools (visible dans les logs MCP), mais que le widget se comporte étrangement :
- Ouvrez DevTools dans le navigateur sur la page de ChatGPT.
- Onglet Console — choisissez le contexte de l’iframe de votre widget.
- Regardez les erreurs JS.
- Onglet Network — assurez‑vous que :
- le bundle JS du widget se charge sans 404/500 ;
- les requêtes supplémentaires (via fetch/window.openai.fetch) renvoient des réponses pertinentes.
En parallèle, regardez votre DebugBanner : si elle n’apparaît pas, c’est que vous n’êtes pas du tout arrivés à l’arbre React.
Étape 4. Utiliser Dev Mode pour reproduire un bug report
Quand vous recevez un bug report d’un collègue/utilisateur, essayez de conserver le prompt exact sur lequel ça a cassé. En Dev Mode, on peut très vite reproduire le scénario :
- Lancer npm run dev, monter le tunnel.
- Dans Dev Mode, choisir l’App.
- Coller le prompt problématique.
- En parallèle :
- regarder quelles requêtes JSON arrivent sur MCP dans les logs ;
- dans l’Inspector, si besoin, répéter tools/call avec les mêmes arguments.
Vous transformez ainsi « parfois, quelque chose ne marche pas » en scénario reproductible.
8. Quelques touches de code pour un debug confortable
Pour ancrer la matière, ajoutons encore deux extraits utiles à notre application GiftGenius.
Configuration d’environnement et niveaux de logging
Quelque part dans la configuration du serveur, il est pratique d’indiquer explicitement l’endpoint MCP et le niveau de logging :
// src/config.ts
export const config = {
mcpEndpoint:
process.env.NODE_ENV === "development"
? "http://localhost:3000/api/mcp" // le tunnel couvre ceci
: "https://api.giftgenius.com/api/mcp",
logLevel: process.env.NODE_ENV === "development" ? "DEBUG" : "ERROR",
};
Et dans logToolEvent, vous pouvez tenir compte de logLevel pour éviter de spammer en prod.
Logger des erreurs structurées MCP
Lors du traitement des tools, essayez d’attraper les erreurs attendues et de renvoyer des messages compréhensibles, au lieu de tout faire tomber avec un throw :
export async function searchGiftsTool(args: { q: string }) {
const traceId = crypto.randomUUID();
logToolEvent("start", { tool: "search_gifts", traceId, args });
try {
// ... code normal ...
return { content: [{ type: "text", text: "3 cadeaux trouvés" }] };
} catch (err) {
logToolEvent("error", { tool: "search_gifts", traceId, error: String(err) });
return {
content: [{ type: "text", text: "Erreur de recherche de cadeaux. Réessayez plus tard." }],
isError: true,
};
}
}
Ainsi, ChatGPT verra que le résultat est marqué isError et pourra annoncer correctement le problème à l’utilisateur, tandis que vous — verrez ce qui s’est passé dans les logs.
9. Erreurs typiques lors du debug local de ChatGPT App
Erreur n° 1 : débugger « via GPT » au lieu de via le serveur et l’inspector.
Il est très tentant de simplement regarder ce que répond le modèle et d’essayer de deviner où est le bug. Mais le modèle — c’est la couche la plus haute. Si le serveur MCP ne fonctionne pas par lui‑même (à la main, via l’Inspector) — n’attendez pas de miracles de GPT. Obtenez d’abord un MCP stable, puis branchez ChatGPT.
Erreur n° 2 : ne pas regarder les logs du tout ou tout logger sans discernement.
L’absence de logs rend aveugle : vous ne savez pas quel tool a été appelé, avec quels arguments, et comment cela s’est terminé. À l’inverse, le sur‑logging transforme la console en « matrice » de lignes sans lien. Mieux vaut avoir un log compact et structuré avec tool, args (anonymisés), traceId, status et le temps d’exécution.
Erreur n° 3 : stocker des données sensibles dans les logs.
Logger des tokens, des e‑mails complets et des numéros de cartes — mauvaise pratique du point de vue sécurité et politique OpenAI. Les logs ne doivent contenir que l’information qui aide réellement le debug ; les données personnelles — masquées ou pas écrites du tout.
Erreur n° 4 : accuser Dev Mode de tous les maux.
Dev Mode devient souvent le bouc émissaire : « OpenAI a dû casser quelque chose ». En réalité, très souvent, vous avez oublié de mettre à jour l’URL après avoir relancé le tunnel ou indiqué le mauvais chemin (/ au lieu de /mcp). Avant d’écrire au support, ouvrez les paramètres du Dev Mode et alignez l’endpoint avec l’adresse réelle du serveur.
Erreur n° 5 : ignorer DevTools et une erreur dans le widget.
Un widget vide ou gris signifie presque toujours une erreur JavaScript côté client. Si vous ne regardez que les logs MCP et n’ouvrez pas DevTools dans ChatGPT, vous ne voyez que la moitié du tableau. L’habitude d’appuyer sur F12 et de regarder Console/Network vous sauvera des heures.
Erreur n° 6 : tenter de « réparer » un bug par des délais magiques.
Parfois on a envie de faire un setTimeout ou un délai à la Thread.sleep « pour que tout ait le temps de charger ». Dans le monde MCP/Next/React, c’est presque toujours un mauvais remède : le problème se trouve d’ordinaire dans le schéma, l’endpoint incorrect ou une erreur de code, pas dans le fait que « le serveur n’a pas eu le temps ». Mieux vaut comprendre où est exactement la rupture (Inspector → Dev Mode → widget) que de la combler par des délais.
Erreur n° 7 : déployer sur Vercel sans s’assurer que tout fonctionne en local.
L’envie d’« aller en prod plus vite » est compréhensible, mais pousser un MCP cassé sur Vercel — c’est le meilleur moyen d’obtenir deux niveaux de problèmes : local et production. Dans ce module, nous exigeons volontairement : d’abord MCP Jam/Inspector → tout OK, Dev Mode → scénarios de base fonctionnels, et seulement ensuite le déploiement.
GO TO FULL VERSION