瀏覽文件

OOMOL TypeScript SDK 參考

@oomol-lab/connector 包含三個 TypeScript client。請先按產品路徑選擇 client,再查閱本頁的共享設定與 API 參考:

  • Connector 透過託管閘道呼叫你在 OOMOL 帳號中連接的帳號。
  • ProjectConnector 為 SaaS 產品的最終使用者連接並呼叫各自的帳號。
  • OpenConnector 呼叫由你維運的 OpenConnector runtime。

三個 client 共用傳輸行為、精確 action 型別和錯誤模型,但使用的 key、帳號邊界與可用方法不同。

這個套件輕量、零依賴;SDK 直接建構請求並解析回應。gmail.search_threadsslack.post_messagenotion.append_block 等 action 可以直接透過 TypeScript 呼叫。

安裝

npm install @oomol-lab/connector   # 或:bun add / pnpm add / yarn add

需要 Node ≥ 18(依賴內建的 fetchAbortController)。SDK 只發佈 dist,零執行時依賴,且 sideEffects: false,可以乾淨地做 tree-shaking。請在 Node、Bun、Deno 或邊緣 worker 等可信服務端環境中使用 ConnectorProjectConnector。瀏覽器應用應呼叫你的後端,不要把個人或 Project API key 打包進客戶端程式碼。

三個客戶端一覽

ConnectorProjectConnectorOpenConnector
驗證個人 api_… keyProject oo_proj_… key選用 runtime token oct_…
對接OOMOL 託管閘道OOMOL 託管閘道你執行的 OpenConnector runtime
帳號資源個人或 Team 的 connectionsexternal users 的 connected accountsruntime 中的 connections
用途呼叫個人或 Team 已連接的帳號為 SaaS 產品使用者連接並呼叫帳號呼叫自部署 runtime
接入指南Connector SDKProjectConnectorOpenConnector SDK

Connector:呼叫個人或 Team connections

取得 API key

你需要一個 OOMOL Connector 的個人 API key,形如 api_…。在 OOMOL Console 建立:

https://console.oomol.com/api-key

把它設為環境變數;本指南統一使用 OOMOL_API_KEY。每個請求都會由閘道授權,並以 Authorization: Bearer <apiKey> 傳送。

個人 api_… key 在個人或 Team connection 上執行 action。代最終使用者連接帳號時,使用 Project key(oo_proj_…)和 ProjectConnector;見 ProjectConnector 接入指南。呼叫自部署 runtime 時,使用選用 runtime token(oct_…)和 OpenConnector;見 OpenConnector SDK 指南

快速上手

建構客戶端後即可呼叫 action。下面兩種寫法等價,可任選一種。

import { Connector } from "@oomol-lab/connector";

const oomol = new Connector({ apiKey: process.env.OOMOL_API_KEY! });

// 路徑 1:動態字串。可呼叫任意 action id。
const { threads } = await oomol.execute("gmail.search_threads", { query: "from:boss" });

// 路徑 2:namespace 語法糖。底層仍是同一個呼叫。
const result = await oomol.gmail.search_threads({ query: "is:unread" });

execute 直接回傳 action 的輸出。當你還想要執行中繼資料時,用 executeRaw

const raw = await oomol.executeRaw("gmail.search_threads", { query: "from:ceo" });
raw.data;        // 和 execute() 的返回值相同
raw.executionId; // 服務端分配的執行 id(用於工單支援 / 日誌關聯)
raw.actionId;    // 回顯的 action id
raw.message;     // 成功響應信封裡的可讀訊息

核心呼叫流程由 executeexecuteRaw 組成。

Connector 的概念

SDK 模型由五個核心概念組成,驗證、憑證和實際 provider 呼叫都由閘道處理:

