06|ChatGPT 連線與實際驗證
本章驗收目標
Section titled “本章驗收目標”在 ChatGPT 網頁版 輸入自然語言,真正呼叫私人 MCP,拿回 PostgreSQL 的資料與 SQL,再比對拒絕與撤銷證據。只把 SQL 貼給 ChatGPT 解釋,不算連線成功。
A. ChatGPT 網頁版的私人連線
Section titled “A. ChatGPT 網頁版的私人連線”1. 在正確的主機與帳號操作
Section titled “1. 在正確的主機與帳號操作”目前實際路徑:
ChatGPT 網頁版→ OpenAI Secure MCP Tunnel→ 5090 上的 tunnel-client 常駐服務→ 私有 MCP → Keycloak Token 驗證→ OpenBao → PostgreSQL → 查詢與撤銷證據MCP 沒有改成公開網址。Tunnel 的 runtime key 只授予 Tunnels Read + Use,不授予模型呼叫或其他資料權限。本次設 30 天到期,屆期必須輪替。使用者瀏覽器仍需 Tailscale 才能到私人 Keycloak 登入;常駐 Tunnel 放在 5090,因此不依賴講師 Mac 開機。
2. 建立專用 Tunnel 與執行憑證
Section titled “2. 建立專用 Tunnel 與執行憑證”在 Platform Tunnels 建立課程 Tunnel,關聯自己的 Platform organization 與目標 ChatGPT workspace。到 API keys 建立專用 key,Permissions 選 Restricted,只將 Tunnels 設為 Use(介面顯示 Read, Use),其他權限保持 None,設定到期日。
不要重用其他專案的 API key,也不要把 key 貼到 ChatGPT。下列命令只在自己的 Linux/WSL 環境執行;共享 5090 的這部分已由講師配置。
從 官方 tunnel-client releases 下載符合主機架構的版本,核對 release digest。課程目前驗證 0.0.16,將 Linux x86-64 的 tunnel-client 放到 private/tunnel-linux/。
chmod 700 private/tunnel-linux/tunnel-clientprivate/tunnel-linux/tunnel-client help quickstart用本機隱藏輸入保存 key(Bash;輸入不會回顯):
umask 077read -r -s -p 'Tunnel runtime key: ' textile_runtime_keyprintf 'CONTROL_PLANE_API_KEY=%s\n' "$textile_runtime_key" > private/tunnel-runtime.envunset textile_runtime_keychmod 600 private/tunnel-runtime.env將以下兩個參數換成自己實際的 Tunnel ID 與私有 MCP URL:
private/tunnel-linux/tunnel-client init \ --profile textile-course \ --profile-dir /root/textile-data-lab/private/tunnel-profiles \ --tunnel-id YOUR_TUNNEL_ID \ --mcp-server-url YOUR_PRIVATE_HTTPS_MCP_URL \ --health-listen-addr 127.0.0.1:28420
set -a. private/tunnel-runtime.envset +aprivate/tunnel-linux/tunnel-client doctor \ --profile textile-course \ --profile-dir /root/textile-data-lab/private/tunnel-profiles --explainunset CONTROL_PLANE_API_KEYprofile 只存 env:CONTROL_PLANE_API_KEY 參照。原始碼包的 tunnel/textile-mcp-tunnel.service 是本次服務範本;目錄不同時先同步修改全部路徑,再由操作人員安裝:
sudo install -m 644 tunnel/textile-mcp-tunnel.service /etc/systemd/system/textile-mcp-tunnel.servicesudo systemctl daemon-reloadsudo systemctl enable --now textile-mcp-tunnel.servicesystemctl is-active textile-mcp-tunnel.servicecurl --fail http://127.0.0.1:28420/readyz預期 active、ready。ready (mcp initialize requires auth ... Unauthorized) 表示 MCP 仍要求使用者 OAuth,並非匿名開放。若有相同名稱的既存服務,先檢查與備份,不直接覆寫。
3. 建立 ChatGPT 的 OAuth client
Section titled “3. 建立 ChatGPT 的 OAuth client”在 ChatGPT Plugins 按 Add → Add custom MCP server:
| 欄位 | 本次實作 |
|---|---|
| Name | Textile orders lab |
| Connection | Tunnel,填本課 Tunnel ID |
| Authentication | OAuth |
| Advanced → Registration method | User-Defined OAuth Client |
| OAuth client ID | textile-chatgpt |
| OAuth client secret | 留空;本課為 PKCE public client |
| Token endpoint auth method | none |
| Default scopes | orders:read |
| Resource | 原始私有 MCP URL,https://...:28100/textile/mcp |
等待 discovery 完成,複製介面顯示的 Callback URL。它是 https://chatgpt.com/connector/oauth/... 的精確路徑;每個連線可能不同,不要使用 Codex 的 loopback callback 或萬用字元。
在自己的沙盒配置:
python3 scripts/configure_mcp.py \ --client-id textile-chatgpt \ --callback https://chatgpt.com/connector/oauth/YOUR_CALLBACK
docker compose up -d --build --no-deps ssodocker compose exec -T gateway nginx -s reloaddocker compose exec -T sso python - < scripts/verify_mcp.py後端只允許 textile-codex 與 textile-chatgpt,其餘 client 仍拒絕;audience、scope、角色也仍需符合。驗收已擴充為 32 項,包含 ChatGPT client 的合法身分、HR 拒絕與缺 scope 拒絕。
保留 Tunnel discovery 產生的 Token URL 與 metadata URL;它們讓雲端交換 Token 時經私人 Tunnel 到達 Keycloak。Auth URL 仍指向私人 Keycloak,供使用者自己的瀏覽器登入。不把 discovery 的 OpenAI 內部轉送網址寫死在教材裡。
4. 登入並真正呼叫工具
Section titled “4. 登入並真正呼叫工具”按 Create as a plugin → Connect,使用自己的 Keycloak 業務帳號登入,同意 orders:read。完成後在插件頁按 Try in chat,保留已選取的 Textile orders lab,輸入:
使用紡織 MCP,先確認我的身分與業務定義,再查詢截至 2026-10-20 逾期且尚未完全出貨的前 10 筆訂單。列出客戶、剩餘未出貨公尺數、實際 SQL、綁定參數、分頁資訊、request ID 與憑證撤銷結果。沒有成功呼叫工具就明確說明,不要編造資料。
這次實際查詢第一筆為 SO-2026-00633、客戶033、剩餘 3,453 公尺。回傳 has_more=true、next_offset=10。後端查詢與撤銷實驗共 255ms;這不包含 ChatGPT 推理、網路與整段回答時間,也不是壓力測試數據。
本次插件建立在講師自己的 ChatGPT 環境,並未公開發布到插件商店。讓全班使用前,還須完成 workspace 配發/各帳號連線、Keycloak 個人帳號與負載驗收;不要共用講師已登入的 ChatGPT 工作階段。
B. 已驗證的 Codex 路徑
Section titled “B. 已驗證的 Codex 路徑”學員電腦需要 Tailscale。自己的 ~/.codex/config.toml 加入以下設定;已有同名區塊就編輯,不能重複新增:
[mcp_servers.textile-orders]url = "https://claude-lab.tail747e9c.ts.net:28100/textile/mcp"
[mcp_servers.textile-orders.oauth]client_id = "textile-codex"callback_url = "http://127.0.0.1/callback"codex mcp login textile-orders --scopes orders:read明確指定 scope,避免要求 Keycloak realm 公告的全部 scope。Codex 的 ChatGPT 訂閱登入與 Keycloak 的企業身分是兩套不同帳號;前者不會自動得到訂單權限。
C. 一組全班可對照的測試
Section titled “C. 一組全班可對照的測試”| 問題/動作 | 預期 |
|---|---|
| 基準日逾期、未完全出貨、前 10 筆 | 台灣市場 10 筆,has_more=true |
| 客戶 33、剩餘量由大到小、3 筆 | 篩選與排序有效,不直接改寫 SQL |
| 查下一頁 | offset=10,不與第一頁重複 |
| 客戶 2 | 不在業務授權市場,空集合 |
| 查薪資/客戶 email | 工具不提供,資料庫權限也拒絕 |
| limit=500 或傳原始 SQL | Schema 拒絕 |
| hr.a 呼叫訂單工具 | 403 |
| SQL 執行錯誤 | 憑證仍清理 |
D. 後台如何核對
Section titled “D. 後台如何核對”操作人員以本次 request ID 查 docker compose logs sso,再比對 OpenBao 稽核與資料庫殘留角色。不要把完整稽核檔直接公開。Grafana 是業務摘要,不會自動顯示每個 MCP trace。
最後回答必須明確寫出「前 10 筆」,不能把一頁結果當全部;數量由資料庫計算,Agent 不自行編造或轉換金額。