返回所有文章
yeonghyeon yeonghyeon · 2026年4月29日

用 Apps SDK 打造 ChatGPT App:MCP·OAuth 實作與審核回顧

用 Apps SDK 打造 ChatGPT App:MCP·OAuth 實作與審核回顧

大家好,我是 yeonghyeon,負責 3Min API 的開發。

過去幾週,我一直在處理 3Min API 的 ChatGPT Apps 註冊。中間被退件過一次,修正後才獲得通過,期間我們用同一份程式碼也提交到了 Claude Connectors。這篇文章是寫給走在同一條路上的開發者的整理:需要實作什麼哪些地方容易被退件審核環節實際在做什麼,我都放在了一起。

跟具體雲端服務或部署堆疊有關的部分,我刻意沒寫。不管底下用的是什麼,ChatGPT Apps 註冊的流程都是一樣的。

概述 — ChatGPT Apps 與作為基礎的 MCP

ChatGPT Apps 是 OpenAI 推出的擴充模型,讓你可以從 ChatGPT 內部呼叫外部服務。使用者在 ChatGPT 裡新增你的 app,在對話流程中可以直接呼叫你後端公開的工具。

它的基礎是 Model Context Protocol(MCP)。MCP 是連接 LLM 與外部工具的開放規範,定義了如何列出工具、如何安全地呼叫它們。ChatGPT Apps 是跑在 MCP 之上的客戶端之一,Anthropic 的 Claude Connectors 也建立在同一個規範上。也就是說,把 MCP 伺服器一次實作好,兩個生態都能涵蓋。我們也是用同一份程式碼同時提交到兩邊,工具定義和認證流程是直接共用的。

為什麼我們選擇成為工具,而不是做自己的 AI 聊天機器人

一開始我們也考慮過做自家的聊天機器人。但退一步看,使用者其實已經把自己的業務脈絡完整地告訴了 ChatGPT 或 Claude。如果我們再做一個聊天機器人,同一個人就要從頭再說一遍同樣的故事。

所以我們轉了方向。把 3Min API 當作工具,接進你已經在用的 AI 裡面。 平常你向 ChatGPT 抒發業務煩惱的那個對話框,現在可以直接說「那我來用 3Min API 幫你設定好」,並且真的把事情做完。我們仍然在評估自家的聊天機器人,但優先順序在工具化這邊。

實作

SDK 選擇 — 只用標準 MCP SDK

OpenAI 確實有提供一個 ChatGPT Apps 專用的 SDK,但我們沒走那條路。我們的伺服器只依賴一個東西:標準 MCP SDK(@modelcontextprotocol/sdk)。原因很單純:同一台伺服器要同時被 ChatGPT Apps 和 Claude Connectors 認得。只用標準 SDK 來寫,就不會因為某邊生態更新而讓程式碼分岔,我們也能把同一份建置產物原樣提交到兩個目錄。

OpenAI 這邊的規範整理在 Build your MCP serverConnect from ChatGPT。兩份文件都是從用戶端視角描述 ChatGPT 用戶端如何辨識和呼叫你的伺服器——只要把標準 MCP 伺服器確實實作好,OpenAI 那邊的要求自然會被滿足。

認證 — OAuth 2.1 為基本,API Key 在同一台伺服器上作為輔助

對 ChatGPT Apps 來說,實際上能選的認證就是 OAuth 2.1 一種。OpenAI 的 官方指南 上講得很明白:「ChatGPT does not support … custom API keys or customer-provided mTLS certificates」。也就是說,要提交到 ChatGPT Apps 目錄的 app,把 OAuth 2.1 實作好就足夠了(對於不會接觸使用者資料的工具,允許使用 noauth 匿名模式;但像我們這種會操作使用者帳號下的端點和記錄檔的工具,OAuth 是必要的)。