術語含義
Gateway(閘道)這個客戶端對接的 OOMOL Connector 託管服務。它持有憑證、真正發起對 provider 的呼叫,並回傳統一的信封。SDK 在本機執行任何整合邏輯。
Provider / service一個第三方 API(gmailslackgithubnotion……)。它就是 action id 的 <service> 前綴。
Actionprovider 上的一個操作,識別為 "<service>.<action>"(例如 gmail.search_threads)。action 由閘道提供,客戶端負責呼叫。
Connection(連接)某個 provider 已授權、已儲存的憑證。你從不直接接觸 token;只用 connectionName 指明用哪一個連接。OAuth 和憑證生命週期是閘道的職責。
Team(團隊)選用的租戶作用域,以 x-oo-team-name 標頭傳送。

同一套詞彙貫穿整個 SDK 介面:action id 始終是 "<service>.<action>"connectionName 用來選擇已儲存的連接;在專案客戶端中,externalUserId 標識你的某個最終使用者。

常見操作

想要……說明
執行一個已建模的 actionexecute / executeRaw帶型別的一行呼叫。executeRaw 還會回傳 { executionId, actionId, message }
呼叫尚未建模為 action 的 endpointproxy透傳到上游 API,由閘道注入該連接的憑證。
把 action 餵給 LLM / 建構動態表單catalog任意 action 或 provider 的執行時 JSON Schema(2020-12)。
查看已經連了什麼apps.list唯讀列出你已經建立的連接。
你的使用者連接他們自己的帳號ProjectConnector一個獨立的、按專案作用域的客戶端,用來代你的最終使用者連接帳號並執行 action。

provider 和 action 的覆蓋由閘道提供。目前支援 600+ 個 provider,並持續增加;可透過 oomol.catalog.providers() 在執行時發現。

精確型別(選用)

動態字串路徑對任意 actionId 都能編譯通過。預設情況下,每個 action 使用寬鬆型別(入參和出參都是 Record<string, any>),便於先完成呼叫。需要精確入/出參型別和 JSDoc 時,安裝配套型別套件,並為用到的每個 provider 新增一行 side-effect import

import { Connector } from "@oomol-lab/connector";
import "@oomol-lab/connector-types/gmail";   // gmail.* 的精確型別 + JSDoc
import "@oomol-lab/connector-types/slack";   // ……以及 slack.*

const oomol = new Connector({ apiKey: process.env.OOMOL_API_KEY! });

await oomol.gmail.search_threads({ query: "from:boss" }); // 入參與出參現在都是精確型別
await oomol.notion.append_block({ pageId, text });        // 沒 import notion → 仍可寬鬆呼叫
npm install -D @oomol-lab/connector-types

精確型別由 @oomol-lab/connector-types 和 side-effect import 提供,專案中不會生成額外檔案。已註冊的 action 會得到字面量補全和精確的入/出參型別;未註冊的會退化為 Record<string, any>。即使型別套件落後於後端,新 action 也可以繼續透過寬鬆型別呼叫。核心執行時與型別套件分別發佈。

需要把 moduleResolution 設為 bundlernode16nodenext,子路徑 import(@oomol-lab/connector-types/gmail)才能解析。設定細節見 @oomol-lab/connector-types 儲存庫。

設定

apiKey 外每個欄位都是選用的:

new Connector({
  apiKey: process.env.OOMOL_API_KEY!,        // 必填
  baseUrl: "https://connector.oomol.com/v1", // 預設值
  team: "acme",                              // 預設團隊 → x-oo-team-name
  connectionName: "work",                    // 預設連線(更推薦按呼叫 / using() 設定)
  timeoutMs: 30_000,                         // 預設單次請求超時
  maxRetries: 2,                             // 預設;對 429 / 5xx / 網路錯誤用退避 + 抖動重試
  fetch: customFetch,                        // 用於測試 / 代理 / 鏈路追蹤時注入
});
欄位預設值說明
apiKey必填。以 Authorization: Bearer <apiKey> 傳送。
baseUrlhttps://connector.oomol.com/v1透過 client 設定顯式覆寫。
team呼叫在哪個租戶下執行。
connectionName當某個 provider 有多個連接時,選用哪一個。單連接場景可作為客戶端預設值;多連接場景建議按呼叫或用 using() 設定。
timeoutMs30_000單次請求逾時(毫秒)。
maxRetries2對 429 / 5xx / 網路錯誤重試,採用指數退避 + 抖動。
fetch全域 fetch為測試、代理 agent 或鏈路追蹤注入自訂 fetch

