跳到內容

06|ChatGPT 連線與實際驗證

在 ChatGPT 網頁版 輸入自然語言,真正呼叫私人 MCP,拿回 PostgreSQL 的資料與 SQL,再比對拒絕與撤銷證據。只把 SQL 貼給 ChatGPT 解釋,不算連線成功。

目前實際路徑:

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 開機。

在 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-client
private/tunnel-linux/tunnel-client help quickstart

用本機隱藏輸入保存 key(Bash;輸入不會回顯):

終端機視窗
umask 077
read -r -s -p 'Tunnel runtime key: ' textile_runtime_key
printf 'CONTROL_PLANE_API_KEY=%s\n' "$textile_runtime_key" > private/tunnel-runtime.env
unset textile_runtime_key
chmod 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.env
set +a
private/tunnel-linux/tunnel-client doctor \
--profile textile-course \
--profile-dir /root/textile-data-lab/private/tunnel-profiles --explain
unset CONTROL_PLANE_API_KEY

profile 只存 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.service
sudo systemctl daemon-reload
sudo systemctl enable --now textile-mcp-tunnel.service
systemctl is-active textile-mcp-tunnel.service
curl --fail http://127.0.0.1:28420/readyz

預期 active、ready。ready (mcp initialize requires auth ... Unauthorized) 表示 MCP 仍要求使用者 OAuth,並非匿名開放。若有相同名稱的既存服務,先檢查與備份,不直接覆寫。

在 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 sso
docker compose exec -T gateway nginx -s reload
docker 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 內部轉送網址寫死在教材裡。

按 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 工作階段。

學員電腦需要 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 的企業身分是兩套不同帳號;前者不會自動得到訂單權限。

問題/動作 預期
基準日逾期、未完全出貨、前 10 筆 台灣市場 10 筆,has_more=true
客戶 33、剩餘量由大到小、3 筆 篩選與排序有效,不直接改寫 SQL
查下一頁 offset=10,不與第一頁重複
客戶 2 不在業務授權市場,空集合
查薪資/客戶 email 工具不提供,資料庫權限也拒絕
limit=500 或傳原始 SQL Schema 拒絕
hr.a 呼叫訂單工具 403
SQL 執行錯誤 憑證仍清理

操作人員以本次 request ID 查 docker compose logs sso,再比對 OpenBao 稽核與資料庫殘留角色。不要把完整稽核檔直接公開。Grafana 是業務摘要,不會自動顯示每個 MCP trace。

最後回答必須明確寫出「前 10 筆」,不能把一頁結果當全部;數量由資料庫計算,Agent 不自行編造或轉換金額。