瀏覽文件

OOMOL OpenConnector 自部署指南

OOMOL OpenConnector 是開源、可自部署的應用連接服務。適合需要讓 Agent 或內部工具呼叫真實外部服務,同時把 provider 憑據、權限和執行記錄留在自己環境裡的場景。它透過 MCP、HTTP 暴露 GitHub、Gmail、Notion、Hacker News、Ably、Abstract、A-Leads 等服務的型別化 action。

Agent 只能看到 schema、scope、執行狀態和安全的帳號標籤;原始 provider token、action 權限和執行記錄由你的部署邊界管理。

它提供什麼

  • 一個 runtime,透過 MCP、HTTP、OpenAPI 和 Web 控制台暴露 provider actions。
  • 憑據儲存,支援 API key、自訂憑據、OAuth2 連接和無需認證的 provider。
  • 型別化 action schema,讓 Agent 在呼叫前先知道自己能呼叫什麼。
  • 連接身分和 scope,讓使用者和 Agent 都能看到 action 會以哪個帳號執行。
  • 臨時檔案中轉,供需要檔案 URL 的 action 使用。
  • 最近執行記錄,包含去識別化後的輸入摘要和 provider 錯誤。
  • provider catalog 和本地 executor;executor 只在 action 被使用時才載入。

先選擇 runtime 的執行位置,再設定儲存、存取控制、provider 連接,以及提供給 Agent 使用的 MCP 或 HTTP 入口。

選擇部署方式

方式適用場景儲存
Docker Compose需要最快完成本地或單伺服器部署。Docker volume 掛載到 /app/data,SQLite 位於 /app/data/connect.sqlite
原始碼執行你正在開發 OOMOL OpenConnector 或 provider executor。預設使用本地 ./data/connect.sqlite;也可設定 OOMOL_CONNECT_DATA_DIR
Cloudflare Workers需要由 Cloudflare 託管 runtime state 和 metadata。D1 儲存執行時記錄,R2 儲存臨時中轉檔案。

準備 runtime

部署前先確定這些值:

用途
OOMOL_CONNECT_ADMIN_TOKEN當 Web 控制台、/api 或本地 API 文件可被你的 shell 之外存取時,用它保護管理面。
OOMOL_CONNECT_ENCRYPTION_KEY加密儲存 provider 憑據和 OAuth client secret。請放在你的密鑰管理系統中。
OOMOL_CONNECT_ORIGIN設定 OAuth callback URL 使用的公開 origin。瀏覽器透過 tunnel、網域或 Worker URL 存取 runtime 時需要設定。
Runtime tokens讓 Agent 和 client 呼叫 /v1/mcp。可在 Web 控制台 Access 頁面或管理 API 中建立。
Action policy限制哪些 actions 可以透過 /v1 和 MCP 執行。使用 OOMOL_CONNECT_ALLOWED_ACTIONSOOMOL_CONNECT_BLOCKED_ACTIONS

Runtime 資料庫需要按敏感檔案處理。未設定 OOMOL_CONNECT_ENCRYPTION_KEY 時,OOMOL OpenConnector 仍可執行,但 provider secret 會存放在敏感的本地 SQLite 檔案中。

使用 Docker Compose 執行

先複製 OOMOL OpenConnector 儲存庫,進入專案目錄,再啟動 runtime:

git clone https://github.com/oomol-lab/open-connector.git
cd open-connector
docker compose up --build

開啟 Web 控制台:

http://localhost:3000

開啟生成的 API 文件:

http://localhost:3000/docs

用一個無需認證的 action 驗證 runtime:

curl -s -X POST http://localhost:3000/v1/actions/hackernews.get_top_stories \
  -H 'content-type: application/json' \
  -d '{"input":{}}'

Docker Compose 會把執行時狀態儲存到 connector-data volume。容器內的 SQLite 路徑是:

/app/data/connect.sqlite