對於 gmail.send_emailslack.post_message 這類有副作用的 action,除非已經確認該 action 提供可用的幂等機制,否則應傳入 { retries: 0 }。網路錯誤可能發生在 provider 已接受請求之後,自動重試可能造成重複操作。

作用域與按呼叫選項

三個層級按此優先順序解析:按呼叫選項 > using() 作用域 > 客戶端預設值。

using() 回傳一個不可變的、合併了給定預設值的子客戶端;原客戶端不受影響:

const work = oomol.using({ connectionName: "work", team: "acme" });
await work.gmail.search_threads({ query: "label:urgent" }); // 在 "work" / "acme" 下執行

按呼叫選項只作用於本次呼叫,並具有最高優先順序:

await oomol.execute(
  "gmail.search_threads",
  { query: "from:ceo" },
  {
    team: "acme",               // 本次呼叫覆蓋團隊
    connectionName: "alt",      // 本次呼叫選用另一個連線
    timeoutMs: 10_000,          // 本次呼叫用更緊的超時
    retries: 0,                 // 本次呼叫禁用重試
    signal: controller.signal,  // 傳入 AbortSignal
  },
);

connectionName 按同樣的層級解析。它在傳輸層以 x-oo-connector-alias 標頭攜帶(閘道欄位名為 alias),SDK 介面統一使用 connectionName

Proxy:直接呼叫上游 endpoint

proxy 可以直接存取上游 API endpoint。閘道會注入所選 connection 的憑證,請求和回應保持上游 API 的原始形態。

// 帶型別的 GET。請求路徑使用 `endpoint` 欄位。
const repos = await oomol.proxy<Array<{ name: string }>>("github", {
  endpoint: "/user/repos",
  method: "GET",
  query: { per_page: 5, sort: "updated" },
});
repos.status;               // 上游 HTTP 狀態碼
repos.data.map((r) => r.name);

// 帶 body 和上游 header 的 POST;這些 header 會傳送給 provider。
await oomol.proxy("github", {
  endpoint: "/repos/acme/widgets/issues",
  method: "POST",
  headers: { "X-GitHub-Api-Version": "2022-11-28" },
  body: { title: "Tracking issue", labels: ["chore"] },
});

endpoint 既接受路徑(相對於 provider 的 base URL 解析),也接受完整 URL,適用於帶區域域名的 provider,例如 https://eu.posthog.com/api/...methodGET | POST | PUT | PATCH | DELETE 之一。回應是 { status, headers, data }。proxy 的 body 在後端是 strict 的:未知頂層 key 會以 invalid_input 拒絕。

Catalog:檢視 provider 與 action

catalog 提供唯讀執行時中繼資料,可用於動態表單、驗證和 LLM 工具定義。入/出參 schema 使用 JSON Schema(2020-12),與編譯期型別套件相互獨立。

// 列出 provider;可選地在服務端收窄。
const all = await oomol.catalog.providers();                       // 全部 provider
const mailish = await oomol.catalog.providers({ q: "mail" });      // 全文搜尋 → ?q=
const some = await oomol.catalog.providers({ service: ["gmail", "slack"] }); // 限定 → ?service=…

// 某個 service 的全部 action。
const actions = await oomol.catalog.actions("gmail");

// 單個 action 的完整後設資料,包含執行時 JSON Schema。
const meta = await oomol.catalog.action("gmail.search_threads");
meta.name;          // 可讀名稱
meta.requiredScopes;// 該 action 需要的 OAuth scope
meta.inputSchema;   // 入參的 JSON Schema(2020-12)
meta.outputSchema;  // 出參的 JSON Schema(2020-12)

每個 provider 攜帶 { service, displayName, iconUrl, homepageUrl, categories, authTypes }

Apps:列出你已連接的帳號

