1. 「ランダム」トンネルの問題
最初に ngrok http 3000 や手早い Cloudflare Quick Tunnel を立ち上げると、まるで魔法のように感じます。ぱっとあなたの http://localhost:3000 が https://random-1234.tunnelprovider.com に変わる。URL を ChatGPT の Dev Mode に貼れば、GPT はうれしそうにあなたの App を読み込みます。
ところがトンネルを再起動すると……新しいドメインに変わってしまいます。ChatGPT の Dev アプリ設定に入れていた古い URL は一瞬で「死んだリンク」になり、GPT は正直に「App unavailable」と表示。あなたはまた設定を開いて URL を変え、Save を押して反映を待ち、そしてこのスタック全体を静かに憎むことになります。
一晩だけ「ちょっと試す」なら耐えられます。でも次のような場合は:
- 毎日 App を改善している;
- 途中版を同僚やマネージャーに見せたい;
- 並行して staging と production も用意している,
新しいランダム URL のたびに Dev Mode を付け替えるのは純粋な苦行になります。
加えて、もしアプリにドメイン依存の設定(例えば OAuth の redirect URI や webhook)が入っていると、新しい URL はそれらも壊します。トンネルを変えたら、App の設定、OAuth プロバイダの redirect‑URL、webhook 受信側の設定も直す——というカスケードが発生します。
ここから導かれるこの講義の要点はこうです: 安定した dev‑URL は贅沢ではなく、開発者のメンタルを守るための必需品。
Insight
ChatGPT はあなたのアプリと対話するときのタイムアウトが非常に厳格で、過小評価しがちです。MCP のツール呼び出しには時間制限があり、最大 2 分です——それを過ぎると、サーバーが何かを実行中でもプラットフォームは失敗と見なします。
さらに厳しいのがアプリ登録(Store または Dev Mode)です。マニフェスト、リソース、ツールの説明の読み込みに ChatGPT が与えるのはおよそ 20 秒。もしその間に MCP サーバーの初期化や tools/resources の一覧返却などが間に合わないと、App の登録は timeout で落ちます。
推奨: 重い初期化は、Dev Mode や Store に進む前に済ませておきましょう。DB 接続のウォームアップ、大きな設定のロード、遅延キャッシュの構築——これらは事前に一度、MCP Jam や内部スクリプトでサーバーを叩いておくなどしてやっておくのがよいです。プラットフォームの観点では、MCP サーバーは「温まっていて」数秒で応答するべきで、登録の最中に「目覚める」べきではありません。
2. 「実務的な」トンネルとは
「実務的な」トンネルが、コース序盤で試したものとどう違うかを確認しておきましょう。
初期のやり方(モジュール 2)はこうでした:
# ngrok の例
ngrok http 3000
# 取得される URL: https://random-abc123.ngrok-free.app
この使い捨て URL を Dev Mode に貼り付ける。次に ngrok を起動したときには URL が別物になり、ChatGPT の設定が古くなる、という流れです。
「実務的」アプローチでは次のようにします:
- 静的サブドメイン(トンネルプロバイダ側、または自分のドメイン)を持つ;
- 同じドメインが常にあなたの localhost:3000 にフォワードされる;
- トンネルやマシン、ルーターを再起動しても、URL は変わらない。
こうした静的サブドメインは例えば次で利用できます:
- ngrok — アカウントにつき無料の静的ドメイン;
- Cloudflare Tunnel — 命名済みトンネルと自分のドメインの紐づけ。
そして ChatGPT の Dev Mode 側のアプリは、その 1 つの URL だけを見に行くよう設定され、あなたに余計な用事を持ち込みません。
形式的に、私たちの「実務的」トンネルの要件は次の通りです:
- 常に同じ公開 HTTPS ドメインで安定している;
- 有効な TLS 証明書(プロバイダが面倒を見てくれます);
- 「https://dev.yourdomain.com に来たものは http://localhost:3000 に流す」という設定;
- オプションで最低限のセキュリティ(少なくとも URL を StackOverflow などに晒さない)。
3. 安定した dev‑URL を設定する: Cloudflare Tunnel の例
このコースでは、Cloudflare Tunnel を主な道具として推奨します。dev にも、より本格的なシナリオにもハマるからです。モジュール 2 ですでに基本設定の例を見ましたが、ここではそれを「常設の dev‑URL」まで仕上げます。
学習用アプリ GiftGenius があり、安定した URL giftgenius-dev.yourdomain.com を使いたいと仮定しましょう。
最小の手順(Cloudflare の UI には依存しない簡略版):
- ドメインを Cloudflare アカウントに紐づける(1 回、管理画面で)。
- cloudflared をローカルに入れ、ログインする。
brew install cloudflare/cloudflare/cloudflared # macOS
cloudflared login # 認可のためにブラウザが開きます
3. 命名済みトンネルを作成:
cloudflared tunnel create giftgenius-dev
4. ~/.cloudflared/config.yml にルーティングを設定:
tunnel: giftgenius-dev
credentials-file: /Users/you/.cloudflared/giftgenius-dev.json
ingress:
- hostname: giftgenius-dev.yourdomain.com
service: http://localhost:3000 # 私たちの Next.js 開発サーバー
- service: http_status:404
5. トンネルを起動:
cloudflared tunnel run giftgenius-dev
これで、npm run dev と cloudflared tunnel run が動いている間、あなたのローカル Next.js は恒久的な URL https://giftgenius-dev.yourdomain.com でアクセス可能です。そして ChatGPT Dev Mode の設定では、まさにこの URL を指定します。
アプリとのつながり方
ChatGPT で Dev アプリを接続する際に入力するアプリの URL をブラウザで開くと:
https://giftgenius-dev.yourdomain.com/mcp
次のような応答(エラー)が見えるはずです:
{"jsonrpc":"2.0","error":{"code":-32000,"message":"Method not allowed."},"id":null}
これはまったく正常です。/mcp のサーバーは GET リクエストを想定していません。その他のパーツ——ウィジェット、MCP エンドポイント /mcp、API ルート——も同じトンネルを通り、毎回新しいドメインを覚える必要はありません。
4. 代替案: ngrok の静的サブドメイン
すでに ngrok に慣れているなら、static domain を使って同様に「実務化」できます。2023 年以降、ngrok は無料プランでも myapp-dev.ngrok-free.app のような静的サブドメインを 1 つ固定できます。
最小構成:
# ~/.config/ngrok/ngrok.yml
authtoken: <あなたのトークン>
tunnels:
giftgenius-dev:
addr: 3000
proto: http
domain: giftgenius-dev.ngrok-free.app
起動:
ngrok start giftgenius-dev
結果として、https://giftgenius-dev.ngrok-free.app は常に固定の URL になり、ChatGPT Dev Mode にはアプリのベース URL としてそれを指定します。
考え方は同じです:
- 「ランダム」アドレスを廃止;
- 内部のトンネル状態(起動/停止)が変わるだけで、ドメインは変わらない;
- Dev Mode を付け替える必要がない。
Cloudflare と ngrok は、いわば好みの違いです。自分のドメインと「薄い」DNS コントロールが好きな人(Cloudflare)、YAML を用意すればすぐ完了が好きな人(ngrok)。コースとしてはどちらも正解で、大事なのは 安定した URL です。
5. 図解: ChatGPT Dev Mode ↔ トンネル ↔ ローカルスタック
状況を少し形式化するために図で確認しましょう。
flowchart TD
ChatGPT["ChatGPT (Dev Mode)"]
AppCfg["Dev App (設定: https://giftgenius-dev...)"]
Tunnel["Cloudflare/ngrok トンネル (giftgenius-dev...)"]
Next["Next.js 開発サーバー localhost:3000 + MCP ハンドラ"]
ChatGPT --> AppCfg
AppCfg -->|"設定に https://giftgenius-dev.../.well-known/openai-app を指定"| Tunnel
Tunnel -->|"HTTPS → HTTP プロキシ"| Next
ChatGPT は、あなたがノート PC 上で何を動かしているかを知りません。彼らに見えているのは 1 つの HTTPS エンドポイントだけ。実体が Vercel なのか、ローカルトンネルなのか、Kubernetes なのかはあなた次第です。この講義で焦点にしているのは、ローカル開発のためにこの HTTPS エンドポイントを安定させることです。
残る課題は、アプリ内部でもこのアドレスを「唯一の真実の源」にすることで、ハードコードされた文字列に分散させないこと——次のセクションで扱います。
6. 環境変数とコード内の baseURL
サプライズを避けるには、Next.js のコード内で一度「アプリの外向きベース URL」を定義し、その後は必ずそれに依存するのが有効です。
たとえば、GiftGenius の app/lib/config.ts に次を用意します:
// app/lib/config.ts
export const baseUrl =
process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000"; // フォールバック
export const mcpEndpoint = `${baseUrl}/mcp`; // MCP サーバーの URL
開発時は .env.local にこう書きます:
NEXT_PUBLIC_APP_URL=https://giftgenius-dev.yourdomain.com
そうすると:
- ウィジェット内やリンク生成では常に baseUrl を使える;
- ChatGPT Dev Mode とブラウザの見え方が一貫する;
- 明日 Vercel の staging ドメイン https://giftgenius-staging.vercel.app に移っても、 環境変数だけ変えれば済む。
これは特に次のケースで重要です:
- callback‑URL(OAuth や webhook ハンドラなど);
- ウィジェットでユーザーに見せるリンク(openExternal での「ブラウザで開く」ボタン);
- アプリロジック上の絶対 URL。
ここでは dev‑URL に限って話しましたが、「baseUrl を唯一の真実の源にする」というアーキテクチャの考え方は、staging/production にもそのまま持ち運べます。
7. ChatGPT Dev Mode での URL 更新
さて、安定したドメインを整えました。Dev Mode ではどう運用するでしょうか。
ロジックはこうです:
- Dev アプリの設定でコア URL を 1 回だけ指定します: https://giftgenius-dev.yourdomain.com/
- ChatGPT はこの URL に対してマニフェスト(.well-known/openai-app)を取りに行き、 以降 MCP(/mcp)や静的ファイルなどでも同じベースを使います。
- コード(React ウィジェット、MCP ハンドラ、スタイル)だけを変更する場合、URL を変える必要はありません。 トンネルが動いていて Next.js サーバーが応答していれば十分です。
- もしドメイン自体を変える(稀に、ngrok から Cloudflare への切替など)なら、そのときだけ Dev Mode の endpoint を更新します。
場合によっては ChatGPT がマニフェストをキャッシュしており、変更が即座に反映されないことがあります。Dev Mode の UI には通常「Reload configuration / Refresh App」のようなボタンがありますが、最悪でも「同じ URL で一度切断して再接続」が効きます。
重要: URL を変えない限り、Dev Mode は新しいコードを自動で「拾います」。App にとっての主なトリガーは ドメイン であって、commit‑hash ではありません。
8. Dev Mode で dev / staging / prod を切り替える
安定した dev ドメインは第一歩に過ぎません。プロジェクトの成長とともに URL の混沌に溺れないよう、dev トンネルが(dev/staging/prod)という環境全体の中でどう位置づくのかを早めに理解しておくと良いです。staging と prod は次回の Vercel 講義が本題ですが、Dev Mode 自体は複数環境での運用に向いています。
以下のような表を頭に置くと理解しやすいでしょう:
| 環境 | ベース URL | 実行場所 |
|---|---|---|
| Local | |
ローカルの Next.js + MCP(トンネル経由) |
| Staging | |
Vercel Preview / ステージングデプロイ |
| Prod | |
Vercel Production |
Dev Mode の運用は 2 通りあります。
1 つ目は Dev アプリを 1 つにして、ときどきその設定の URL を更新し、staging や prod を触る(注意深く)。初期段階ではこれで構いませんが、混乱しやすいのも事実です。今日はローカルをテスト、明日は staging、明後日は切り替え忘れて Dev アプリ経由で prod に投げてしまう、といったことが起きます。
2 つ目は、より健全なやり方で、環境ごとに Dev アプリを分ける方法です:
- GiftGenius Dev → giftgenius-dev.yourdomain.com;
- GiftGenius Staging → giftgenius-staging.vercel.app;
- GiftGenius(本番、Store 経由) → giftgenius.vercel.app。
この講義では、まず dev‑URL の整備から順番に進めます。次回は、Vercel と preview デプロイを staging/production にどう結びつけるかを見ていきます。
9. チーム開発: 複数開発者と 1 つのトンネル
開発者が 1 人で dev ドメインも 1 つなら、トンネルはあなたの相棒です。しかしチームが入ってくると、トンネルや環境が交差し始め、「1 つのサブドメインを巡る戦い」を起こさない工夫が重要です。
たとえば 2 人の開発者が同じ静的サブドメイン、giftgenius-dev.ngrok-free.app を使うことにしたとします。2 人とも ngrok start giftgenius-dev を起動。良くてどちらかのトンネルが立ち上がらない(ドメイン衝突)、悪いと互いのセッションを奪い合い、ChatGPT からのアクセスが片方に行ったりもう片方に行ったりします。
いくつか戦略があります。
最もシンプルなのは 個人用 dev ドメイン です:
- alex.dev.giftgenius.app;
- maria.dev.giftgenius.app。
そして ChatGPT でも各自の Dev アプリ、例えば GiftGenius Dev (Alex) と GiftGenius Dev (Maria)。これなら各自がローカルで気兼ねなく回せて、他人の邪魔をしません。
より「チーム的」な道は、共通の staging エンドポイントです:
- 全員が個人の dev トンネルを持つ(個人のデバッグ用)。
- 加えて Vercel 上に staging があり、feature ブランチがマージされ、共通の Dev アプリ GiftGenius Staging がそこを見る。
現実のチームでよく見られる流れは次の通りです:
- 機能はローカルで生まれ、個人のトンネル経由でデバッグ;
- pull request とマージの後、全員が staging でテスト(トンネル不要、Vercel の URL だけ)。
10. dev トンネルのセキュリティ(手短に、被害妄想なしで)
トンネルはローカルサーバーを外部に出す便利な方法です。そしてインターネットは、ボット、スキャナー、「admin/admin のパスワードを忘れていないか」確認するのが好きな人々でできています。
dev 段階で覚えておくべき基本:
- トンネルはそのポートで公開されているもの全てに外部アクセスを与えます。DB の管理画面や phpMyAdmin、「パスワードなしのテスト用 CRM」を載せないこと;
- トンネルの URL をオープンなリポジトリやチャットに公開しないこと;
- 作業していないときはトンネルを止める(ノート PC もたまには電源を切りましょう——彼にも休息が必要)。
Basic 認証、特殊ヘッダの検証、URL のトークンといった本格的な対策は、セキュリティのモジュールで扱います。現時点で重要なのは 1 つ: トンネルは開発用の道具であって、保護されたサーバーではない ということ。プロダクションは Vercel のような正規のホスティングで稼働し、別の防御メカニズムが働きます。
11. 実践: 自分のアプリに安定した dev‑URL を設定する
理屈と注意点はここまで。では、Next.js(Apps SDK のテンプレート)で作った学習用アプリにこれを適用しましょう。
プロジェクト構成が次のようだとします:
apps/
web/ # Next.js App + ウィジェット
mcp-server/ # (任意)別の MCP、または web の /mcp ハンドラ
実際には MCP を Next.js に同居させ、app/api/mcp/route.ts に置いても構いません。原理は同じです。
ステップ 1. .env.local を編集
安定した dev トンネルの URL を追加します:
NEXT_PUBLIC_APP_URL=https://giftgenius-dev.yourdomain.com
開発コードではすでにこの変数から baseUrl を使っていました(上記参照)。まだなら、今こそ切り出しましょう。
ステップ 2. dev サーバーとトンネルを起動
cd apps/web
npm run dev # localhost:3000 で Next.js
# 別ターミナル
cloudflared tunnel run giftgenius-dev
ブラウザで https://giftgenius-dev.yourdomain.com を開き、あなたの App が表示されることを確認します。
ステップ 3. ChatGPT Dev Mode に接続
ChatGPT のインターフェース(開発者セクション)で:
- GiftGenius Dev を作成または編集;
- URL/Endpoint に https://giftgenius-dev.yourdomain.com/ を指定;
- 保存。
これで ChatGPT は /.well-known/openai-app からマニフェストを取得し、そのドメイン上であなたの App を起動します。
ここからは次のことができます:
- ウィジェットや MCP ハンドラ、スタイルを変更;
- npm run dev を再起動;
- cloudflared tunnel run giftgenius-dev を再起動;
それでもドメインが同じ限り、Dev アプリの設定を二度と触らなくてよいのです。
12. コードロジックでの見え方: openExternal の例
これまでの講義と結びつけるために、ウィジェットに「フル UI をブラウザで開く」ボタンを追加し、そこでも安定した dev‑URL を使ってみます。
ウィジェットの React コンポーネント GiftWidget があるとします:
// app/components/GiftWidget.tsx
"use client";
import { baseUrl } from "../lib/config"; // env から baseUrl を取得
export function GiftWidget() {
const handleOpenFull = () => {
window.openai.openExternal({
// アプリのページを新しいタブで開く
url: `${baseUrl}/full`,
label: "フル UI を開く",
});
};
return (
<div>
<button onClick={handleOpenFull}>
フルモード
</button>
</div>
);
}
NEXT_PUBLIC_APP_URL がトンネルを指していれば:
- ローカル開発中は https://giftgenius-dev.yourdomain.com/full を開く;
- staging にデプロイした後は https://giftgenius-staging.vercel.app/full;
- prod では本番ドメイン。
ここでも、ドメインの唯一の真実の源は 1 つ。環境を替えてもコードは替えません。
13. ミニ戦略: トンネルを「実務的」に捉える
シンプルなメンタルモデルにまとめると、次のように考えられます:
- トンネルは、あなたのノート PC と安定した公開ドメインをつなぐ一時的なケーブルにすぎない;
- ChatGPT Dev Mode が知っているのはドメインだけで、コードがどこで動くかはどうでもよい;
- ドメイン変更が少ないほど、ChatGPT や OAuth プロバイダの設定を触る時間が減る;
- dev トンネルは、あなたの環境マップの 1 行にすぎない。隣には staging(Vercel preview)や prod(Vercel production)がある。
次回は、この「ケーブル」を本格ホスティング(Vercel)に置き換え、Git ブランチ、プレビューデプロイ、本番運用と結びつける方法を見ていきます。
14. 「実務的」トンネルでのありがちなミス
エラー No. 1: 「静的ドメインを設定したのに、結局ランダム URL を使い続けている」
せっかく giftgenius-dev.yourdomain.com を用意したのに、習慣で ngrok http 3000 を設定なしで起動してしまうケース。結果、ChatGPT は一方のドメインを見るのに、コードは別の裏側で動く。安定した dev‑URL を作ったなら、それだけを使い、設定ファイル(命名済みトンネル/プロファイル)経由で起動しましょう。
エラー No. 2: localhost:3000 をコードにベタ書き
React コンポーネントや MCP ハンドラの中で fetch("http://localhost:3000/api/...") と書くパターン。ローカルでは動いていても、Dev Mode や staging/prod では即死です。ベース URL は常に設定に切り出し(baseUrl、NEXT_PUBLIC_APP_URL)、絶対リンクが必要な場所ではそれを使いましょう。
エラー No. 3: 安定トンネルの代わりに、Dev Mode の URL を毎回手で変える
「まあ今回だけ設定の URL を変えればいいか」と思ったなら要注意。ngrok/Cloudflare の静的サブドメイン設定は 10〜15 分の一度きり。その後は開発中に何時間も節約できます。
エラー No. 4: チームで 1 個の静的ドメインをルールなしで共有
2 人の開発者、1 個のドメイン giftgenius-dev.ngrok-free.app、そして双方が好きなときにトンネルを起動。結果はトンネル衝突、Dev Mode で返答が「謎に」消える、そして「自分の手元では動いた」系のデバッグに時間を溶かすことに。チームでは常に、個人用の dev ドメインか、実ホスティング上の 1 つの staging ドメインを選びましょう。
エラー No. 5: トンネルを「ほぼ本番」として使う
「安定した HTTPS URL がトンネルで手に入った。じゃあ本当のユーザーや決済も流そう」と考える人が時々います。これは痛みへの道です。ノート PC を閉じたらアプリは死に、ネットが切れたら同じ。セキュリティもせいぜい象徴的。トンネルは dev 用の道具。本番トラフィックは Vercel のような本格インフラで扱います。次回そこで詳しく扱います。
エラー No. 6: 環境変数と Dev Mode の URL を同期し忘れる
よくあるのは、NEXT_PUBLIC_APP_URL を .env.local で変えたのに Dev Mode の URL を変え忘れる(あるいはその逆)。結果、ウィジェットはあるドメインへのリンクを生成し、ChatGPT は別のドメインにアクセスします。「環境 ↔ ドメイン ↔ ChatGPT の App」の簡単な表を 1 つ持ち、変更時に更新しましょう。どの URL が正なのか当て物をするより、ずっと安上がりです。
GO TO FULL VERSION