私有伺服器部署時,至少在啟動前設定 admin token 和 encryption key:

請在 Bash 中執行這些範例。僅在隱藏輸入提示中填寫真實憑證,請勿將憑證寫入 shell 命令或命令日誌。

(
  set -eu
  read -r -s -p "OOMOL_CONNECT_ADMIN_TOKEN: " OOMOL_CONNECT_ADMIN_TOKEN
  echo
  test -n "$OOMOL_CONNECT_ADMIN_TOKEN"
  export OOMOL_CONNECT_ADMIN_TOKEN
  read -r -s -p "OOMOL_CONNECT_ENCRYPTION_KEY: " OOMOL_CONNECT_ENCRYPTION_KEY
  echo
  test -n "$OOMOL_CONNECT_ENCRYPTION_KEY"
  export OOMOL_CONNECT_ENCRYPTION_KEY
  docker compose up --build
)

如果 runtime 透過公開網域或 tunnel 暴露,需要同時啟用管理端與執行端鑑權,並設定公開 origin:

(
  set -eu
  export OOMOL_CONNECT_ORIGIN="https://connect.example.com"
  read -r -s -p "OOMOL_CONNECT_ADMIN_TOKEN: " OOMOL_CONNECT_ADMIN_TOKEN
  echo
  test -n "$OOMOL_CONNECT_ADMIN_TOKEN"
  export OOMOL_CONNECT_ADMIN_TOKEN
  read -r -s -p "OOMOL_CONNECT_RUNTIME_TOKEN: " OOMOL_CONNECT_RUNTIME_TOKEN
  echo
  test -n "$OOMOL_CONNECT_RUNTIME_TOKEN"
  export OOMOL_CONNECT_RUNTIME_TOKEN
  read -r -s -p "OOMOL_CONNECT_ENCRYPTION_KEY: " OOMOL_CONNECT_ENCRYPTION_KEY
  echo
  test -n "$OOMOL_CONNECT_ENCRYPTION_KEY"
  export OOMOL_CONNECT_ENCRYPTION_KEY
  docker compose up --build
)

透過環境變數提供 runtime token 適合公開部署的首次啟動。如果希望改用控制台建立的 oct_… token,應先讓 runtime 保持私有,建立第一個 token 後再開放公網存取。

Docker 映像檔會在容器內綁定 0.0.0.0。外部存取範圍由主機防火牆、反向代理或容器平台控制。

保護管理面和執行面

管理端 HTTP client 呼叫 /api/docs 或 Web 控制台時發送:

Authorization: Bearer replace-with-an-admin-token

在 Web 控制台 Access 頁面為 Agent 和 SDK 類 client 建立 runtime token。token 只顯示一次,SQLite 中只儲存 hash。未設定 token 的 runtime 必須留在 localhost 或私有網路中。

也可以透過管理 API 建立:

以下帶驗證的請求範例需要 Python 3 和 curl 7.76.0 或更新版本,以支援這些範例使用的 —fail-with-body 選項。範例透過隱藏提示讀取機密值,再透過標準輸入傳給 curl,不把憑據放入命令列參數或 shell 歷史紀錄。

python3 - <<'PY'
import getpass
import json
import subprocess
import warnings

warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
    raise SystemExit("A token is required")