apps.list() 回傳閘道已持有連接的唯讀檢視。連接建立和移除在 Console 中完成。

const apps = await oomol.apps.list();
for (const app of apps) {
  // { id, service, status, connectionName, … };未設定時 connectionName 為 null。
  console.log(`${app.service}: id=${app.id} status=${app.status} connectionName=${app.connectionName}`);
}

// 把某個連線的 connectionName 作為按呼叫選擇器傳回,即可指定它。
const work = apps.find((a) => a.connectionName === "work");
if (work) {
  await oomol.execute("gmail.search_threads", { query: "is:unread" }, { connectionName: "work" });
}

錯誤處理

失敗會拋出帶型別的 ConnectorError。呼叫方主動取消(被中止的 AbortSignal)會拋出標準 AbortError,可與閘道或傳輸錯誤區分。

import { Connector, ConnectorError, isRetryable } from "@oomol-lab/connector";

try {
  await oomol.slack.post_message({ channel: "#general", text: "shipped" });
} catch (err) {
  if (err instanceof ConnectorError) {
    err.code;        // 可判別聯合,例如 "rate_limited"、"credential_expired"
    err.status;      // HTTP 狀態碼(客戶端 / 網路錯誤時為 0)
    err.requestId;   // 失敗關聯 id
    err.actionId;    // 適用時
    err.executionId; // 適用時
    err.data;        // 上游響應體,例如 provider_error 時
    if (isRetryable(err)) {
      // 429 / 5xx / 網路 / rate_limited / proxy_upstream_timeout / request_in_progress
    }
  } else {
    throw err; // 非 ConnectorError,例如呼叫方取消產生的 AbortError;重新丟擲
  }
}

err.code 是一個開放聯合:已知後端碼有自動補全,新的後端碼也會作為字串透傳。處理時建議保留預設分支。常見錯誤碼按組如下:

分組錯誤碼
輸入 / 請求invalid_inputinvalid_request_payloadinvalid_request_signature
App / providerapp_not_foundapp_not_readyapp_auth_type_mismatchprovider_not_foundprovider_not_configuredprovider_config_not_foundprovider_errorprofile_not_found
憑證 / 驗證credential_expiredscope_missinguser_oauth_client_required
連接選擇connection_ambiguousconnection_account_conflictconnection_alias_conflictconnection_request_not_foundconnected_account_not_found
Proxyproxy_not_supportedproxy_upstream_errorproxy_upstream_timeoutproxy_response_too_large
限流 / 並行rate_limitedrequest_in_progressrequest_key_conflictrequest_key_used
僅客戶端(status 0,請求未發出或傳輸失敗)client_invalid_requestclient_timeoutclient_network_errorclient_wait_timeout

isRetryable(err)rate_limitedproxy_upstream_timeoutrequest_in_progress、HTTP 429、任意 5xx 以及傳輸失敗(status 0)回傳 true。對客戶端驗證錯誤(client_invalid_request)和 waitForConnection 的等待上限(client_wait_timeout)回傳 false;這兩類錯誤通常需要修改呼叫或等待邏輯。

取消與逾時

傳入 AbortSignal 可取消呼叫;設定 timeoutMs 可限定單次呼叫。內建重試層會處理瞬時失敗;需要一次確定性的單次嘗試時,可設定 retries: 0

const controller = new AbortController();
setTimeout(() => controller.abort(), 50);
try {
  await oomol.execute("gmail.search_threads", { query: "huge" }, { signal: controller.signal });
} catch (err) {
  (err as Error).name; // "AbortError"
}

ProjectConnector:為產品使用者連接帳號

Connector你自己的連接上執行 action。ProjectConnector 面向 SaaS 產品:你的最終使用者透過你的應用連接他們自己的 Gmail / Slack / GitHub / …… 帳號,然後由你的後端代他們執行 action。這屬於託管驗證(managed auth)模型,使用方式類似 ComposioPipedream Connect