但是,如果你想讓同一台 MCP 伺服器也能在 Cursor、Claude Code、Gemini CLI 這類本機客戶端裡直接使用,情況就不一樣了。本機環境是使用者自己可以保管金鑰的地方,OAuth 流程在那裡反而顯得太重。所以我們選擇了 把 OAuth 2.1 當作 ChatGPT Apps·Claude Connectors 的基本認證,再在同一台伺服器上疊一條 API Key 路徑。同一份工具定義,在兩種認證下都能直接運作。

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 驗證做 token 交換 → access token(1 小時)+ refresh token(30 天,輪替)。access token 過期時用 refresh token 續發,每次續發連 refresh token 自身也輪替,以降低被竊用的風險。

實作裡最費心的一處是 確保 code 在原子層面只被消費一次。authorization code 是被中途攔截價值很大的資源,所以我們在 token 交換那一步,用單一 update 陳述把 code 一次 claim 起來,避免出現 race condition。

API Key — 讓同一份程式碼也能在 CLI/IDE 裡使用的輔助路徑

ChatGPT Apps 走 OAuth 進來,但我們希望同一份工具定義在 Cursor、Claude Code、Gemini CLI、Smithery 這類本機 MCP 客戶端裡也能直接使用,所以又疊上一條 API Key 路徑。這邊使用者自己可以保管金鑰,用 x-api-key 標頭帶核發過的 key 就最乾淨。我們核發帶 tm_mcp_ 前綴、按使用者區分的 key,並按 key 追蹤啟用/停用狀態與最後使用時間。

對外公開的 8 個工具,以及它們的權限標註

3Min API 對 ChatGPT/Claude 公開 8 個工具。每個工具都需要明確聲明 MCP 標準的三個 annotation。先看含義:

annotation含義true 範例
readOnlyHint是否唯讀(不變更資料)查詢記錄檔/統計
destructiveHint是否會造成不可逆的變更修改/刪除端點
openWorldHint是否會與外部世界通訊或產生副作用送出外部 webhook

把這三個值套到我們的 8 個工具,結果如下:

工具說明readOnlydestructiveopenWorld
help查詢服務指南truefalsefalse
endpoints端點 CRUD/部署falsetruefalse
api_call呼叫端點falsetruetrue
logs查詢呼叫記錄檔truefalsefalse
stats用量統計truefalsefalse
collaborators協作金鑰/邀請falsetruetrue
subscription訂閱資訊truefalsefalse
archives封存下載truefalsetrue

刻意沒有公開的領域

帳號本身的刪除、付款方式變更、MCP 認證金鑰的核發與撤銷 這類一旦同意有偏差就無法回頭、或是直接牽動認證體系本身的動作,我們沒有暴露成工具。把這些交給 LLM 判斷,一次錯誤呼叫就能直接演變成資安事故,而且這也跟 ChatGPT Apps 審核指南推薦的方向不符。

測試 — 兩條認證路徑都跑一次比較穩

正式提交前,我們把兩條路徑都跑了一遍。

  • OAuth 路徑 — 在 ChatGPT 網頁版的 Settings → Apps & Connectors → Advanced settings 開啟 Developer Mode,然後用 'Create app' 註冊我們的 MCP 伺服器 URL。ChatGPT 會自動去讀我們伺服器的 /.well-known/... 來辨識 OAuth 中繼資料,所以可以親自走完同意畫面、直到實際呼叫工具。
  • API Key 路徑 — 在 Claude Code、Cursor、Gemini CLI 加上 MCP 伺服器,驗證工具呼叫。同時也註冊到 Smithery,確認在目錄上能正常出現。

兩條路徑的程式碼本身是一樣的,但 OAuth 那邊還要再過 well-known 中繼資料、同意畫面、token 續發這幾關,所以多了一道難度。提交之前,建議你至少把 真正的 ChatGPT 連到你的伺服器、把工具清單拉下來 這一步親自跑一次。

提交審核 — 被退件一次之後才理解的事