payload = {'name': 'Claude Desktop'}
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
config += "data = " + json.dumps(json.dumps(payload)) + "\n"
subprocess.run(
    ['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'POST', 'http://localhost:3000/api/runtime-tokens', '--header', 'content-type: application/json'],
    input=config, text=True, check=True,
)
PY

之後 runtime client 呼叫 /v1/mcp 時發送:

Authorization: Bearer oct_...

為了啟動腳本和向後相容,仍然可以使用 OOMOL_CONNECT_RUNTIME_TOKEN

(
  set -eu
  read -r -s -p "OOMOL_CONNECT_ADMIN_TOKEN: " OOMOL_CONNECT_ADMIN_TOKEN
  echo
  test -n "$OOMOL_CONNECT_ADMIN_TOKEN"
  export OOMOL_CONNECT_ADMIN_TOKEN
  read -r -s -p "OOMOL_CONNECT_RUNTIME_TOKEN: " OOMOL_CONNECT_RUNTIME_TOKEN
  echo
  test -n "$OOMOL_CONNECT_RUNTIME_TOKEN"
  export OOMOL_CONNECT_RUNTIME_TOKEN
  docker compose up --build
)

限制 Agent 可以執行的 actions:

OOMOL_CONNECT_ALLOWED_ACTIONS="hackernews.*,github.get_current_user" docker compose up --build

即使較大的 allowlist 包含某些 actions,也可以單獨封鎖它們:

OOMOL_CONNECT_ALLOWED_ACTIONS="github.*" \
OOMOL_CONNECT_BLOCKED_ACTIONS="github.delete_repository" \
docker compose up --build

連接 API-key provider

GitHub 是一個簡單的 API-key 範例,因為它可以使用 personal access token。

查看 provider 契約:

python3 - <<'PY'
import getpass
import json
import subprocess
import warnings

warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
    raise SystemExit("A token is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
subprocess.run(
    ['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'GET', 'http://localhost:3000/api/providers/github'],
    input=config, text=True, check=True,
)
PY

儲存預設 GitHub 連接:

python3 - <<'PY'
import getpass
import json
import subprocess
import warnings

warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
    raise SystemExit("A token is required")
payload = {'authType': 'api_key', 'values': {'apiKey': None}}
payload["values"]["apiKey"] = getpass.getpass("GitHub API key: ")
if not payload["values"]["apiKey"]:
    raise SystemExit("An API key is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
config += "data = " + json.dumps(json.dumps(payload)) + "\n"
subprocess.run(
    ['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'PUT', 'http://localhost:3000/api/connections/github', '--header', 'content-type: application/json'],
    input=config, text=True, check=True,
)
PY

透過 runtime 呼叫 GitHub:

python3 - <<'PY'
import getpass
import json
import subprocess
import warnings

warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("Runtime token: ")
if not token:
    raise SystemExit("A token is required")
payload = {'input': {}}
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
config += "data = " + json.dumps(json.dumps(payload)) + "\n"
subprocess.run(
    ['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'POST', 'http://localhost:3000/v1/actions/github.get_current_user', '--header', 'content-type: application/json'],
    input=config, text=True, check=True,
)
PY

查看已設定的連接,以及會暴露給 Agent 的安全帳號身分:

python3 - <<'PY'
import getpass
import json
import subprocess
import warnings

warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
    raise SystemExit("A token is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
subprocess.run(
    ['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'GET', 'http://localhost:3000/api/connections'],
    input=config, text=True, check=True,
)
PY

命名連接

同一個 provider 需要多個帳號時,加入 connectionName

python3 - <<'PY'
import getpass
import json
import subprocess
import warnings

warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
    raise SystemExit("A token is required")
payload = {'authType': 'api_key', 'connectionName': 'work', 'values': {'apiKey': None}}
payload["values"]["apiKey"] = getpass.getpass("GitHub API key: ")
if not payload["values"]["apiKey"]:
    raise SystemExit("An API key is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
config += "data = " + json.dumps(json.dumps(payload)) + "\n"
subprocess.run(
    ['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'PUT', 'http://localhost:3000/api/connections/github', '--header', 'content-type: application/json'],
    input=config, text=True, check=True,
)
PY

執行時選擇該帳號:

python3 - <<'PY'
import getpass
import json
import subprocess
import warnings

warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("Runtime token: ")
if not token:
    raise SystemExit("A token is required")
payload = {'input': {}}
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
config += "data = " + json.dumps(json.dumps(payload)) + "\n"
subprocess.run(
    ['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'POST', 'http://localhost:3000/v1/actions/github.get_current_user', '--header', 'x-oo-connector-alias: work', '--header', 'content-type: application/json'],
    input=config, text=True, check=True,
)
PY

也可以使用 alias query 參數。

連接 OAuth provider

OAuth provider 使用你自己的 provider OAuth app。先在 provider OAuth app 中設定 callback URL。這個 callback URL 是 OpenConnector origin 加上 /oauth/callback

使用預設連接埠時,GitHub 使用這個 callback URL:

http://localhost:3000/oauth/callback

如果 runtime 透過其他 origin 暴露,請先設定 OOMOL_CONNECT_ORIGIN,再啟動 runtime,然後用該 origin 拼出 callback URL:

https://connect.example.com/oauth/callback

把準確 callback URL 填入 provider OAuth app。

在 Web 控制台中儲存 OAuth client:

  1. 開啟 http://localhost:3000
  2. 開啟 provider 頁面,例如 GitHub
  3. 點擊 Configure OAuth ClientEdit OAuth Client
  4. 貼上 provider app 的 Client IDClient Secret
  5. 點擊 Save OAuth Client

如果 provider 需要額外 client config 欄位,請在同一個 OAuth client 表單中填寫。繼續設定前,請使用能展示該 provider 所需欄位的 OpenConnector 控制台版本。

儲存 OAuth client 後,在 provider 頁面點擊 Connect。在 provider 授權頁批准授權。Provider 跳回 OpenConnector 後,確認 provider 頁面顯示帳號已連接。

給 Agent 使用工具

支援 MCP 的 client 可以連接到:

http://localhost:3000/mcp

MCP server 暴露一組面向探索流程的工具:

  • list_apps
  • search_actions
  • get_action_guide
  • execute_action

預覽 MCP tool metadata:

python3 - <<'PY'
import getpass
import json
import subprocess
import warnings

warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("Runtime token: ")
if not token:
    raise SystemExit("A token is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
subprocess.run(
    ['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'GET', 'http://localhost:3000/mcp/tools'],
    input=config, text=True, check=True,
)
PY

HTTP client 使用 /v1 runtime API:

python3 - <<'PY'
import getpass
import json
import subprocess
import warnings

warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("Runtime token: ")
if not token:
    raise SystemExit("A token is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
subprocess.run(
    ['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'GET', 'http://localhost:3000/v1/actions'],
    input=config, text=True, check=True,
)
PY

每個 action 都有一份本地 Markdown guide,包含輸入 schema、scope、provider 權限、目前連接身分和請求範例:

python3 - <<'PY'
import getpass
import json
import subprocess
import warnings

warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
    raise SystemExit("A token is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
subprocess.run(
    ['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'GET', 'http://localhost:3000/api/actions/github.get_current_user/agent.md'],
    input=config, text=True, check=True,
)
PY

Web 控制台也可以為每個 action 複製 cURL、TypeScript 和 agent prompt 範例。

從原始碼執行

開發 OOMOL OpenConnector 或 provider executor 時使用原始碼工作流程。請使用 Node.js 22 或更新版本。

git clone https://github.com/oomol-lab/open-connector.git
cd open-connector
npm install
npm run build:web
npm run dev

npm installnpm run dev 會在生成檔案缺失或過期時建立本地檔案。

原始碼執行時,執行時狀態儲存在:

./data/connect.sqlite

使用其他資料目錄:

OOMOL_CONNECT_DATA_DIR=/path/to/data npm run dev

Admin token、encryption key、origin、runtime token 和 action policy 這些環境變數與前面相同。

部署到 Cloudflare Workers

Cloudflare Workers 支援作為 metadata 和 runtime state 的部署目標。

先複製儲存庫,再建立 Cloudflare 資源並部署:

git clone https://github.com/oomol-lab/open-connector.git
cd open-connector
cp wrangler.example.jsonc wrangler.local.jsonc
npm install
npm run generate:catalog
npm run build:web
npx wrangler d1 create oomol-connect
npx wrangler r2 bucket create oomol-connect-transit-files

部署前,把 Cloudflare 回傳的 D1 database_id 填入被忽略的 wrangler.local.jsonc

首次設定期間保持 Worker 私有:在本機 Wrangler 配置中將 workers_dev 和 preview_urls 設為 false,不配置公開路由或自訂網域。設定完下面的三個 secret 前,請勿開放公網存取。

npx wrangler d1 migrations apply oomol-connect --remote --config wrangler.local.jsonc
npm run deploy:cloudflare

使用 Wrangler 設定 secrets:

npx wrangler secret put OOMOL_CONNECT_ADMIN_TOKEN --config wrangler.local.jsonc
npx wrangler secret put OOMOL_CONNECT_RUNTIME_TOKEN --config wrangler.local.jsonc
npx wrangler secret put OOMOL_CONNECT_ENCRYPTION_KEY --config wrangler.local.jsonc

設定完三個 secret 後,在本機配置中啟用所需的公開路由並重新部署。

wrangler.local.jsonc 中把 OOMOL_CONNECT_ORIGIN 設為公開 Worker origin。Admin token 保護控制台與 /api,runtime token 從第一次公開請求開始保護 /v1/mcp

Cloudflare 使用相同的環境變數名稱設定 origin、auth tokens、action policy、中轉檔案限制和憑據加密。PORTHOSTOOMOL_CONNECT_DATA_DIR 只適用於本地 Node runtime。

Worker runtime 會提供 catalog metadata、/api/v1 metadata endpoints、連接、runtime tokens、OAuth config/state、基於 R2 的中轉檔案,以及生成版 provider action executor registry。如果希望自動清理未讀取的過期中轉檔案,請為 transit bucket 設定 R2 lifecycle rule。

維運部署

為支援和恢復保留這些記錄:

記錄查看位置
Runtime 資料庫Docker volume、本地 OOMOL_CONNECT_DATA_DIR 或 Cloudflare D1。
臨時中轉檔案本地 runtime 的 OOMOL_CONNECT_DATA_DIR/files,或 Cloudflare R2。
Admin token 和 encryption key你的密鑰管理系統。OOMOL OpenConnector 不會替你儲存 encryption key。
Runtime token 前綴Web 控制台 Access 頁面或 /api/runtime-tokens。完整 runtime token 只顯示一次。
執行歷史Web 控制台最近執行記錄,或 GET /api/runs

對於檔案上傳類 action,本地中轉檔案儲存在 OOMOL_CONNECT_DATA_DIR/files 並按時間清理。使用 OOMOL_CONNECT_TRANSIT_FILE_TTL_SECONDSOOMOL_CONNECT_TRANSIT_FILE_MAX_BYTES 調整生命週期和上傳大小。

排查建議

現象檢查項
Web 控制台或 /api 回傳 unauthorized。發送 Authorization: Bearer <admin-token>,並確認 OOMOL_CONNECT_ADMIN_TOKEN 與目前執行環境一致。
/v1/mcp 回傳 unauthorized。使用 Access 頁面或 POST /api/runtime-tokens 建立的 runtime token。Admin token 用於管理面,不用於 runtime client。
OAuth 回調到了錯誤主機。OOMOL_CONNECT_ORIGIN 設為使用者在瀏覽器中開啟的 origin,重啟 runtime,然後在 provider app 中使用 <openconnector-origin>/oauth/callback
Action 找不到憑據。查看 /api/connections、目前 x-oo-connector-alias,以及連接是否仍然 available。
Action 被封鎖。檢查 OOMOL_CONNECT_ALLOWED_ACTIONSOOMOL_CONNECT_BLOCKED_ACTIONS。Blocked actions 優先於更寬泛的 allowlist。
Provider 憑據之前可用,現在失敗。如果 token 已過期且沒有 refresh token,請重新連接 provider;同時確認 encryption key 與已儲存記錄相符。