使用 OAuth 時,使用者在閘道託管頁面上授權,provider token 保留在閘道中,你的程式碼只持有不透明識別碼。project.connect.apiKeyproject.connect.customCredential 的邊界不同:最終使用者的密鑰會先由你的可信後端接收,再傳送給閘道。不要讓這些密鑰進入瀏覽器、提示詞、模型上下文或日誌。

最終使用者識別碼

externalUserId 是專案客戶端的使用者隔離鍵,由選定,通常使用你自己資料庫裡的使用者 id。連接帳號、等待連接、執行 action 等專案操作都按它作用域。始終傳入一致的 externalUserId,閘道會讓每個使用者的連接彼此隔離。

建構專案客戶端

ProjectConnector 是一個使用 Project API keyoo_proj_…)的獨立客戶端。它提供 connect.*waitForConnectiongetUserProfileexecuteexecuteRawforUser 等 Project 作用域操作。

import { ProjectConnector } from "@oomol-lab/connector";

const project = new ProjectConnector({ apiKey: process.env.OOMOL_PROJECT_API_KEY! }); // oo_proj_...

先在 Console 完成設定。 在你的後端連接帳號之前,需要由管理員在 OOMOL Console 建立 Project、服務設定(provider config)和 Project API key。一次性設定和對應的後端 REST 流程見 Connector for SaaS 使用指南。本 SDK 是同一套執行時 API 的帶型別封裝。

OAuth:建立連結,再等待完成

OAuth 分兩步:建立待處理的連接請求,引導使用者授權,再等待完成。

// 1. 為你的某個使用者建立一個待處理的連線請求。
const request = await project.connect.oauth("user_42", {
  service: "gmail",
  connectionName: "work",                          // 要賦予的名字;之後用它來指定這個賬號
  returnUri: "https://app.example.com/connected",  // 回撥完成後閘道器把使用者帶回到這裡
});

// 2. 把使用者帶到 provider 的授權頁面。
redirectUserTo(request.authorizationUrl);

// 3. 輪詢直到使用者完成(或失敗 / 過期)。返回最終的連線請求。
const connected = await project.waitForConnection(request);
connected.status;             // "connected" | "failed" | "expired"
connected.connectedAccountId; // 連線成功後儲存的賬號 id

authorizationUrl 會先開啟 Connector 託管的授權入口頁,展示你的專案和使用者將要授權的 provider,然後再把使用者帶到 provider 的 OAuth 頁面。

Connector 授權入口頁,顯示 my-saas 正在連線 Gmail 並即將跳轉到 Gmail

waitForConnection 會輪詢到請求離開 initiated 狀態後回傳它。如果使用者未完成授權,請求會自然變為 expiredmaxWaitMs(預設 600_000 毫秒,與請求過期時間一致)先耗盡時,會拋出錯誤碼為 client_wait_timeoutConnectorError;被中止的 signal 會拋出標準 AbortError

當你的 returnUri 被命中時,閘道會追加一些 query 參數,你可以在回呼頁面讀取:

status=success
service=gmail
providerConfigId=pc-1
externalUserId=user_42
connectedAccountId=ca-1

……或者在取消 / provider 出錯時:

status=error
code=<connector-error-code>
message=<human-readable-message>

API key / 自訂憑證:同步回傳

API key 和自訂憑證連接會立即回傳帳號;只有 OAuth 需要 waitForConnection

// 終端使用者自己的上游 key(例如 OpenAI 的 sk-…),不要填寫 oo_proj_ key。
const account = await project.connect.apiKey("user_42", { service: "openai", apiKey: "sk-..." });
account.available; // 該賬號此刻是否可以執行 action

// provider 特有的憑證欄位,由閘道器按 provider config 校驗。
await project.connect.customCredential("user_42", {
  service: "jira",
  values: { email: "user@acme.com", token: "..." },
});

每個 connect.* 呼叫用 serviceproviderConfigId恰好一個來標識 provider。當一個專案對同一 service 有多個設定時用 providerConfigId;簡單情形用 service

使用者連接的是哪個第三方帳號

