1. Problème des tunnels « aléatoires »
Lorsque vous lancez pour la première fois ngrok http 3000 ou un Cloudflare Quick Tunnel rapide, c’est presque magique : et hop — votre http://localhost:3000 devient https://random-1234.tunnelprovider.com. Vous pouvez copier l’URL dans le ChatGPT Dev Mode, et GPT charge votre App avec joie.
Puis vous redémarrez le tunnel… et vous obtenez un nouveau domaine. L’ancien URL dans la configuration de l’App Dev dans ChatGPT se transforme soudain en « lien cassé », GPT affiche honnêtement « App unavailable », et vous retournez dans les paramètres, changez l’URL, cliquez sur Save, attendez que ça se mette à jour, tout en détestant silencieusement toute cette stack.
Pour « jouer un soir », c’est tolérable. Mais quand vous :
- peaufinez l’App chaque jour ;
- voulez montrer une version intermédiaire à un collègue/manager ;
- montez en parallèle un staging et une production,
reconnecter le Dev Mode à chaque nouvel URL aléatoire devient une vraie souffrance.
En plus, si vous avez déjà ajouté dans l’application quelque chose qui dépend du domaine (par exemple un OAuth redirect URI ou des webhooks), chaque nouvel URL les casse également. Effet domino : vous changez de tunnel — et il faut corriger la configuration de l’App, l’URL de redirection chez le fournisseur OAuth et la configuration du récepteur de webhook.
Il en découle l’idée clé de ce cours : un dev‑URL stable n’est pas un luxe, c’est un moyen de préserver la santé mentale du développeur.
Insight
ChatGPT impose des délais d’attente très stricts pour travailler avec votre application, et on les sous‑estime facilement. Un appel d’outil MCP a une limite de temps : au maximum 2 minutes — après cela, la plateforme considère simplement l’appel comme un échec, même si votre serveur est encore en train de faire quelque chose.
C’est encore plus strict pour l’enregistrement de l’application (Store ou Dev Mode) : ChatGPT accorde environ 20 secondes pour lire le manifeste, les ressources et les descriptions des outils. Si, dans ce délai, votre serveur MCP n’a pas eu le temps de s’initialiser, de renvoyer la liste des tools/resources, etc., l’enregistrement de l’App échouera par timeout.
Recommandation : toute l’initialisation lourde doit se produire avant d’aller dans le Dev Mode ou le Store. Pré‑chauffer les connexions à la base de données, charger de gros fichiers de configuration, remplir des caches paresseux — tout cela est préférable à exécuter à l’avance, par exemple en appelant une fois le serveur via MCP Jam ou un script interne dédié. Du point de vue de la plateforme, le serveur MCP doit être « chaud » et répondre en quelques secondes, pas « se réveiller » au moment de l’enregistrement.
2. Qu’est‑ce qu’un tunnel « mature »
Clarifions ce qui distingue un tunnel « mature » de celui que vous avez lancé au début du cours.
Le mode initial (module 2) ressemblait à ceci :
# Exemple avec ngrok
ngrok http 3000
# Résultat : https://random-abc123.ngrok-free.app
Vous preniez cet URL jetable et le colliez dans le Dev Mode. Au lancement suivant d’ngrok, l’URL est déjà différent, et la configuration ChatGPT est périmée.
Dans l’approche « mature », vous avez :
- un sous‑domaine statique chez le fournisseur de tunnel (ou votre propre domaine) ;
- le même domaine est toujours redirigé vers votre localhost:3000 ;
- vous pouvez redémarrer le tunnel, la machine, le routeur, l’URL reste le même.
De tels sous‑domaines statiques sont disponibles, par exemple :
- dans ngrok — un domaine statique gratuit par compte ;
- dans Cloudflare Tunnel — via un tunnel nommé et l’attachement à votre domaine.
Et l’application ChatGPT en Dev Mode est configurée exactement sur cet unique URL et ne vous embête plus.
D’un point de vue formel, les exigences pour notre tunnel « mature » sont les suivantes :
- toujours le même domaine public HTTPS ;
- un certificat TLS valide (le fournisseur s’en charge) ;
- une configuration qui décrit : « tout ce qui arrive sur https://dev.yourdomain.com, proxie‑le vers http://localhost:3000 » ;
- optionnel — des mesures de sécurité minimales (au moins ne pas exposer l’URL sur Stack Overflow).
3. Configurer un dev‑URL stable : exemple avec Cloudflare Tunnel
Dans le cours, nous recommandons Cloudflare Tunnel comme outil principal, car il convient aussi bien au dev qu’à des scénarios plus sérieux. Vous avez déjà vu un exemple de configuration de base au module 2, et maintenant nous allons l’amener jusqu’à un dev‑URL permanent.
Supposons que nous ayons l’application d’exemple GiftGenius et que nous souhaitions l’URL stable giftgenius-dev.yourdomain.com.
Étapes minimales (simplifiées, sans dépendre de l’UI Cloudflare) :
- Lier le domaine à votre compte Cloudflare (une fois, via leur console).
- Installer cloudflared en local et se connecter.
brew install cloudflare/cloudflare/cloudflared # macOS
cloudflared login # ouvrira le navigateur pour l’authentification
3. Créer un tunnel nommé :
cloudflared tunnel create giftgenius-dev
4. Configurer l’itinéraire dans ~/.cloudflared/config.yml :
tunnel: giftgenius-dev
credentials-file: /Users/you/.cloudflared/giftgenius-dev.json
ingress:
- hostname: giftgenius-dev.yourdomain.com
service: http://localhost:3000 # notre serveur Next.js de dev
- service: http_status:404
5. Démarrer le tunnel :
cloudflared tunnel run giftgenius-dev
Désormais, tant que npm run dev et cloudflared tunnel run tournent, votre Next.js local est accessible via l’URL permanent https://giftgenius-dev.yourdomain.com. Et c’est précisément celui que vous indiquez dans les paramètres du ChatGPT Dev Mode.
Comment cela s’intègre à notre application
Si vous ouvrez dans le navigateur l’URL de votre application que vous saisissez dans ChatGPT lors du branchement de l’App Dev :
https://giftgenius-dev.yourdomain.com/mcp
vous verrez une réponse (erreur) — quelque chose comme :
{"jsonrpc":"2.0","error":{"code":-32000,"message":"Method not allowed."},"id":null}
C’est tout à fait normal, car le serveur sur /mcp n’attend pas de requête GET. Toutes les autres parties de l’application — le widget, l’endpoint MCP /mcp, les routes d’API — passent par le même tunnel, vous n’avez pas à vous souvenir d’un nouveau domaine à chaque fois.
4. Alternative : sous‑domaine stable avec ngrok
Si vous êtes déjà habitué à ngrok, vous pouvez le rendre « mature » de la même manière en utilisant un domaine statique. Depuis 2023, ngrok propose même sur son offre gratuite la possibilité de réserver un sous‑domaine statique du type myapp-dev.ngrok-free.app.
Schéma minimal :
# ~/.config/ngrok/ngrok.yml
authtoken: <votre jeton>
tunnels:
giftgenius-dev:
addr: 3000
proto: http
domain: giftgenius-dev.ngrok-free.app
Lancement :
ngrok start giftgenius-dev
Au final, l’URL https://giftgenius-dev.ngrok-free.app sera permanent, et c’est celui que vous fournissez au ChatGPT Dev Mode comme URL de base de l’application.
La même philosophie :
- pas d’adresses « aléatoires » ;
- seul l’état interne du tunnel change (démarré/arrêté), pas le domaine ;
- pas besoin de reconnecter le Dev Mode.
Cloudflare et ngrok sont, de ce point de vue, simplement deux parfums de glace. Certains préfèrent leurs propres domaines et un contrôle DNS « fin » (Cloudflare), d’autres préfèrent « on configure un YAML — c’est prêt » (ngrok). Pour le cours, les deux approches sont valides, l’essentiel étant un URL stable.
5. Schéma : ChatGPT Dev Mode ↔ tunnel ↔ stack local
Pour formaliser un peu ce qui se passe, dessinons un schéma.
flowchart TD
ChatGPT["ChatGPT (Dev Mode)"]
AppCfg["Dev App (config : https://giftgenius-dev...)"]
Tunnel["Tunnel Cloudflare/ngrok (giftgenius-dev...)"]
Next["Serveur Next.js de dev localhost:3000 + gestionnaire MCP"]
ChatGPT --> AppCfg
AppCfg -->|"dans la config : https://giftgenius-dev.../.well-known/openai-app"| Tunnel
Tunnel -->|"proxy HTTPS → HTTP"| Next
ChatGPT ne sait jamais ce que vous exécutez sur votre ordinateur portable. Pour lui, il n’y a qu’un seul endpoint HTTPS. Ce qui se trouve derrière — Vercel, un tunnel local, Kubernetes — c’est votre affaire. Et dans cette leçon, ce qui nous intéresse, c’est la stabilité de cet endpoint HTTPS pour le développement local.
Il reste à faire en sorte qu’au sein de notre application cette adresse soit aussi une « source de vérité » unique, et non pas dispersée dans des chaînes en dur — c’est l’objet de la section suivante.
6. Variables d’environnement et baseURL dans le code
Pour que tout cela fonctionne sans surprise, il est utile de définir une fois pour toutes dans le code Next.js « l’URL externe de base de l’application » et de s’y référer partout.
Par exemple, dans le dossier app/lib/config.ts de notre application GiftGenius, on peut déclarer :
// app/lib/config.ts
export const baseUrl =
process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000"; // fallback
export const mcpEndpoint = `${baseUrl}/mcp`; // URL du serveur MCP
Et dans .env.local en développement, définir :
NEXT_PUBLIC_APP_URL=https://giftgenius-dev.yourdomain.com
Alors :
- dans le widget et pour tous les liens, vous utilisez toujours baseUrl ;
- pour le ChatGPT Dev Mode et le navigateur, tout reste cohérent ;
- si demain vous migrez vers un staging Vercel avec le domaine https://giftgenius-staging.vercel.app, il suffit de changer uniquement la variable d’environnement.
C’est particulièrement important pour :
- les callback‑URL (par exemple pour OAuth, les gestionnaires de webhook) ;
- les liens que vous affichez à l’utilisateur dans le widget (bouton « Ouvrir dans le navigateur » via openExternal) ;
- tout URL absolu dans la logique applicative.
Nous ne parlons ici que du dev‑URL, mais l’idée d’architecture « une seule source de vérité pour baseUrl » fonctionne très bien et vous accompagnera sereinement vers le staging/production.
7. Mettre à jour l’URL dans le ChatGPT Dev Mode
OK, nous nous sommes offert un joli domaine stable. Comment vivre avec dans le Dev Mode ?
La logique est la suivante :
- Dans les paramètres de l’App Dev, vous indiquez une fois l’URL racine : https://giftgenius-dev.yourdomain.com/
- ChatGPT l’utilise pour récupérer le manifeste (.well-known/openai-app) puis s’appuie sur ces mêmes racines pour joindre MCP (/mcp), les statiques, etc.
- Si vous ne changez que le code (widget React, handlers MCP, styles), il n’y a aucun besoin de changer l’URL. Il suffit que le tunnel soit démarré et que le serveur Next.js réponde.
- Si vous changez le domaine lui‑même (rare, par exemple de ngrok à Cloudflare), vous devez aller une fois dans le Dev Mode et modifier l’endpoint.
Dans certains cas, ChatGPT met en cache le manifeste et les changements peuvent ne pas apparaître instantanément. Dans l’interface du Dev Mode, il y a généralement un bouton du type « Reload configuration / Refresh App », mais dans le pire des cas, « déconnecter puis reconnecter l’App avec le même URL » suffit.
Important : tant que vous ne changez pas l’URL, le Dev Mode « capte » automatiquement les nouvelles versions du code. Le déclencheur principal pour l’App est le domaine, pas le commit‑hash.
8. Basculer entre dev / staging / prod dans le Dev Mode
Un domaine de dev stable n’est que la première étape. Pour ne pas se noyer dans le chaos des URL au fur et à mesure que le projet grandit, il est utile de comprendre dès le départ comment le tunnel de dev s’intègre dans le schéma global des environnements (dev/staging/prod) et du Dev Mode. Bien que staging et prod soient plutôt le sujet de la prochaine leçon sur Vercel, le Dev Mode sait déjà travailler avec plusieurs environnements.
Pour comprendre la suite, ce tableau est utile :
| Environnement | URL de base | Où tourne le code |
|---|---|---|
| Local | |
Next.js local + MCP via tunnel |
| Staging | |
Aperçu Vercel / déploiement de staging |
| Prod | |
Production Vercel |
Il existe deux façons d’utiliser le Dev Mode.
La première — une seule App Dev, mais vous mettez à jour son URL dans les paramètres de temps en temps pour tester le staging ou la prod (avec prudence). Cette approche convient au début, mais l’erreur est vite arrivée : aujourd’hui vous testiez le local, demain le staging, après‑demain vous oubliez de basculer et vous envoyez par inadvertance des requêtes en prod via l’App Dev.
La seconde — plus saine : plusieurs Apps Dev, chacune clairement liée à un environnement :
- GiftGenius Dev → giftgenius-dev.yourdomain.com ;
- GiftGenius Staging → giftgenius-staging.vercel.app ;
- GiftGenius (en production, via le Store) → giftgenius.vercel.app.
Dans cette leçon, nous avançons pas à pas pour mettre de l’ordre au moins dans le dev‑URL. Dans la prochaine leçon, vous verrez comment relier logiquement Vercel et les déploiements d’aperçu au staging/production.
9. Travail en équipe : plusieurs développeurs et un seul tunnel
Tant que nous avons un seul domaine de dev et un développeur introverti, le tunnel est votre ami. Mais dès qu’une équipe arrive sur le projet, les tunnels et environnements commencent à se croiser, et il est important d’éviter la « guerre pour un sous‑domaine ».
Imaginons que deux développeurs décident d’utiliser le même sous‑domaine statique, disons giftgenius-dev.ngrok-free.app. Tous deux lancent ngrok start giftgenius-dev chez eux. Au mieux, un tunnel ne se lèvera pas (conflit de domaine), au pire — vous vous « écraserez » mutuellement la session, et ChatGPT tombera tantôt chez l’un, tantôt chez l’autre.
Plusieurs stratégies sont possibles.
La plus simple — des domaines de dev personnels :
- alex.dev.giftgenius.app ;
- maria.dev.giftgenius.app.
Et chacun sa propre App Dev dans ChatGPT, par exemple GiftGenius Dev (Alex) et GiftGenius Dev (Maria). Ainsi chacun fait tourner son local tranquillement sans gêner l’autre.
Une voie plus « équipe » — un endpoint de staging commun :
- Tous les développeurs ont leur tunnel de dev personnel (pour le debug perso).
- En plus, il y a un staging sur Vercel où les branches de feature sont mergées et sur lequel pointe une App Dev commune GiftGenius Staging.
Cette approche est fréquente dans les équipes réelles :
- la fonctionnalité naît en local et se débug via un tunnel personnel ;
- après le pull‑request et le merge, elle est testée par tous via le staging (sans tunnel, juste via l’URL Vercel).
10. Sécurité du tunnel de dev (brièvement et sans paranoïa)
Un tunnel est un moyen pratique d’exposer votre serveur local sur internet. Et internet, comme nous le savons, est largement peuplé de bots, de scanners et de personnes qui aiment vérifier si vous n’avez pas oublié le mot de passe admin/admin.
Les bases auxquelles penser dès le dev :
- un tunnel donne un accès externe à tout ce qui écoute sur ce port ; n’y exposez pas aussi l’admin de la base, phpMyAdmin et « ma CRM de test sans mot de passe » ;
- ne publiez pas l’URL du tunnel dans des dépôts publics et des chats ;
- éteignez le tunnel quand vous ne travaillez pas (et éteignez aussi parfois l’ordinateur portable — lui aussi a besoin de dormir).
Des mesures plus sérieuses comme le Basic Auth, la vérification d’en‑têtes spécifiques ou un jeton dans l’URL seront abordées dans les modules sur la sécurité. L’essentiel pour l’instant : un tunnel est un outil de développement, pas un serveur sécurisé. La production vivra sur un hébergement sérieux, comme Vercel, avec d’autres mécanismes de protection.
11. Pratique : configurer un dev‑URL stable pour notre application
Passons maintenant de la théorie et des avertissements à la pratique : relions tout cela à notre application Next.js (template Apps SDK).
Supposons que la structure du projet ressemble à ceci :
apps/
web/ # Next.js App + widget
mcp-server/ # (optionnel) MCP séparé, ou /mcp handler dans web
En pratique, vous pouvez garder MCP directement dans Next.js, dans app/api/mcp/route.ts, mais le principe reste le même.
Étape 1. Modifier .env.local
Ajoutons‑y le dev‑URL stable du tunnel :
NEXT_PUBLIC_APP_URL=https://giftgenius-dev.yourdomain.com
Dans le code de dev, nous utilisions déjà baseUrl depuis cette variable (voir ci‑dessus). Si ce n’est pas le cas — c’est le bon moment pour l’extraire.
Étape 2. Lancer le serveur de dev et le tunnel
cd apps/web
npm run dev # Next.js sur localhost:3000
# terminal séparé
cloudflared tunnel run giftgenius-dev
Vérifiez dans le navigateur que https://giftgenius-dev.yourdomain.com s’ouvre et affiche votre App.
Étape 3. Connecter dans le ChatGPT Dev Mode
Dans l’interface de ChatGPT (section développeurs) :
- créez ou éditez GiftGenius Dev ;
- dans le champ URL/Endpoint, indiquez https://giftgenius-dev.yourdomain.com/ ;
- enregistrez.
Après cela, ChatGPT récupère le manifeste à l’emplacement /.well-known/openai-app, puis commence à lancer votre App sur ce domaine.
Vous pouvez désormais :
- modifier le code du widget, les handlers MCP, les styles ;
- redémarrer npm run dev ;
- redémarrer cloudflared tunnel run giftgenius-dev ;
tout en n’ayant plus jamais à toucher aux paramètres de l’App Dev, tant que le domaine reste le même.
12. Ce que cela donne dans la logique du code : exemple avec openExternal
Pour boucler l’exemple avec ce que nous avons écrit dans les leçons précédentes, ajoutons dans le widget un bouton « Ouvrir l’interface complète dans le navigateur », qui utilisera lui aussi le dev‑URL stable.
Supposons que nous ayons un composant React de widget GiftWidget :
// app/components/GiftWidget.tsx
"use client";
import { baseUrl } from "../lib/config"; // on prend baseUrl depuis l'env
export function GiftWidget() {
const handleOpenFull = () => {
window.openai.openExternal({
// on ouvre la page de l’application dans un nouvel onglet
url: `${baseUrl}/full`,
label: "Ouvrir l’interface complète",
});
};
return (
<div>
<button onClick={handleOpenFull}>
Mode complet
</button>
</div>
);
}
Si NEXT_PUBLIC_APP_URL pointe vers le tunnel, alors :
- en développement local, la page https://giftgenius-dev.yourdomain.com/full s’ouvrira ;
- après le déploiement sur le staging — https://giftgenius-staging.vercel.app/full ;
- en prod — le domaine de production.
Encore une fois — une seule source de vérité pour le domaine : on change d’environnement, mais pas le code.
13. Mini‑stratégie : penser le tunnel de manière « mature »
En résumant dans un modèle mental simple, on peut considérer que :
- un tunnel n’est qu’un câble temporaire entre votre portable et un domaine public stable ;
- le ChatGPT Dev Mode ne connaît que le domaine, et il se fiche d’où le code tourne physiquement ;
- moins vous changez de domaine, moins vous passez de temps dans les réglages de ChatGPT et des fournisseurs OAuth ;
- le tunnel de dev n’est qu’une ligne sur votre carte des environnements, à côté du staging (aperçu Vercel) et de la prod (production Vercel).
La prochaine leçon montrera justement comment ce câble est remplacé par un hébergement complet sur Vercel, et comment relier tout cela aux branches Git, aux déploiements d’aperçu et à la production.
14. Erreurs typiques avec un tunnel « mature »
Erreur n° 1 : « J’ai configuré un domaine statique, mais j’utilise quand même des URL aléatoires ».
Parfois, un développeur crée une fois un joli giftgenius-dev.yourdomain.com, mais par habitude lance ngrok http 3000 sans config. Résultat : ChatGPT regarde un domaine, tandis que le code tourne derrière un autre. Si vous avez déjà un dev‑URL stable — utilisez‑le exclusivement et lancez le tunnel via la config (tunnel/profil nommé).
Erreur n° 2 : localhost:3000 en dur dans le code.
On le voit quand, dans un composant React ou un handler MCP, on écrit fetch("http://localhost:3000/api/..."). En local, ça marche encore plus ou moins, mais en Dev Mode, et a fortiori en staging/prod, ça casse immédiatement. Externalisez toujours l’URL de base dans une config (baseUrl, NEXT_PUBLIC_APP_URL) et utilisez‑la partout où des liens absolus sont nécessaires.
Erreur n° 3 : Modifier constamment l’URL dans le Dev Mode au lieu d’un tunnel stable.
Si vous vous surprenez à penser « bon, je change l’URL une fois de plus dans les paramètres, tant pis », c’est un signal. Configurer un sous‑domaine statique dans ngrok/Cloudflare prend 10–15 minutes une fois, mais fait gagner des heures au fil du développement.
Erreur n° 4 : Un domaine statique commun à l’équipe sans règles.
Deux développeurs, un domaine giftgenius-dev.ngrok-free.app, et chacun lance le tunnel quand il veut. Résultat — conflit de tunnels, réponses « mystérieusement » manquantes dans le Dev Mode, et debug du style « chez moi ça marchait ». Pour une équipe, choisissez soit des domaines de dev personnels, soit un domaine de staging sur un hébergement réel.
Erreur n° 5 : Le tunnel comme « presque la production ».
Parfois, quelqu’un se dit : « Puisque j’ai un URL HTTPS stable via un tunnel, faisons passer de vrais utilisateurs/paiements par là ». C’est le chemin vers la douleur : portable éteint — application morte, internet coupé — pareil, et la sécurité au mieux symbolique. Un tunnel est un outil de dev. Le trafic de production est destiné à Vercel et à une infrastructure mature, que nous aborderons dans la prochaine leçon.
Erreur n° 6 : Synchronisation oubliée des variables d’environnement et du Dev Mode.
Souvent, on modifie NEXT_PUBLIC_APP_URL dans .env.local, mais on oublie de changer l’URL dans le Dev Mode (ou inversement). Résultat : le widget génère des liens vers un domaine, tandis que ChatGPT s’adresse à un autre. Tenez un simple tableau « environnement ↔ domaine ↔ App dans ChatGPT » et mettez‑le à jour lors des changements — cela coûte moins cher que de deviner quel URL est le bon en ce moment.
GO TO FULL VERSION