ChatGPT Apps SDKでアプリを作る:MCP・OAuth実装と審査の振り返り
こんにちは、3Min APIの開発を率いているyeonghyeonです。
この数週間、3Min APIのChatGPT Apps登録に取り組んでいました。一度リジェクトを受けて修正したのち承認され、その間に同じコードベースをClaude Connectorsにも提出しています。本記事は、同じ道を歩む開発者の方々に向けたまとめです。何を実装すればよいのか、どこでリジェクトされやすいのか、審査ではどんなことが行われているのか を一通り整理しました。
特定のクラウドやデプロイ環境に依存する話は、意図的に外しています。ChatGPT Appsの登録は、どんなスタックの上でも同じフローで進みます。
概要 — ChatGPT Appsとその基盤であるMCP
ChatGPT Appsは、OpenAIがChatGPTの中から外部サービスを呼び出せるようにするための拡張モデルです。ユーザーはChatGPT画面でアプリを追加し、会話の流れの中でこちらのバックエンドが公開しているツールを直接呼び出せるようになります。
その土台が Model Context Protocol(MCP) です。MCPはLLMと外部ツールをつなぐためのオープン仕様で、ツール一覧を公開し、それらを安全に呼び出すための約束ごとを定めています。ChatGPT AppsはMCP上で動くクライアントの一つで、AnthropicのClaude Connectors も同じ仕様の上にあります。つまりMCPサーバーを一度きちんと実装しておけば、両方のエコシステムに対応できます。私たちも同じコード一式で両方に提出していて、ツール定義と認証フローはそのまま共有しています。
なぜ自前のAIチャットボットではなく、ツールになることを選んだか
最初は自前のチャットボットも検討していました。ですが一歩引いて見ると、ユーザーはすでにChatGPTやClaudeに自分のビジネス文脈を十分に伝えていたのです。私たちがもう一つチャットボットを作れば、同じ方が同じ事情を最初から説明し直すことになります。
そこで方向を切り替えました。すでに使われているAIに、3Min APIをツールとして加える という発想です。普段からビジネスの相談をしているChatGPTが、その場で「では3Min APIで作っておきますね」と直接動いてくれる、そんな形になります。自前のチャットボットも引き続き検討中ですが、優先順位はツール化です。
実装
SDKの選択 — 標準MCP SDKのみ
OpenAIはChatGPT Apps専用のSDKも提供していますが、私たちはそちらは使いませんでした。サーバー実装の依存は 標準MCP SDKのみ(@modelcontextprotocol/sdk)です。理由は単純で、同じサーバーがChatGPT AppsとClaude Connectorsの両方に認識される必要があるからです。標準SDKだけで書いておけば、片方のエコシステムの変更でコードが分岐することもなく、両方のディレクトリに同じビルドをそのまま提出できます。
OpenAI側の仕様は Build your MCP server と Connect from ChatGPT にまとまっています。どちらもChatGPTクライアント側からこちらのサーバーをどう認識・呼び出すかを説明したもので、標準のMCPサーバーを誠実に実装すれば、OpenAI側の要件は自然に満たされます。
認証 — OAuth 2.1を基本に、同じサーバーにAPIキーを補助で
ChatGPT Appsが受け付ける認証は、実質OAuth 2.1の一択です。OpenAIの 公式ガイド にも明確に書かれています — 「ChatGPT does not support … custom API keys or customer-provided mTLS certificates」。つまりChatGPT Appsディレクトリに登録するアプリなら、OAuth 2.1をきちんと実装すれば十分です(ユーザーデータを扱わないツールであれば noauth 匿名モードも許容されますが、私たちのようにユーザーアカウントのエンドポイントやログを扱うツールではOAuthが必須になります)。
ただし、同じMCPサーバーをCursorやClaude Code、Gemini CLIといったローカルクライアントからも使えるようにするとなると、事情が変わります。ローカル環境はユーザーが自分の鍵を直接保管できる場であり、OAuthのフローはそこではむしろ重い。そこで私たちは、OAuth 2.1をChatGPT Apps・Claude Connectorsの基本認証として実装し、その上にAPIキー認証をもう一系統重ねる 形を選びました。同じツール定義が、両方の認証で同じように動きます。
OAuth 2.1 — ChatGPT AppsとClaude Connectorsの基本
ホスティング環境で動くクライアントでは、ユーザーが鍵を直接管理することはできず、OpenAIのポリシー上も他に選択肢はありません。クライアントがこちらのサーバーを正しく認識するには、以下の標準を満たす必要があります。
- RFC 7591 — OAuth Dynamic Client Registration。クライアントが登録手続きを自分で行います。
- RFC 8414 — Authorization Server Metadata。
/.well-known/oauth-authorization-serverで認証エンドポイントを公開します。 - RFC 9728 — Protected Resource Metadata。MCPリソース自体のメタデータを公開します。
- RFC 7636 PKCE — Authorization Code 横取り攻撃を防ぐS256 challenge。
フロー自体は標準そのものです。クライアントが動的登録 → ユーザー認証後にauthorization codeを発行(一回限り・10分で失効) → PKCE検証付きでトークン交換 → access token(1時間) + refresh token(30日、ローテーション)。失効したaccess tokenはrefresh tokenで更新し、更新のたびにrefresh token自体もローテーションさせて、漏洩のリスクを抑えています。
実装で一番気を遣ったのは codeを原子的に一回限りで使い切る保証 です。authorization codeは横取りされる価値が大きいリソースなので、トークン交換の段階でレースが発生しないよう、update文一つでまとめてclaimするようにしました。
APIキー — 同じコードをCLI/IDEから使うための補助系統
ChatGPT AppsにはOAuthで届きますが、同じツール定義をCursor、Claude Code、Gemini CLI、Smitheryのようなローカル MCPクライアントからもそのまま使えるようにするため、APIキー方式をもう一系統重ねました。こちらはユーザーが自分の鍵を直接保管できる環境なので、x-api-key ヘッダーで発行済みの鍵を送る方式が一番すっきりします。私たちは tm_mcp_ プレフィックス付きのユーザー単位の鍵を発行し、鍵単位で有効・無効と最終使用日時を追跡しています。
公開しているツール8種と、その権限表示
3Min APIがChatGPT/Claudeに公開しているツールは8つです。各ツールにはMCP標準の3つのアノテーションを明示する必要があります。まずは意味から整理します。
| annotation | 意味 | true の例 |
|---|---|---|
readOnlyHint | 読み取り専用か(データを変更しないか) | ログ・統計の取得 |
destructiveHint | 取り返しのつかない変更を引き起こすか | エンドポイントの編集・削除 |
openWorldHint | 外部の世界と通信するか/副作用を生むか | 外部Webhookの送信 |
この3つの値を私たちの8ツールに当てはめると、次のようになります。
| ツール | 概要 | readOnly | destructive | openWorld |
|---|---|---|---|---|
help | サービスガイドの参照 | true | false | false |
endpoints | エンドポイントのCRUD・デプロイ | false | true | false |
api_call | エンドポイントの呼び出し | false | true | true |
logs | 呼び出しログの参照・検索 | true | false | false |
stats | 使用量の統計 | true | false | false |
collaborators | 協業キー・招待 | false | true | true |
subscription | サブスクリプション情報 | true | false | false |
archives | アーカイブのダウンロード | true | false | true |
あえて公開していない領域
アカウント自体の削除、決済手段の変更、MCP認証キーの発行・破棄 のように、ユーザーの同意が一度ずれるだけで取り返せなくなる、あるいは認証の仕組みそのものに直結するアクションは、ツールとして公開していません。これらをLLMの判断に任せると、一度の誤った呼び出しがそのままセキュリティ事故につながりますし、ChatGPT Apps審査ガイドラインが推奨する方向にも合いません。
テスト — 認証の両系統を一度回しておく
本提出の前に、私たちは両方を試しました。
- OAuth系統 — ChatGPTのウェブ版で 設定 → Apps & Connectors → Advanced settings から 開発者モード を有効にし、'Create app' から自社のMCPサーバーURLを登録しました。ChatGPTは登録したサーバーの
/.well-known/...を自動で読みに来てOAuthメタデータを認識してくれるので、同意画面を経て実際のツール呼び出しまで一通り体験できます。 - APIキー系統 — Claude Code、Cursor、Gemini CLIにMCPサーバーを追加し、ツール呼び出しを検証しました。同時にSmitheryにも登録して、ディレクトリ上での見え方も確認しています。
コード自体は両系統で共通ですが、OAuth側はwell-knownメタデータ、同意画面、トークン更新まで全部通す必要があるので、もう一段難しい作業です。提出の前に 実際のChatGPTがこちらのサーバーに到達してツール一覧を取得する段階 までは、自分で一度動かしてみることをおすすめします。
審査提出 — 一度リジェクトされて分かったこと
提出自体は、OpenAI公式ガイド(app-submission-guidelines)を見ながらChatGPTに質問しつつ進めれば、無理なくこなせます。ただし、フォーム入力の段階でChatGPTが実際にこちらのサーバーに接続してツール一覧を取りに来るので、提出時点で本番環境にデプロイされている必要 があります。テスト用のアカウント情報(メール/パスワード、または別途用意した審査用アカウント)も併せて提供する必要があり、これがないとOpenAIの審査担当が直接ツールを呼び出せません。
注意 1 — ツール権限の表示は保守的に
ここが一番よくあるリジェクトの理由です。私たちも最初の提出で、外部と通信する3つのツール(api_call、collaborators、archives)の openWorldHint をfalseにしていてリジェクトされました。修正後は通っています。
この3つのうち、archives は外部のダウンロードURLを発行するので openWorldHint=true は明白です。一方 api_call(ユーザー定義のwebhookで外部通信を引き起こす)と collaborators(協業者にメール招待を送る)は、外部に届く経路が間接的なため一瞬迷うところです。結果的には、この2つも保守的にtrueにしておくのが正解でした。
判断基準はシンプルです。一度リジェクトされると、次の審査までまた約2週間待つことになります。 ツールに少しでも外部と通信する流れがあるなら、迷わずtrueに。時間コスト的にこれが一番安いやり方です。各ツールが生む副作用を一行ずつ書き出して、それが自社のシステム外まで届くかどうかを確認すれば、フラグは自然と整います。
注意 2 — スクリーンショットは「キャプチャ」ではなく「テンプレート作業」
提出フォームには、自社アプリがChatGPT内でどう見えるかを示すスクリーンショット項目があります。独自にキャプチャしてアップロードすると無条件に差し戻されます。 この段階にはOpenAIが定めた明確なガイドがあり、提出画面の中からガイドへのリンクが直接案内されます。流れはこうです。
- 提出フォームのスクリーンショット段階で、ガイドリンクがOpenAI公式のFigmaファイルに繋がります。
- そのFigmaファイルにある スクリーンショットのテンプレートを自分のFigmaワークスペースにコピー します。
- 自社アプリがChatGPT内で実際に動いている画面を、その テンプレートの所定の位置に貼り付けます。比率や余白、構図はテンプレートに既に決まっているので、こちらは中身を埋めるだけです。
- 仕上がったボードを 画像としてエクスポート し、提出フォームにアップロードします。
つまり提出物は単なる画面キャプチャではなく、OpenAIが定めた額縁の上に自社アプリの画面を配置した成果物 です。ガイドは提出直前にもう一度確認して、その通りに従ってください。この手順を飛ばして自前のキャプチャを上げると、そのまま差し戻されます。
審査の振り返り — 承認後にもう一段
提出から結果まではおおよそ2週間以上かかりました。リジェクトされて修正後に再提出すると、また同じくらいの期間がかかります。OpenAIの開発者ダッシュボードで状況を確認でき、リジェクト/承認のタイミングではメールも届きます。
承認メールが届いたからといってすぐに公開されるわけではありません。ダッシュボードで Publish ボタンを一度押して、はじめて一般ユーザーに見えるようになります。
公開直後にもう一つ気を付けたいことがあります。ユーザーがアプリを追加したからといって、ChatGPTがすべての会話で自動的にツールを呼びに行くわけではありません。同じトピックでも、ツールが絡まない応答が普通に返ってくることが多いです。そんなときは @ メンションでアプリを直接呼び出してください と案内しておくのがコツです。これを伝えておくだけで、ツール利用率が大きく上がります。
Claude Connectorsの進捗
Claude Connectorsは同じMCPの上で動きます。コードの追加作業はほぼ不要でした。ただし審査プロセスはOpenAI側ほど整っていません。提出状況を確認できるダッシュボードがなく、申請してから1か月近く経つのに承認・リジェクトの返事はまだ来ていません。両方に同時に提出しておくこと自体は推奨ですが、スケジュールはOpenAI側を基準に組んでおくのが安全です。
実際に試す
3Min APIのMCPツールをChatGPT内で試してみたい、あるいは私たちがどう公開しているかを確認したい場合は、ChatGPT Apps追加画面で「3Min API」を検索 してインストールできます。一度認証を済ませれば、普段の会話で @3Min API とメンションするだけでツールを呼び出せます。
ローカルのMCPクライアント(Claude Code、Cursorなど)から試したい場合は、ログイン後、設定 → AI連携 画面でAPIキーを発行すればすぐに利用できます。各ツールの詳細や呼び出し例は AI連携ガイド にまとめています。
同じ道を歩む他の方々にとって、ささやかな近道になれば嬉しいです。
Related Posts
ChatGPTの中でAPIを作れます — 3Min APIがChatGPT Appsに加わりました
3Min APIがChatGPT Appsに加わりました。コードを書いたり、サーバーを借りたりしなくても、ChatGPTとの会話の流れの中でAPIを作って、テストして、取引先を協業者として招待できます。スタートしたばかりのワイン販売ビジネスのシナリオで、何がどう変わるのかを一緒に見ていきましょう。
AIでAPIを管理しよう:MCP対応のご紹介
3Min APIがMCPに対応しました。AIアシスタントを接続して、エンドポイント管理やログ確認を会話で行えます。
JSONとは?非開発者の店主のための入門ガイド
ワインショップ店主の物語を締めくくるJSON入門ガイド。Excelシートを出発点に、キー・値・オブジェクト・配列という再帰構造をたどり、今日そのまま自分のビジネスの注文をJSONで書いてみる実習まで、事前知識なしで読める形に整理しました。