getUserProfile 讀取一個已連接帳號背後的第三方帳號主體:你的最終使用者在 provider 上到底是誰。閘道會即時從 provider 拉取,並歸一化成跨 provider 統一的結構。

const { service, profile, fetchedAt } = await project.getUserProfile(account.connectedAccountId);

profile.id;          // provider 側穩定的使用者 id
profile.kind;        // "user" | "bot" | "service_account" | "unknown"(開放聯合型別)
profile.username;    // 使用者名稱/handle,或 null
profile.displayName; // 展示名,或 null
profile.avatarUrl;   // 頭像 URL,或 null
profile.email;       // 郵箱;已授權的 scope 不包含時為 null
profile.metadata;    // 閘道器暴露的 provider 特有欄位

provider 未提供或已授權 scope 未覆蓋的欄位(最常見的是 email)會保留在回應中並設為 nullfetchedAt 是閘道讀取該資料時的 Unix 毫秒時間戳。connectedAccountId 可以來自 connect.apiKey / connect.customCredential 的回傳值,或來自已進入 connected 狀態的 ConnectionRequest。未知 id 會以 connected_account_not_found 拒絕;provider 缺少 userProfile 能力時回傳 profile_not_found;帳號未就緒或不可用時還可能回傳 app_not_readyapp_auth_type_mismatchcredential_expired。限定到單一使用者的子客戶端上也有這個方法:user.getUserProfile(connectedAccountId)

代使用者執行 action

// provider service 從 actionId 字首("gmail")推導。
// 未傳 connectionName / connectedAccountId 時,使用該使用者最新的 active 賬號。
const out = await project.execute(
  "user_42",
  "gmail.search_threads",
  { query: "is:unread" },
  { connectionName: "work" },
);

帳號選擇優先順序:connectedAccountId(指定某個帳號)優先於 connectionName(按名字選帳號);兩者都不傳時,閘道使用該使用者在該 provider 下最新的 active 帳號。project.executeRaw 回傳與個人客戶端相同的 { data, executionId, actionId, message } 信封。

綁定到單一使用者

forUser 可預先綁定 externalUserId,後續呼叫不必重複傳 id:

const user = project.forUser("user_42");
const request = await user.connect.oauth({ service: "slack" });
const slack = await user.waitForConnection(request);
if (slack.status === "connected") {
  await user.execute(
    "slack.post_message",
    { channel: "#general", text: "shipped" },
    { connectedAccountId: slack.connectedAccountId },
  );
}

project.execute 複用與個人路徑相同的 @oomol-lab/connector-types 註冊表:已 import 的 provider 得到精確的入/出參,其餘保持寬鬆可呼叫。

授權請求與帳號生命週期

物件何時拿到關鍵欄位
ConnectionRequestconnect.oauth 回傳,可經 getConnectionRequest / waitForConnection 重新讀取idstatusinitiatedconnected / failed / expired)、authorizationUrlconnectedAccountIdexternalUserIdconnectionNameexpiresAt
ConnectedAccountconnect.apiKey / connect.customCredential 同步回傳;OAuth 請求完成後也指向它id / connectedAccountIdstatusactivereauth_requirederrordisconnected)、availableexternalUserIdconnectionNameservice

只有當執行所需條件都就緒時,available 才為 true:provider config 是 active、帳號是 active、底層 app 是 active,且憑證存在。兩個 status 欄位都是開放聯合;處理時保留預設分支,以相容新的後端狀態。

從 Composio / Pipedream 遷移?

Composio / Pipedream@oomol-lab/connector
userId / external_user_idexternalUserId
connectedAccounts.initiate / createConnectToken(OAuth)project.connect.oauth
connectedAccounts.initiate + AuthScheme.APIKeyproject.connect.apiKey
waitForConnection()project.waitForConnection()
tools.execute(slug, { userId, arguments })project.execute(externalUserId, actionId, input)
composio.getEntity(userId)project.forUser(externalUserId)

OpenConnector:呼叫自部署 runtime