提交本身,跟著 OpenAI 官方指南(app-submission-guidelines)邊看邊問 ChatGPT 也能順利完成。但在表單填寫階段,ChatGPT 會實際連進你的伺服器去拉工具清單,所以 提交當下你的正式環境必須已經上線。還要一併提供測試帳號資訊(電子郵件/密碼,或另開一個審核專用帳號),不然 OpenAI 的審核人員沒辦法直接呼叫工具。

注意事項 1 — 工具權限標註要保守

這是最常見的退件原因。我們第一次提交時,把三個會跟外部通訊的工具(api_callcollaboratorsarchives)的 openWorldHint 設成 false,結果就被退件。修正後才通過。

這三個裡面,archives 會核發外部下載 URL,openWorldHint=true 是顯而易見的。api_call(透過使用者定義的 webhook 觸發外部通訊)和 collaborators(向協作者發出邀請信)就比較間接,會讓人猶豫一下。事後看來,把這兩個也保守地標成 true 才是對的。

判斷標準其實很單純:一旦被退件,下一次審核又得再等大約兩週。 工具只要有任何往外通訊的流程,別猶豫,直接 true。從時間成本看這是最划算的做法。把每個工具產生的副作用一行一行寫下來,確認其中有沒有任何一條會傳到系統外,標註自然就會就位。

注意事項 2 — 截圖不是「螢幕擷圖」,而是「模板作業」

提交表單裡有一項要展示你的 app 在 ChatGPT 裡看起來是什麼樣的截圖。自己截圖直接上傳一定會被退件。 這一步 OpenAI 給了明確的指南,提交頁面裡也直接給了指南連結。流程是這樣:

  1. 提交表單到截圖這一步時,指南連結會把你導到 OpenAI 官方的 Figma 檔
  2. 把那份 Figma 檔裡的 截圖模板複製到你自己的 Figma 工作區
  3. 把你的 app 在 ChatGPT 裡實際運作的畫面 貼到模板裡指定的位置。比例、留白、構圖在模板裡都已經訂好,你只要把內容填進去就好。
  4. 把完成的畫板 匯出成圖片,在提交表單上傳。

也就是說,提交的成果不是單純的螢幕擷圖,而是 把你的 app 畫面放進 OpenAI 訂好的畫框裡得到的成品。提交前再過一次指南,照做就行。如果跳過這一步、自己擷圖丟上去,提交會直接被退件。

審核回顧 — 通過之後還有一步

從提交到出結果,大約要等兩週以上。被退件再修正後重投,通常還要再花差不多的時間。可以從 OpenAI 開發者控制台查看狀態,被退件或通過時都會收到電子郵件。

收到通過信並不代表已經上架。還要在控制台按一次 Publish 按鈕,一般使用者那邊才會看到。

上架之後還有一件事要留意。即使使用者已經把你的 app 加進去了,ChatGPT 也不會在每段對話裡都自動去用你的工具。就算話題相關,經常也會回出沒有工具呼叫的回覆。這種時候要 提醒使用者用 @ 提及來直接喚出你的 app。把這一點告訴使用者,工具使用率會明顯上升。

Claude Connectors 目前進度

Claude Connectors 跑在同一套 MCP 上。程式碼層面幾乎沒有額外要補的東西。但審核流程不像 OpenAI 那邊那麼整齊:沒有可以查詢提交狀態的控制台,我們提交快滿一個月了,還沒收到通過或退件的回覆。兩邊同時投是值得推薦的,但時程建議以 OpenAI 那邊為基準。

親自試一試

如果你想在 ChatGPT 裡試試 3Min API 的 MCP 工具,或者想看看我們具體公開了什麼,只要 在 ChatGPT Apps 新增畫面搜尋「3Min API」 就能安裝。完成一次認證之後,平常對話裡用 @3Min API 提及就能呼叫工具。

如果想從本機 MCP 客戶端(Claude Code、Cursor 等)試,登入後到 設定 → AI 整合 頁面核發 API Key,就能馬上用。每個工具的細節和呼叫範例,都整理在 AI 整合指南 裡。

願這篇文章能成為走在同一條路上的開發者的一條小捷徑。