想自己執行開源的 Connector 服務——在 localhost、Docker,或你自己的基礎設施上?OpenConnector 就是它的個人客戶端。它把 Connector 的介面(兩條呼叫路徑、proxycatalogapps)指向你自己執行的伺服器,所以你已經寫好的程式碼幾乎不用改。搭建伺服器是另一個話題,見 OpenConnector 自託管指南

OpenConnector client 指向一個由你維運的 runtime。帳號組織方式、connection 選擇和存取策略由該 runtime 的設定決定。

import { OpenConnector } from "@oomol-lab/connector";

const open = new OpenConnector(); // 僅用於本地或私有網路;預設 http://localhost:3000

await open.execute("hackernews.get_top_stories", {});             // 路徑 1 — 動態字串
await open.gmail.search_threads({ query: "from:boss" });          // 路徑 2 — namespace 語法糖,登錄檔型別相同
await open.proxy("github", { endpoint: "/user", method: "GET" }); // 路徑 3 — 透傳到尚未建模的 endpoint
await open.catalog.search("send email", { limit: 5 });            // catalog 擴充套件:search、services
await open.apps.list();                                           // 只讀檢視執行時的連線

兩條呼叫路徑和精確型別工作流都與個人 Connector 一致;同樣的 @oomol-lab/connector-types side-effect import 也適用。OpenConnector 的 proxy.endpoint 接受以 / 開頭的相對路徑;傳入絕對 URL 會回傳 invalid_input。缺少 proxy executor 的 provider 會回傳 proxy_not_supported

指向你的伺服器

baseUrl 接收伺服器 origin,客戶端會自動新增 API 路徑前綴。未建立 token 的 runtime 只適合 localhost 或其他私有網路。透過公開 URL 暴露前,應先在 Web Console 的 Access 頁面建立 runtime token(oct_…),並要求所有 /v1/mcp client 攜帶它。

const open = new OpenConnector({
  baseUrl: "https://connect.internal.example.com",       // 伺服器 origin
  runtimeToken: process.env.OOMOL_CONNECT_RUNTIME_TOKEN!, // oct_…;公開 runtime 必填
  connectionName: "work",                                // 可選的客戶端級預設連線
});

每個欄位都是選用的。timeoutMsmaxRetriesfetch 的行為與託管客戶端完全一致。

僅執行時才有的介面

OpenConnector 提供以下 runtime 專用介面:

await open.health();                                    // { ok, runtime } — 連通性 / 鑑權探測
await open.catalog.services();                          // 所有有 action 的 service id
await open.catalog.search("top stories", { limit: 3 }); // 按全文相關度對 action 排序
await open.apps.listByService("github");                // 某個 service 的連線
await open.apps.authenticated(["github", "notion"]);    // 哪些已存有**真實**憑證

連接選擇分兩層:按呼叫的 connectionName 覆寫客戶端預設值;兩層都省略時,runtime 使用 "default" connection。

管理操作使用 Web Console。 在主控台建立 connection、設定 OAuth client 並生成 runtime token;OpenConnector SDK 呼叫已經設定好的 runtime。設定方法見自託管指南。當 service id 與成員名(execute / executeRaw / health / proxy / catalog / apps)衝突時,可以透過 execute("<service>.<action>", …) 呼叫。

完整可執行範例——examples/open.ts

參考

Connector(個人 api_… key)

new Connector(config: ClientConfig)

oomol.execute(actionId, input, options?)     // → action 輸出
oomol.executeRaw(actionId, input, options?)  // → { data, executionId, actionId, message }
oomol.<service>.<action>(input, options?)    // execute 的 namespace 語法糖
oomol.using(scope)                           // → 不可變的作用域子客戶端
oomol.proxy(service, { endpoint, method, query?, headers?, body? }, options?) // → { status, headers, data }
oomol.catalog.action(actionId, options?)     // → ActionMetadata
oomol.catalog.actions(service, options?)     // → ActionMetadata[]
oomol.catalog.providers(query?, options?)    // → ProviderMetadata[]   query: { service?: string[]; q?: string }
oomol.apps.list(options?)                    // → ConnectedApp[]

ProjectConnector(專案 oo_proj_… key)

new ProjectConnector(config: ProjectConnectorConfig)

project.connect.oauth(externalUserId, input, options?)            // → ConnectionRequest(待處理)
project.connect.apiKey(externalUserId, input, options?)           // → ConnectedAccount(同步)
project.connect.customCredential(externalUserId, input, options?) // → ConnectedAccount(同步)
project.getConnectionRequest(connectionRequestId, options?)       // → ConnectionRequest
project.waitForConnection(requestOrId, options?)                  // → ConnectionRequest   options: { pollIntervalMs?, maxWaitMs?, signal?, timeoutMs? }
project.getUserProfile(connectedAccountId, options?)              // → ConnectedAccountProfile
project.execute(externalUserId, actionId, input, options?)        // → action 輸出
project.executeRaw(externalUserId, actionId, input, options?)     // → { data, executionId, actionId, message }
project.forUser(externalUserId)                                   // → ProjectUser(方法相同,id 已繫結)

connect.* 的入參是 { service | providerConfigId } & { connectionName?, … }service / providerConfigId 恰好取一個)。execute 的選項額外增加 { providerConfigId?, service?, connectedAccountId?, connectionName? }

OpenConnector(自託管執行時,選用 oct_… token)

new OpenConnector(config?: OpenConnectorConfig)   // 每個欄位都可選;baseUrl 預設 http://localhost:3000

open.execute(actionId, input, options?)       // → action 輸出
open.executeRaw(actionId, input, options?)    // → { data, executionId, actionId, message }
open.<service>.<action>(input, options?)      // execute 的 namespace 語法糖
open.health(options?)                         // → { ok, runtime }
open.proxy(service, { endpoint, method, query?, headers?, body? }, options?) // → { status, headers, data }(endpoint 必須是相對路徑)
open.catalog.action(actionId, options?)       // → OpenActionMetadata
open.catalog.actions(service, options?)       // → OpenActionMetadata[]
open.catalog.services(options?)               // → string[](有 action 的 service id)
open.catalog.providers(query?, options?)      // → ProviderMetadata[]
open.catalog.search(q, query?, options?)      // → OpenActionSearchResult[]   query: { service?, limit? }
open.apps.list(options?)                      // → ConnectedApp[]
open.apps.listByService(service, options?)    // → ConnectedApp[]
open.apps.authenticated(services, options?)   // → string[](存有真實憑證的 service)

options{ connectionName?, signal?, timeoutMs?, retries? }config 支援 { baseUrl?, runtimeToken?, connectionName?, timeoutMs?, maxRetries?, fetch? }

匯出項

import {
  Connector,
  ProjectConnector,
  OpenConnector,
  ConnectorError,
  isRetryable,
} from "@oomol-lab/connector";
import type {
  ClientConfig, CallOptions, ScopeOptions, RawResult,
  ProxyRequest, ProxyResponse, ProxyMethod,
  CatalogApi, ActionMetadata, ProviderMetadata, ProviderQuery,
  AppsApi, ConnectedApp,
  ConnectorErrorCode,
  ProjectConnectorConfig, ProjectCallOptions, ProjectExecuteOptions,
  ConnectionRequest, ConnectedAccount, ProviderSelector,
  ConnectedAccountProfile, ProviderUserProfile, ProviderUserKind,
  OAuthConnectInput, ApiKeyConnectInput, CustomCredentialConnectInput,
  OpenConnectorConfig, OpenConnectorApi, OpenCallOptions, OpenExecuteOptions,
  OpenCatalogApi, OpenAppsApi, OpenHealth,
  OpenActionMetadata, OpenActionFollowUp, OpenActionAsyncLifecycle,
  OpenActionSearchResult, OpenSearchQuery,
} from "@oomol-lab/connector";

可執行且經過型別檢查的範例位於儲存庫的 examples/ 目錄。

授權條款

MIT,見 connector-sdk 儲存庫。