mcpField
I
integration-development

integration-development

github.com/membranehq开发599 次安装2 周前更新
代码仓库

关于 integration-development

面向使用 Membrane 构建集成的开发者,介绍从认证、创建连接到调用外部应用操作的开发流程。涵盖单租户与多租户设计、连接管理及多种操作分发方式。

主要功能

  1. 01Membrane 五步集成流程
  2. 02单租户与多租户配置
  3. 03连接创建与生命周期管理
  4. 04按意图发现集成和操作
  5. 05通过 API、代码或操作 ID 分发

适用场景

  1. 01为团队共享账号创建连接
  2. 02让用户连接各自的外部应用账号
  3. 03查找并调用适用的集成操作

MCP 相关

该技能说明 MCP 会话中的认证状态,并列出连接、集成和操作发现等对应的 MCP 工具。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/membranehq/agent-skills --skill integration-development

安装后在 Claude Code 中输入/integration-development确认已加载。

详细介绍

集成开发

集成如何运作——五步心智模型

每项由 Membrane 提供支持的功能都会沿着同一条线性主线推进。 步骤 0–3 是核心路径;步骤 4 则是能力升级,当你的应用不再绑定到某个特定供应商时,就可以进阶到这一步。

Step 0: Authenticate yourself   — you, the developer; via MCP or `membrane` CLI
Step 1: Authenticate your code  — sign JWTs in your backend
                                  ↳ single-tenant vs multi-tenant decided HERE
Step 2: Create connections      — `membrane.ui.connect()` in your app UI
Step 3: Use connections         — call actions on the external app
                                  ↳ discover via intent search when you don't know what to call
                                  ↳ dispatch: inline api/code | catalog id | workspace key
Step 4 (advanced): Universal    — app-agnostic code via universal actions + cross-connection logic

在 MCP 会话中,步骤 0 已经完成——membrane status 会对此进行确认。

最重要的选择——你的应用是为一个共享的外部账号(单租户:团队 CRM、内部 Slack)提供服务,还是为许多最终用户提供服务(多租户:每位用户连接自己的 Gmail、Linear 等)——发生在步骤 1。它决定了后端在签发的 JWT 中放入什么信息,而这又会限定后续每个连接和操作的作用范围。步骤 1 之后的所有流程都统一:相同的 membrane.ui.connect() 调用,相同的 /act 分派。

编写代码前——典型的发现流程

四条命令足以覆盖大多数起步场景:

# 1. Populate env with real credentials.
membrane credentials --shell > .env.local

# 2. Find the app you're integrating with (returns externalAppId).
membrane external-app list --search <app-name>

# 3. List catalog actions for that app.
membrane action list --externalAppId <id>

# 4. Check existing connections in the current tenant.
membrane connection list

CLI 快速参考。

  • 签发 JWT:membrane token(按用户区分时添加 --tenantKey <key>;用于 POST /actions 和其他 manager 操作时添加 --manager)。
  • membrane --help 或 membrane <command> --help 会列出完整的命令和选项。

创建连接时,如果 connection list 中没有匹配项,首次连接请传入 connectorKey(目录级别,始终可解析),而不是 integrationKey(工作区本地)。

步骤 0:验证你的身份

确认你拥有有效的 Membrane 会话。

membrane status

显示绿色的“Logged in”以及工作区 → 已就绪。显示红色的“Not logged in” → 进行身份验证:

npx @membranehq/cli login

浏览器窗口会打开以供登录。凭据保存在 ~/.membrane/credentials.json;之后每条 membrane 命令都会复用这些凭据。会话过期后请重新运行。

步骤 1:验证你的代码

后端向 Membrane 发出的每个请求都会携带一个 Membrane 令牌——一个使用工作区密钥签名、有效期较短的 HS256 JWT。 它的 tenantKey 决定了请求所操作的连接隔离范围。

载荷结构
  • workspaceKey——请求所针对的 Membrane 工作区。
  • tenantKey——工作区内隔离的连接范围。不同租户无法看到彼此的连接。

此外还需要工作区密钥(用于签名)和 API URL。

如何选择 tenantKey:

单租户多租户
适用场景一个共享的外部应用账号(团队 CRM、Slack)每位最终用户都有自己的外部应用账号(Gmail、Linear)
JWT 中的 tenantKey固定值——例如 process.env.MEMBRANE_TENANT_KEY已登录用户的稳定 ID
连接密钥形式固定值——例如 hubspot-prod根据用户 ID 生成——例如 gmail-${userId}
连接体验(步骤 2)管理员页面;每个应用只需点击一次每位用户各自拥有一个按钮;每位用户连接自己的账号
获取这些值
membrane credentials --json
# { "apiUri": "...", "workspaceKey": "...", "workspaceSecret": "...", "tenantKey": "..." }

为 .env 文件生成 shell 导出格式:membrane credentials --shell。

签发令牌

使用标准的 HS256 JWT,每次请求都进行签名,并设置较短的 TTL。

import jwt from 'jsonwebtoken'

// tenantKey source:
//   single-tenant  → process.env.MEMBRANE_TENANT_KEY
//   multi-tenant   → the signed-in user's stable id
const token = jwt.sign(
  { workspaceKey: process.env.MEMBRANE_WORKSPACE_KEY, tenantKey },
  process.env.MEMBRANE_WORKSPACE_SECRET,
  { algorithm: 'HS256', expiresIn: '5m' },
)
// Use as Authorization: Bearer <token>, or hand to
// <MembraneProvider fetchToken={…}> on the frontend.

对于 shell 测试,membrane token 会签发一个限定租户范围的令牌(按用户区分时添加 --tenantKey <key>)。get-membrane-token 是对应的 MCP 方式。

工作区管理器令牌

租户范围的令牌涵盖 act、列表查询以及应用运行时执行的所有操作。以下操作需要管理器令牌(引擎 workspaceAsManager 身份验证层的作用范围):

  • POST /actions / PATCH /actions/<id> / DELETE /actions/<id>(工作区级可复用操作)
  • PATCH /integrations/<id>/parameters(OAuth 凭据、范围)
  • POST /import / GET /export(工作区配置往返导入导出)

使用 membrane token --manager 创建,或直接签名:

const managerToken = jwt.sign({ workspaceKey, isAdmin: true }, workspaceSecret, { algorithm: 'HS256', expiresIn: '5m' })

步骤 2:创建连接

连接是与一个外部应用建立的已验证身份链接。每次 /act 调用都会通过某个连接进行身份验证。

前端的 membrane.ui.connect(…) 是面向用户的连接流程——它会打开 OAuth 弹出窗口、跟踪状态,并在用户完成操作后返回结果。

连接生命周期
操作CLIMCP 工具
列出现有连接membrane connection listlist-connections
发现应用membrane integration list --search "<q>"list-integrations
创建membrane.ui.connect(…)(生产环境)/ membrane connect …(开发环境)connect
重新连接membrane connect --connectionId <id>使用 { connectionId } 调用 connect
删除membrane connection delete <id-or-key>delete-connection
发现可连接的目标

list-integrations 会搜索工作区的集成,以及目录中的连接器和外部应用。每个结果都包含 connectorId、integrationId、externalAppId——将其中任意一个传给 connect。如果没有匹配项,connect 也接受自由文本 intent,并会即时构建连接器——首次以此方式连接可能需要更长时间(预期会进入 BUILDING 阶段;如果失败,请检查 CONFIGURATION_ERROR / SETUP_FAILED)。

创建连接

向 connect 提供以下其中一项:

  • connectorKey / connectorId — 目录连接器(即使全新工作区中还没有集成,也可以使用)。
  • integrationKey / integrationId — 已知集成。
  • intent — 自由文本;服务器会选择或构建连接器。

可选:提供自己的 connectionKey,以便稳定查找;否则系统会自动生成。

connect 会返回连接(id、key、state,以及在用户仍在进行身份验证时返回的 clientAction.uiUrl)。CLI 会阻塞,直到 state: READY;在 MCP 中,连接面板会呈现此流程。

重新连接

使用现有的 connectionId 调用 connect,即可重新授权 OAuth,而无需创建新连接。当 act 调用返回连接已断开的错误时,请使用此方法。

删除(归档)

delete-connection / membrane connection delete 会归档记录。该记录将不再显示在 list-connections 中,也无法再用于 act。

自定义集成

默认情况下,集成使用 Membrane 共享的 OAuth 客户端。自定义集成可使用自己的 OAuth 凭据、调整连接表单字段,或更改每个集成的行为。详情请参阅 integration-catalog。

连接 UI — 单租户

一个共享账户,一个管理员连接页面。连接键是固定的。

import { MembraneClient } from '@membranehq/sdk'
// The backend mints a token (step 1) and exposes it at /api/membrane-token
// as { token, apiUri }.
const { token, apiUri } = await (await fetch('/api/membrane-token')).json()
const m = new MembraneClient({ token, apiUri })
await m.ui.connect({ connectorKey: 'hubspot', connectionKey: 'hubspot-prod' })
Connect UI — 多租户

每位已登录用户连接自己的账户。连接密钥根据用户 ID 生成。

使用 @membranehq/react — 用 MembraneProvider 包装一次应用:

import { MembraneProvider, useMembrane } from '@membranehq/react'
;<MembraneProvider
  apiUri={process.env.NEXT_PUBLIC_MEMBRANE_API_URI}
  fetchToken={async () => (await (await fetch('/api/membrane-token')).json()).token}
>
  {children}
</MembraneProvider>

// In any component:
const m = useMembrane()
m.ui.connect({ connectorKey: 'gmail', connectionKey: `gmail-${userId}` })

纯 JS — 使用相同模式和用户级连接密钥:

await m.ui.connect({ connectorKey: 'gmail', connectionKey: `gmail-${userId}` })

关于令牌路由的说明:

  • 任何提供 GET /api/membrane-token 并返回 { token, apiUri } 的服务端框架都适用。fetchToken 会按请求调用,因此 令牌的有效期较短。
  • 仅用于 Vite 开发环境的应用:添加一个开发中间件,用于签署与步骤 1 相同的 JWT。loadEnv(mode, …) 可让中间件访问 MEMBRANE_* 环境变量。
  • 必须从浏览器调用 m.ui.connect(...) — 它会打开 OAuth 弹窗。服务端重定向或 CLI 命令无法替代。
仪表板目录页面

类似设置的 UI,供用户管理多种集成。使用 @membranehq/react 钩子:

useIntegrations() // → { items: Integration[], loading, loadMore, loadingMore, refresh }
useConnections() //  → { items: Connection[], loading, refresh }
useMembrane() //     → MembraneClient (.ui.connect(…), .connection(id).archive(), …)

type Connection = {
  id: string
  name: string
  integrationId: string
  integration?: { key: string; name: string; logoUri?: string }
  disconnected: boolean
  state: 'READY' | 'BUILDING' | 'DISCONNECTED' | 'ERROR'
}

type Integration = {
  id: string
  key: string
  name: string
  logoUri?: string
}

需要接入的四个独特 Membrane 调用:

// Connect a new integration the user picked from the available list:
m.ui.connect({ integrationKey: i.key })

// Reconnect a disconnected/errored connection:
m.ui.connect({ connectionId: c.id })

// Disconnect (archive):
m.connection(c.id).archive()

state 枚举对 UI 很重要:BUILDING 表示 用户完成身份验证后,Membrane 完成连接器配置前的阶段(首次连接时可能需要 几秒钟)。如果 UI 只显示已连接/未连接,BUILDING 或 ERROR 会被显示为“已连接”——但事实并非如此。

第 3 步:使用连接 — 调用操作

/act 是执行操作的统一端点。提供四种分发方式, 每次调用只能使用其中一种:

  • api — 通过连接的身份验证层和基础 URL 执行内联 HTTP 请求。 这是默认的基础构建模块。
  • code — 在沙箱中运行 JS 代码片段,其中已预先配置经过身份验证的 membrane 客户端、connection 和 integration。适用于多步骤组合操作。
  • id — 指定某个操作(目录操作或工作区本地操作)。通过 action list --externalAppId 获取的目录操作只有 id( key 字段为空)。
  • key — 工作区本地操作的稳定句柄。目录操作没有 key;工作区本地操作同时具有两者,任选其一即可。

api 和 code 需要连接(connectionKey 或 connectionId)。key/id 会根据操作自身的作用范围进行路由。

运行操作

使用 membrane act,并指定 --api、--code、--key、--id 之一,以及连接和输入标志。

响应:{ output, actionRunId }。失败时响应中仍会包含 actionRunId — 将其传给运行日志以便调试。

按意图发现操作

知道要做什么,但不知道如何实现时,可按意图排序列出操作。

membrane action list --connectionKey <key> [--intent "<text>"]

分发示例 — 按 id 调用目录操作

典型流程:发现工作流的第 3 步会提供一个目录操作列表,其中包含 id、name、inputSchema、config.request。 按 id 使用匹配项 — Membrane 会处理特定供应商的请求格式、身份验证范围和重试。只有在没有匹配的目录操作时,才回退到内联 api:。

// /act body:
{
  id: GMAIL_SEND_MESSAGE_ACTION_ID,   // paste from `membrane action list --externalAppId`
  connectionKey: `gmail-${userId}`,   // or `hubspot-prod` for single-tenant
  input: { to, subject, body },       // matches the action's inputSchema
}

// On failure, detect disconnected connection and forward actionRunId:
const needsReconnect = res.status === 401 || payload?.code === 'CONNECTION_DISCONNECTED'
// Return { error: payload, actionRunId: payload?.actionRunId, needsReconnect }

没有匹配目录操作时使用内联 api::

{
  connectionKey,
  api: {
    method: 'POST',
    path: '/<vendor-endpoint>',
    body: { … },
  },
}
升级为可复用的工作区本地操作

当相同的分发形式在多个路由中反复出现时,将其提取为具有稳定 key 的工作区本地操作。使用管理令牌调用一次 POST /actions。

有两种创建方式:

  • 按意图 — Membrane Agent 构建并注册操作。
  • 按明确规范 — 由你提供 type + config。
# by intent
membrane action create --intent "<text>" --connectionKey <key>

# by explicit spec
membrane action create --key <key> --type <type> --config '<json>' --integrationKey <key>

# update (merge) / replace (whole) / delete
membrane action update <identifier> --data '<json>'
membrane action replace <identifier> --data '<json>'
membrane action delete <identifier>

POST /actions 请求体格式(明确规范):

{
  "key": "create-lead",
  "name": "Create Lead",
  "type": "api-request-to-external-app",
  "externalAppId": "<app-id>",
  "config": {
    "request": { "method": "POST", "path": "/<vendor-endpoint>" }
  },
  "inputSchema": { "type": "object", "required": ["…"], "properties": { "…": "…" } }
}

返回 { id, key, state: 'READY', inputSchema, outputSchema, … }。 运行时使用 { key: '<your-key>', connectionKey, input } 调用。

操作的作用范围取决于你设置的字段:

  • 设置 connectionKey/connectionId → 连接级(一个连接)。
  • 设置 integrationKey/integrationId(且不设置连接) → 集成级(在该集成的每个连接之间共享)。通用操作可以通过这种方式按集成定制 — 使用相同的 key,但字段映射和前置/后置钩子各不相同。
  • 两者都不设置 → 通用操作(见步骤 4)。

POST /actions 需要管理令牌;租户令牌会收到 403。

调试失败的操作

每个 act 响应都带有 actionRunId。将它传给运行日志 (--details / includeDetails: true 也会获取与外部应用之间的原始 HTTP 交互)。

membrane action-run-log get <actionRunId> --details

步骤 4(高级):通用集成

步骤 0–3 假设你的代码知道正在与哪个外部应用交互。 步骤 4 适用于另一种情况:无论用户连接哪个供应商,你的应用都能提供价值——例如跨 Gmail、Outlook、SendGrid 使用的“发送邮件”功能,或以统一方式处理 Salesforce、HubSpot、Pipedrive 的 CRM 同步层。

这是不同的分发路径。只有当供应商无关的行为是产品的一项核心需求时,才使用这种方式。

通用操作

通用操作的注册不包含 externalAppId 或 integrationKey。它会命名一个意图(“创建联系人”“发送 消息”“列出发票”),并提供由每个实现该操作的集成都共享的输入/输出架构。分发会根据调用方连接所属的集成进行路由。

# Create by intent — Membrane builds a shared schema at workspace scope.
membrane action create --intent "send an email" --key send-email

分发——不指定 externalAppId;操作会根据连接进行适配:

await act({
  key: 'send-email',
  connectionKey: userPickedConnection, // Gmail, Outlook, SendGrid, etc.
  input: { to, subject, body },
})

供应商专属调整(不同的字段名称、某个供应商专用的额外钩子)可在集成层覆盖:使用相同的 key 创建第二个操作,并设置 integrationKey: "gmail"。使用 Gmail 连接时,运行时分发会自动采用该覆盖配置。

跨连接逻辑

Membrane 不会直接对多连接操作建模。当任务涉及多个连接时:

  • 从你的后端编排:按顺序调用 /act,每次使用各自的连接。
  • code: 分发:使用预先配置一个连接的简短 JS 代码片段;在片段中通过 membrane 客户端访问第二个连接。
通用流程的工作区设置

使用管理器令牌通过 PATCH /workspaces/:id 切换:

  • useMembraneUniverse — 操作发现范围涵盖目录中所有集成的通用操作,而不只限于工作区集成。
  • autoGenerateIntegrationsFromConnectors — 使用新连接器时按需创建集成。通用 UX 需要此设置:用户可以选择任意连接器,并确保存在可供通用操作分发使用的集成。
  • autoGenerateIntegrationsFromExternalApps — 按需从外部应用目录生成集成。

这些设置默认关闭,因为它们会扩大应用用户可见的内容范围。

何时应继续使用步骤 3
  • 供应商专属标头或边缘情况 → 使用步骤 3 目录中的 id 或内联 api:。
  • 重复调用同一个应用 → 按 key 升级为工作区本地的可复用操作(步骤 3)。
  • 赶进度的单一供应商产品 → 通用操作会增加额外的设计工作;之后再考虑。

错误恢复

当 /act 返回连接已断开的错误时,将 connectionId 显示给 UI(或发送到你的运维频道),并让用户重新连接。不要创建新连接——现有连接只需重新授权 OAuth。

if (err.status === 401 || err.payload?.code === 'CONNECTION_DISCONNECTED') {
  // Interactive path: return 409 with the connectionId; frontend
  // calls m.ui.connect({ connectionId }) and retries after the user
  // completes the popup.
  //
  // Unattended workload (cron, worker, sync): log the connectionId
  // and actionRunId, emit an alert, and bail. The next run picks up
  // once an admin has reconnected via the Settings page.
}

超出已公开工具的范围——原始 API

Membrane REST API 支持的任何功能,都可以在代码中通过 get-membrane-token 生成令牌并直接调用端点来使用。以下是一些未由一等工具覆盖的重要端点:

  • 恢复已归档的操作 / 连接 — POST /actions/:id/restore、 POST /connections/:id/restore。
  • 整体替换操作 — PUT /actions/:id(MCP update-action 只会合并)。
  • 修改连接 — PATCH /connections/:id。
  • 刷新连接凭据 — POST /connections/:id/refresh。
  • 检查 / 自定义集成 — GET /integrations/:id、 PATCH /integrations/:id。参见 integration-catalog。

完整 API 参考: docs.getmembrane.com/reference。 调用端点前,使用 search-docs 查找具体端点。


前端设置

了解如何在浏览器代码中初始化 Membrane — React 应用、其他浏览器框架、嵌入式 UI。凡是会渲染连接对话框或重新连接流程的内容,都属于此处,因为它需要在真实浏览器中运行 OAuth。

**黄金法则:**浏览器永远不会接触你的工作区密钥。在服务器上签发令牌,再通过 fetchToken 这类回调将令牌传给前端。请参阅 backend-setup,了解用于签发令牌的服务器路由。

Next.js / React

安装 @membranehq/react。使用 MembraneProvider 包裹你的应用,并提供一个调用后端的 fetchToken 函数:

// app/layout.tsx
import { MembraneProvider } from '@membranehq/react'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <MembraneProvider
          fetchToken={async () => {
            const res = await fetch('/api/membrane-token')
            return (await res.json()).token
          }}
        >
          {children}
        </MembraneProvider>
      </body>
    </html>
  )
}

然后,在 Provider 树中的任意位置:

import { useMembrane } from '@membranehq/react'

function ConnectButton() {
  const membrane = useMembrane()
  return <button onClick={() => membrane.ui.connect({ integrationKey: 'hubspot' })}>Connect HubSpot</button>
}

**Next.js 注意事项:**在 next.config.ts 中添加 transpilePackages: ['@membranehq/react', '@membranehq/sdk']。

非 React 浏览器代码

直接使用 @membranehq/sdk。令牌同样从后端获取 — 将 fetchToken(或静态 token)传给 MembraneClient:

import { MembraneClient } from '@membranehq/sdk'

const membrane = new MembraneClient({
  fetchToken: async () => {
    const res = await fetch('/api/membrane-token')
    return (await res.json()).token
  },
})

await membrane.ui.connect({ integrationKey: 'hubspot' })

Vue、Svelte、Angular、纯 JavaScript — 用法相同。提供 fetchToken 后,SDK 会自动刷新令牌。

何时调用前端 SDK

  • 需要用户交互的 UI 流程 — membrane.ui.connect(...)、重新连接对话框、连接管理界面。这些必须在浏览器中运行。
  • 与已登录用户关联的只读读取操作 — 列出某个租户自己的连接、读取其自己的操作结果等,都可以从前端执行,因为令牌的作用域已经限定在该租户。
  • 其他所有操作 — 代表应用执行写入、后台同步、工作区范围的管理操作 — 应在后端执行。请参阅 backend-setup。

另请参阅


后端设置

了解如何在服务器代码中初始化 Membrane — 后端路由、后台工作进程、定时任务、CLI 和一次性脚本。适用于可以安全存放工作区密钥,且不运行面向用户的 UI 流程的任何场景。

这里也需要实现 frontend-setup 中提到的 /api/membrane-token 路由:前端向后端请求租户范围的 JWT,后端签发令牌并返回。

**黄金法则:**工作区密钥必须保留在服务器上。不要将其嵌入浏览器包、移动应用、用户运行的桌面应用,或任何用户可以检查二进制文件的地方。

两种标准流程

  1. 基于凭据的客户端 — 将 workspaceKey、workspaceSecret 和 tenantKey 传给 MembraneClient;它会为你签发并刷新令牌。最适合一次处理一个租户的长时间运行代码(工作进程、脚本、CLI、单租户后端)。
  2. 基于令牌的客户端 / 原始 HTTP — 自行签发 JWT,然后将其传给 MembraneClient({ token }),或在原始 fetch 请求中通过 Authorization: Bearer <token> 标头传递。最适合需要显式控制的场景 — 多租户请求处理程序、/api/membrane-token 路由、非 JavaScript 服务。

两种流程都使用同一个 API。根据你对令牌生命周期的控制需求进行选择。

使用工作区凭据创建 MembraneClient

可直接用于脚本、工作进程和单租户后端。

import { MembraneClient } from '@membranehq/sdk'

const membrane = new MembraneClient({
  workspaceKey: process.env.MEMBRANE_WORKSPACE_KEY!,
  workspaceSecret: process.env.MEMBRANE_WORKSPACE_SECRET!,
  tenantKey: process.env.MEMBRANE_TENANT_KEY!, // or the end-user id
  apiUri: process.env.MEMBRANE_API_URL,
})

const connections = await membrane.connections.find()

SDK 会为每次调用签发 HS256 令牌,并在令牌过期前刷新它。在多租户后端中,可按请求更换 tenantKey(通过构造限定作用域的客户端)。

签发令牌并直接调用 API

需要精细控制、运行在非 JavaScript 环境,或正在为前端编写 /api/membrane-token 路由时,请使用此模式。

import jwt from 'jsonwebtoken'

function membraneTokenForTenant(tenantKey: string) {
  return jwt.sign(
    {
      workspaceKey: process.env.MEMBRANE_WORKSPACE_KEY!,
      tenantKey,
    },
    process.env.MEMBRANE_WORKSPACE_SECRET!,
    { algorithm: 'HS256', expiresIn: 3600 },
  )
}

// Example: Next.js API route that vends tokens to the browser
export async function GET(req: Request) {
  const tenantKey = await resolveTenantForUser(req) // your auth system
  return Response.json({ token: membraneTokenForTenant(tenantKey) })
}

// Or: call Membrane directly from the server
async function listConnections(tenantKey: string) {
  const token = membraneTokenForTenant(tenantKey)
  const res = await fetch(`${process.env.MEMBRANE_API_URL}/connections`, {
    headers: { Authorization: `Bearer ${token}` },
  })
  return res.json()
}

如果你不在 JavaScript 环境中,get-membrane-token 工具提供了完整的载荷参考,以及 Python、Go、Ruby、Java 和 PHP 中的签名示例。

脚本和 CLI

与后端路由采用相同模式:从本地环境文件(或 get-credentials)读取工作区凭据,签发令牌,然后调用 API。单租户脚本可以将与你的控制台会话关联的租户密钥写死 — 这就是 get-credentials 默认返回的租户。

// scripts/sync-hubspot.ts
import { MembraneClient } from '@membranehq/sdk'

const membrane = new MembraneClient({
  workspaceKey: process.env.MEMBRANE_WORKSPACE_KEY!,
  workspaceSecret: process.env.MEMBRANE_WORKSPACE_SECRET!,
  tenantKey: process.env.MEMBRANE_TENANT_KEY!,
})

const contacts = await membrane.connection('hubspot-prod').action('list-contacts').run({})

另请参阅

  • 身份验证
  • 租户
  • get-credentials 工具 — 返回当前环境的 workspaceKey、 workspaceSecret、tenantKey 和 apiUrl。
  • get-membrane-token 工具 — 返回一个已签名、可直接使用的 JWT。

集成目录

了解如何设置和配置工作区的集成目录——即产品(或脚本)在运行时通过 integrationKey 引用的集合集成。配置工作在构建时或管理时完成,而非运行时。

运行时 UI 模式

如果要在产品中构建集成页面,标准的运行时流程如下:

  1. 获取当前租户的 connections
  2. 获取可用的 integrations
  3. 将已连接的账户与用户仍可连接的应用分开展示
  4. 使用 membrane.ui.connect(...) 启动连接或重新连接
  5. 归档连接以断开连接
import { useConnections, useIntegrations, useMembrane } from '@membranehq/react'

function IntegrationsPage() {
  const membrane = useMembrane()
  const { items: integrations = [] } = useIntegrations()
  const { items: connections = [] } = useConnections()

  const connectedIntegrationIds = new Set(connections.map((c) => c.integrationId))
  const availableIntegrations = integrations.filter((i) => !connectedIntegrationIds.has(i.id))

  return (
    <>
      {connections.map((connection) => (
        <div key={connection.id}>
          <span>{connection.name}</span>
          {connection.disconnected ? (
            <button onClick={() => membrane.ui.connect({ connectionId: connection.id })}>Reconnect</button>
          ) : (
            <button onClick={() => membrane.connection(connection.id).archive()}>Disconnect</button>
          )}
        </div>
      ))}

      {availableIntegrations.map((integration) => (
        <button key={integration.id} onClick={() => membrane.ui.connect({ integrationKey: integration.key })}>
          Connect {integration.name}
        </button>
      ))}
    </>
  )
}

对于非 React 前端应用,遵循相同的流程:从后端获取临时令牌,在 UI 中列出集成和连接,并从前端代码触发相同的连接/重新连接流程。对于该 UI 背后的后端路由、工作进程或 cron 作业,请使用原始 API 调用和短期 Membrane 令牌,而不要使用服务端 SDK。

只有后端脚手架?添加一个最简前端。

如果脚手架是一个没有 UI 的纯 API Express / Fastify / Next 路由处理程序,你仍需要一个用于连接流程的前端界面。 从后端提供一个静态 HTML 页面;该页面会从应用获取每位用户的令牌,并通过浏览器 SDK 调用 membrane.ui.connect:


<!doctype html>
<html>
  <body>
    <button id="connect-github">Connect GitHub</button>
    <script type="module">
      import { MembraneClient } from 'https://esm.sh/@membranehq/sdk'
      const { token } = await (await fetch('/api/membrane-token')).json()
      const m = new MembraneClient({ token, apiUri: 'https://api.getmembrane.com' })
      document.getElementById('connect-github').onclick = () =>
        m.ui.connect({ connectorKey: 'github', connectionKey: 'github-dev' })
    </script>
  </body>
</html>

/api/membrane-token 路由是一个轻量的后端端点,用于签发 JWT(参见 kit 中的身份验证)并返回 { token }。 这是唯一正确的终端用户连接模式——不要向终端用户提供 CLI 操作说明,也不要让后端返回 clientAction.uiUrl,再让用户手动粘贴。

React Hook/客户端速查

以下内容均来自 @membranehq/react:

// client — has all the methods you call imperatively
const m = useMembrane()
m.ui.connect({ connectorKey })            // new connection (catalog-level)
m.ui.connect({ integrationKey })          // new connection (workspace-level)
m.ui.connect({ connectionId })            // reconnect an existing connection
m.connection(id).archive()                // disconnect

// hooks — SWR-style; re-render on changes
const { items: integrations, loading, error } = useIntegrations({ search?: string })
const { items: connections, loading, error } = useConnections({ integrationKey?: string })

// shape of a Connection
type Connection = {
  id: string
  key: string
  name: string
  integrationId: string
  disconnected: boolean
  state: 'READY' | 'DISCONNECTED' | 'ERROR' | 'BUILDING' | ...
}

// shape of an Integration
type Integration = {
  id: string
  key: string
  name: string
  logoUri?: string
}

操作的适用场景

应用连接后,产品功能通常只需使用操作,无需更复杂的机制:

  • 在后端、工作进程或 cron 作业中,直接通过 api 或 code 运行内联 act 调用
  • 使用 get-credentials 或 get-membrane-token 为这些后端调用签发令牌;将 SDK 保留在前端层
  • 只有在需要稳定的键、架构或可在多个调用位置共享的抽象时,才保存可复用操作
  • “同步”通常只需由你自己的作业运行器按计划重复执行操作

如果希望 Membrane 负责更多同步编排、事件处理或可按租户自定义的映射,则可使用数据源、字段映射和流程等更丰富的 Membrane 基元。

从连接器创建集成

每个集成都由一个连接器提供支持(即 Membrane 保存在其目录中的特定供应商适配器)。创建一次集成后,便可始终通过稳定的 integrationKey 引用它。

curl -X POST $API/integrations -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"key":"github","name":"GitHub","connectorKey":"github"}'

生成的集成支持连接(connect --integrationKey github)和可复用操作(create-action --integrationKey github)。

集成参数

参数用于配置集成的行为:OAuth 客户端凭据、默认范围、特定于 API 的设置。参数保存在集成上,可以更新而无需重新创建集成。

# fetch
curl -sG $API/integrations/<id>/parameters -H "Authorization: Bearer $TOKEN"

# set
curl -X PATCH $API/integrations/<id>/parameters -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"clientId":"...","clientSecret":"..."}'

自动生成的集成

设置 connectors=1 / externalApps=1(参见 list-integrations)后,工作区会按需根据目录合成集成——Agent 可以连接尚无专用集成的应用,系统会自动创建一个。只有在需要预先向目录添加自定义名称、参数或连接器版本时,才需要显式调用 POST /integrations。

首次连接可能比普通连接流程耗时更久:Membrane 可能会搜索目录、创建集成记录,或启动 Agent 来构建新的连接器。请预期会进入初始 BUILDING 阶段。如果构建/设置流程失败,通常会看到 CONFIGURATION_ERROR 或 SETUP_FAILED;请完善意图或检查具体失败原因后再重试。

归档和恢复

可以归档集成,而不是删除——默认情况下,DELETE /integrations/<id> 会将其归档。除非设置 includeArchived=1,否则归档的集成会从 list-integrations 中隐藏。使用 POST /integrations/<id>/restore 恢复。

另请参阅


故障排查

在 Membrane 中出现问题时,修复几乎总是需要同时查看两个资源——失败的 act 调用,以及它所经过的连接。本节介绍跨资源的工作流;各工具文档则介绍具体操作(如何重新连接、日志结构是什么样、每种状态代表什么)。

诊断流程

当操作调用失败时,请按照以下流程排查。每一步都为下一步提供依据:

  1. 阅读响应。 每个 act 响应都包含一个 actionRunId——无论成功还是返回 4xx 都有。请记下它。
  2. 获取运行日志。 调用 get-action-run-log 并设置 includeDetails: true。这是查看 Membrane 映射后的输入、输出、错误以及——最重要的——与外部应用之间原始 HTTP 交互的最快方式。无需查看其他内容,大多数故障都能在此找到答案。
  3. 判断日志所示的问题类型。 根据情况选择对应的修复方式:
    • 身份验证/连接断开 → 重新连接。
    • 输入格式错误/缺少字段 → 修正 act 输入后重试(内联 api / code 是最小化的测试方式)。
    • 外部应用返回错误 → 查看日志中的响应正文;修正输入或更换端点。
    • 缺少连接器配置 → 参见 integration-catalog。
  4. 使用尽可能简单的调用重试。 先将失败的同步或可复用操作简化为单个内联 act(api 或 code)。确认可用后,再逐步恢复原有方式。

日志层级——从上往下排查

Membrane 会记录三层证据;请按顺序查看:

  1. 操作运行日志——每次 act 调用对应一条记录。包含 Membrane 对此次调用的记录:输入、输出、错误、状态变更,以及(设置 includeDetails: true 时的)原始 HTTP 交互。从这里开始。
  2. 外部 API 日志——发送给供应商的原始请求/响应,可通过 includeDetails 查看。当操作运行日志显示“外部错误”且你需要查看供应商的确切响应正文/状态码时使用。
  3. Webhook 日志——来自供应商的原始传入负载。仅在调试事件驱动流程时使用;与同步 act 调用无关。

你很少需要深入到第 2 层以下。

症状 → 工具映射

症状首选操作
act 返回错误并包含 actionRunId使用 includeDetails: true 调用 get-action-run-log
身份验证错误 / “连接已断开”使用出错的 connectionId 调用 connect
连接长时间处于 BUILDING 状态等待,然后调用 list-connections 查看最终状态
连接进入 CONFIGURATION_ERROR 状态重新运行 connect;检查返回的错误
连接进入 SETUP_FAILED 状态无法构建连接器;尝试使用其他 intent
“未找到集成” / “没有匹配的操作”重试前调用 list-integrations 或 list-actions
可复用操作产生了错误的输出先使用 act + api / code 内联重建
问题似乎影响整个平台(多个租户、webhook)使用 search-docs 搜索受影响的功能

另请参阅

工具

获取凭据
membrane credentials

获取从任何后端与 Membrane 通信所需的凭据。 用于后端路由、工作进程、cron 作业或服务器端脚本。 前端代码应从后端获取临时令牌,然后 使用前端 SDK。

返回内容:

  • 工作区密钥 — 指定要访问哪个 Membrane 工作区。
  • 工作区密钥 — 用于签署 JWT。仅保留在服务器端。
  • 租户密钥 — 将调用限定到某个用户的一组连接。 测试时使用控制台会话租户;在生产环境中,替换为 当前最终用户的唯一标识符。
  • API URL — 当前环境的 Membrane 端点。

如果之后遇到 401 错误,工作区密钥可能已轮换 — 再次调用此工具以获取新的密钥。

在代码中

使用返回的值生成 JWT 并验证 API 调用:

# 1. Mint a token (HS256, 1-hour expiry)
TOKEN=$(node -e "
  const jwt = require('jsonwebtoken');
  console.log(jwt.sign(
    {
      workspaceKey: '<workspaceKey>',
      tenantKey: '<tenantKey>',
      name: 'Acme Inc'
    },
    '<workspaceSecret>',
    { algorithm: 'HS256', expiresIn: 3600 }
  ))
")

# 2. Use it
curl -sG $API/connections -H "Authorization: Bearer $TOKEN"

任何带有 JWT 库的语言都适用 — 负载为 { "workspaceKey": "<workspaceKey>", "tenantKey": "<tenantKey>" } 以及可选的显示元数据(如 name),并使用工作区密钥签名。切勿在浏览器/客户端代码中公开此密钥。

获取 Membrane 令牌
membrane token [--tenantKey <key>] [--manager] [--expiresIn <duration>] [--json]

生成 Membrane 令牌——一种短期有效的 JWT,用于对 Membrane API 请求进行身份验证。需要在后端代码的 Authorization: Bearer <token> 标头中使用令牌时,调用此工具。前端代码应向后端请求一个临时令牌,然后使用前端 SDK。

输入

  • tenantKey(可选)——标识令牌可访问的连接所属的隔离范围。默认为与你当前控制台会话关联的租户。
  • manager(可选,仅 CLI)——签发工作区管理令牌,用于 POST /actions、集成参数编辑和导入/导出等工作区级变更。对应引擎的 workspaceAsManager 身份验证层。设置 manager 后,租户密钥为可选项。(为兼容引擎现有的身份验证解析逻辑,载荷中会包含 isAdmin: true。)
  • expiresIn(可选,仅 CLI)——令牌有效期,例如 5m、1h、24h。默认值:1h。

响应

一个 JWT 字符串。在每次 API 调用中,将其作为 Authorization: Bearer <token> 传递。

令牌载荷

字段必填描述
workspaceKey是你的工作区密钥。
tenantKey否租户(用户、团队或组织)的标识符。工作区级操作可省略。
name否易于阅读的租户名称(有助于排查问题)。
fields否要存储的租户附加元数据。
isAdmin否对于工作区管理令牌(不含 tenantKey),设为 true。

代码示例

使用任何语言和 JWT 库,只需 3 到 5 行代码。务必在服务器端签名——任何拥有工作区密钥的人都可以为任意租户签发令牌。

import jwt from 'jsonwebtoken'

const token = jwt.sign(
  {
    workspaceKey: '<WORKSPACE_KEY>',
    tenantKey: '<TENANT_ID>',
    name: '<TENANT_NAME>',
  },
  '<WORKSPACE_SECRET>',
  { expiresIn: 7200, algorithm: 'HS256' },
)
import datetime, jwt

token = jwt.encode(
    {
        "workspaceKey": "<WORKSPACE_KEY>",
        "tenantKey": "<TENANT_ID>",
        "name": "<TENANT_NAME>",
        "exp": datetime.datetime.now() + datetime.timedelta(seconds=7200),
    },
    "<WORKSPACE_SECRET>",
    algorithm="HS256",
)
import (
    "time"
    "github.com/golang-jwt/jwt/v5"
)

claims := jwt.MapClaims{
    "workspaceKey": "<WORKSPACE_KEY>",
    "tenantKey":    "<TENANT_ID>",
    "name":         "<TENANT_NAME>",
    "exp":          time.Now().Add(time.Hour * 2).Unix(),
}
token, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).
    SignedString([]byte("<WORKSPACE_SECRET>"))
require 'jwt'

payload = {
  workspaceKey: '<WORKSPACE_KEY>',
  tenantKey: '<TENANT_ID>',
  name: '<TENANT_NAME>',
  exp: Time.now.to_i + 7200,
}
token = JWT.encode(payload, '<WORKSPACE_SECRET>', 'HS256')
String token = Jwts.builder()
    .claim("workspaceKey", "<WORKSPACE_KEY>")
    .claim("tenantKey", "<TENANT_ID>")
    .claim("name", "<TENANT_NAME>")
    .setExpiration(Date.from(Instant.now().plusSeconds(7200)))
    .signWith(
        new SecretKeySpec("<WORKSPACE_SECRET>".getBytes(), "HmacSHA256"),
        SignatureAlgorithm.HS256)
    .compact();
$token = JWT::encode([
    'workspaceKey' => '<WORKSPACE_KEY>',
    'tenantKey'    => '<TENANT_ID>',
    'name'         => '<TENANT_NAME>',
    'exp'          => time() + 7200,
], '<WORKSPACE_SECRET>', 'HS256');
# Shell: sign via Node for a one-off curl call
TOKEN=$(node -e "
  const jwt = require('jsonwebtoken');
  console.log(jwt.sign(
    { workspaceKey: '<WORKSPACE_KEY>', tenantKey: '<TENANT_ID>' },
    '<WORKSPACE_SECRET>',
    { algorithm: 'HS256', expiresIn: 3600 }
  ));
")
curl -sG $API/connections -H "Authorization: Bearer $TOKEN"

获取令牌的其他方式

  • 公钥/私钥签名——如需更严格的密钥管理,可使用工作区所有的私钥,通过任意非对称算法(ES256、RS256、PS256,……)签名,并在工作区设置中注册公钥。可直接替换上述示例。
  • 工作区管理令牌——对于跨租户操作(如管理租户、工作区设置和导入/导出),添加 isAdmin: true(并省略 tenantKey)。
  • 测试令牌——只想快速试用 API 时,可从控制台设置中的「访问」页面获取预先生成的令牌。
  • 长期有效令牌——需要一个不会过期的令牌时(例如,无法在运行期间刷新令牌的服务器到服务器任务),可从「访问」页面的「客户端令牌」部分创建。
  • 开发者令牌——代表你的 Membrane 帐户在所有工作区中执行操作。在控制台设置中的 API 令牌处签发。

完整参考请参阅身份验证文档。

列出连接
membrane connection list [--integrationKey <key>] [--search "<text>"] [--limit <n>] [--cursor <cursor>]

列出当前租户的活动连接。在要求用户重新进行身份验证之前,可用它检查是否已存在连接。

筛选条件

  • integrationKey / integrationId——缩小范围,仅列出某个集成。
  • search——按名称 / 密钥进行全文匹配。
  • limit、cursor——标准分页参数。

响应

每条记录都包含 id、key、integrationId、连接名称和连接状态等标识信息。将 connectionId 或 connectionKey 传给 act,即可在该连接上运行操作。

代码示例

# all connections
curl -sG $API/connections -H "Authorization: Bearer $TOKEN"

# filter to one integration
curl -sG $API/connections -H "Authorization: Bearer $TOKEN" \
  --data-urlencode 'integrationKey=github'
删除连接
membrane connection delete <identifier> [--json]

按 ID 或密钥归档连接。归档后,该连接不会再显示在 list-connections 中,也无法再通过 act 使用。这是断开连接的操作——Membrane 默认执行软删除;如需恢复或彻底删除,请使用原始 API。

输入

  • connectionId — 连接的数据库 ID。

使用场景

  • 撤销用户的连接(例如,用户在应用的设置中断开 HubSpot)。
  • 删除租户或清理账户——归档与该租户关联的所有连接。
  • 用新创建的连接替换配置错误的连接。

代码示例

# Shell
curl -s -X DELETE "$API/connections/<id>" -H "Authorization: Bearer $TOKEN"
// SDK
import { MembraneClient } from '@membranehq/sdk'
const membrane = new MembraneClient({ token })
await membrane.connection('<id>').archive()
列出集成
membrane integration list [--search "<text>"] [--connectors 1] [--externalApps 1] [--limit <n>] [--cursor <cursor>]

浏览用户可以连接的内容。返回工作区集成;启用后,还会返回由目录连接器和外部应用生成的合成集成(这些候选项在有人连接后可成为完整集成)。

筛选条件

  • search — 按名称/密钥/描述进行全文搜索。省略此项可浏览完整目录。
  • connectors — 包含来自 Membrane 目录中连接器的合成集成(1 表示启用,0 表示禁用;省略时使用工作区默认值)。
  • externalApps — 同上,适用于尚未转为集成的外部应用。
  • limit、cursor — 标准分页参数。

响应

每个条目都包含足够的标识信息(id、key、相关时的 connectorKey、显示名称和徽标),可直接传递给 connect。

如果没有符合用户意图的结果,请跳过此步骤,调用带有 intent: "…" 的 connect——服务端会搜索连接器,并在需要时启动 Agent 即时构建连接器。

代码示例

# free-text search
curl -sG $API/integrations -H "Authorization: Bearer $TOKEN" \
  --data-urlencode 'search=crm'

# include catalog candidates that could become integrations
curl -sG $API/integrations -H "Authorization: Bearer $TOKEN" \
  --data-urlencode 'connectors=1' --data-urlencode 'externalApps=1'
连接
membrane connect [--integrationKey <key>] [--integrationId <id>] [--connectorKey <key>] [--connectorId <id>] [--connectionId <id>] [--connectionKey <key>] [--intent "<text>"] [--name <name>] [--non-interactive]

连接到外部应用,或重新连接现有应用。返回包含 id、state 的连接;如果用户仍需进行身份验证,还会包含 clientAction.uiUrl。将此 URL 交给用户,以便他们完成 OAuth 流程。

返回的 id 是连接 ID,可用于重新连接、查找和后续调试。

如何指定要连接的对象

  • integrationKey / integrationId — 工作区中已知的集成。
  • connectorKey / connectorId — 连接器(适用于尚无集成的情况;服务会按需创建集成)。
  • intent — 用自由文本描述用户想要连接的对象。服务会搜索连接器/集成并选择匹配项;如果没有合适的对象,则会启动 Agent 来构建连接器。
  • connectionId — 重新连接现有连接(使用相同的 OAuth 流程和记录)。

从 list-integrations 获取任何具体标识符后,直接将其传入上述参数之一。可以自由组合,例如使用 integrationKey + connectionKey,在稳定的键下创建新连接。

如果 intent 或目录候选项需要生成新内容,首次连接可能会比普通 OAuth 重定向耗时更久。请预期会有一个短暂的 BUILDING 阶段,此时 Membrane 正在搜索目录、创建集成或构建连接器。如果此流程失败,连接将进入 CONFIGURATION_ERROR 或 SETUP_FAILED 状态。

连接键

传入你自己的 connectionKey,以便之后识别该连接(act { connectionKey, ... }、list-connections、重新连接)。如果省略,服务会分配一个。

重新连接

调用 connect,并将 connectionId 指向现有连接(例如,发生过身份验证错误的连接)。使用相同的 OAuth 流程和记录,不会创建新连接。

响应

完整的连接对象:

{
  "id": "...",
  "key": "...",
  "state": "BUILDING" | "CLIENT_ACTION_REQUIRED" | "READY" | "CONFIGURATION_ERROR" | "SETUP_FAILED",
  "clientAction": { "type": "connect", "uiUrl": "..." }?,
  ...
}

交互式 CLI 会一直阻塞,直到 state 为 READY。在 MCP 中,连接面板会渲染 clientAction.uiUrl,并在用户完成后更新连接状态。

在代码中

连接需要用户交互——OAuth / 同意流程必须在真实浏览器中运行——因此它属于前端,并通过 Membrane SDK 的 UI 辅助方法完成。不要自行编写 HTTP 调用;membrane.ui.connect(...) 会以 iframe 覆盖层打开托管的连接界面,处理 OAuth 往返流程以及任何由 Agent 构建的连接器流程,并在完成后返回连接(如果用户关闭对话框,则返回 null)。

import { MembraneClient } from '@membranehq/sdk'

// Mint the token on your backend with get-membrane-token and hand it
// to the browser — never embed workspace secrets in front-end code.
const membrane = new MembraneClient({ token })

// Connect by connector key (catalog-level; resolves in a fresh workspace)
const connection = await membrane.ui.connect({
  connectorKey: 'github',
  connectionKey: 'github-dev',
})

// Connect by integration key (only once you've confirmed the
// integration exists in the current workspace, e.g. via
// `membrane integration list --search github`)
const existing = await membrane.ui.connect({
  integrationKey: 'github',
  connectionKey: 'github-dev',
})

// Connect by intent (agent may build a connector if needed)
const built = await membrane.ui.connect({
  intent: 'connect my GitHub account',
})

// Reconnect an existing connection (same record, fresh OAuth)
const reconnected = await membrane.ui.connect({
  connectionId: existing.id,
})

if (!connection) {
  // User closed the dialog before completing the flow
  return
}

在 React 中,使用 @membranehq/react 的 useMembrane() hook 获取相同的客户端 — useMembrane().ui.connect({...}) — 这样就不必手动接入 MembraneClient。

列出操作
membrane action list [--connectionId <id>] [--connectionKey <key>] [--intent "<text>"] [--limit <n>]

查找工作区中可复用的操作。先用它通过 key 发现可运行的内容,再考虑使用 act(运行)或 create-action(保存新操作)。

筛选条件

  • connectionId / connectionKey — 将范围缩小到特定连接的操作集。
  • intent — 自然语言描述。与连接(ID 或键)搭配时,结果会按语义匹配度排序。不指定连接时,intent 会被忽略。
  • limit、cursor — 标准分页参数。

响应

每个项目都包含 id、key、name、description、integrationKey、layer、inputSchema 和 outputSchema — 这些信息足以直接传入 act。如果没有匹配项,请转而使用 create-action(intent 路径)来构建操作。

目录操作(通过 --externalAppId 筛选时返回)会带有空的 key 字段 — 使用 act --id <hex> 调用它们。工作区本地操作的 key 非空,可通过任一方式调用。

在代码中

# semantic match on a specific connection
curl -sG $API/actions -H "Authorization: Bearer $TOKEN" \
  --data-urlencode 'connectionId=<id>' \
  --data-urlencode 'intent=create a contact'
执行
membrane act [--id <id>] [--key <key>] [--integrationKey <key>] [--connectionKey <key>] [--connectionId <id>] [--api <json>] [--code <text>] [--input <json>] [--meta <json>]

执行一项操作。通过 POST /act 进行一次统一调用——可复用操作或内联规范。

内联 api 和 code 执行是构建应用功能、脚本和同步任务的默认方式。需要稳定的键、共享架构,或希望从多个位置调用同一抽象时,请使用可复用操作。

只能指定一个调度字段

  • id — 可复用操作的数据库 ID。
  • key — 可复用操作的键。如果多个集成中存在相同的键,请与 integrationKey / integrationId / connectionId / connectionKey 搭配使用。

目录/公开操作(来自 list-actions --externalAppId …)会返回 id,但不返回 key——请通过 id 调度。对于工作区本地操作,id 和 key 可以互换使用。

  • api — 通过已解析连接的身份验证层和基础 URL 发送的内联 HTTP 请求:{ method, path, body?, headers?, query? }。
  • code — 在沙盒中运行的内联 JavaScript:module.exports = ({ input, membrane, connection, integration }) => …。其返回值即为操作输出。

api 和 code 需要连接。请提供 connectionId / connectionKey / integrationId / integrationKey,或使用工作区默认值。

连接解析

优先级:connectionId → connectionKey → 集成默认值(来自 integrationId 或 integrationKey)→ 工作区默认值。与 POST /actions/:selector/run 使用相同的链路。

响应

{ "output": ..., "actionRunId": "..." }

对于 4xx 响应,仍会携带 actionRunId——请将其与操作运行日志搭配使用以进行调试。

在代码中

# reusable action by key
curl -X POST $API/act -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"key":"create-issue","integrationKey":"github","input":{"title":"Hello"}}'

# inline HTTP through a connection
curl -X POST $API/act -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"api":{"method":"GET","path":"/user/repos"},"connectionKey":"github-dev"}'

# inline JS
curl -X POST $API/act -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"code":"module.exports = ({ input }) => ({ echoed: input })","input":{"hi":1},"connectionKey":"github-dev"}'
创建操作
membrane action create [--intent "<text>"] [--key <key>] [--name <name>] [--type <type>] [--config <json>] [--inputSchema <json>] [--integrationId <id>] [--integrationKey <key>] [--connectionId <id>]

将可复用操作保存到工作区。有两种方式:

按意图创建 — 传入 intent: "…" 和集成或连接上下文。Membrane Agent 会基于供应商的 API 构建操作、对其进行验证并注册。响应会返回处于 BUILDING 状态的操作 id;轮询 list-actions(按 ID 筛选),直到 state 为 READY,然后使用 act 运行。

按明确规范创建 — 传入 type(例如 api-request-to-external-app、run-javascript、list-data-records 等)、config(特定类型的载荷)以及可选的 key / name / inputSchema。当你已知所需结构时使用此方式——例如,你已通过内联 act 调用测试过,现在希望重复使用。

存储位置

  • 设置了 connectionId → 连接级操作实例(限定在一个连接内)。
  • 设置了 integrationId / integrationKey(未设置连接)→ 集成级操作(该集成上的所有连接均可使用)。
  • 两者均未设置 → 通用操作(所有支持该结构的集成都可使用)。

响应

{ "id": "...", "state": "BUILDING" | "READY" | "ERROR", ...standard action fields }

操作变为 READY 后,可通过 id 或 { integrationKey, key } 使用 act 运行。

在代码中

# intent
curl -X POST $API/actions -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"intent":"open a pull request","integrationKey":"github"}'

# explicit spec
curl -X POST $API/actions -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"key":"list-users","type":"api-request-to-external-app","integrationKey":"github",
       "config":{"request":{"method":"GET","path":"/users"}}}'
获取操作
membrane action get <id> [--wait] [--timeout <seconds>] [--json]

通过 ID 获取单个可复用操作。返回完整记录——id、key、name、state、type、config、inputSchema、outputSchema,以及所属的 integrationId / connectionId。

输入

  • id — 操作的数据库 ID。适用于集成级操作和连接级操作实例。

适用场景

  • create-action 返回 state: BUILDING 后——轮询此接口,直到 state: READY。
  • 通过 id / key 调用 act 之前——确认操作存在,且符合预期结构。
  • act 调用返回疑似操作配置问题的错误时——检查已保存的 config / inputSchema。

在代码中

# Shell
curl -s "$API/actions/<id>" -H "Authorization: Bearer $TOKEN"
// SDK
import { MembraneClient } from '@membranehq/sdk'
const membrane = new MembraneClient({ token })
const action = await membrane.action('<id>').get()
更新操作
membrane action update <selector> [--connectionId <id>] [--integrationId <id>] --data <json> [--json]

按 ID 或键部分更新可重复使用的操作。采用合并语义——只传入要更改的字段。要替换整个记录,请使用原始 API(PUT /actions/:selector)。

输入

  • id — 操作的数据库 ID(或使用 CLI <selector>,它接受 ID 或键)。
  • data — 要合并的字段。常见字段包括:name、description、config、inputSchema、key。具体结构取决于操作的 type。

作用域提示(仅限 CLI)

  • --connectionId / --integrationId — 当某个键同时存在于连接级实例和集成级操作时,用于区分。传入数据库 ID 时无需使用。

适用场景

  • 迭代改进刚创建的可重复使用操作——优化提示词、重命名、调整架构,而无需重新创建。
  • 将经过测试的内联 act 转换为可重复使用的操作,通常通过 create-action 完成;之后调整其 config 时可使用此工具。

代码示例

# Shell — merge update
curl -s -X PATCH "$API/actions/<id>" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"Create HubSpot contact"}'
// SDK
import { MembraneClient } from '@membranehq/sdk'
const membrane = new MembraneClient({ token })
const updated = await membrane.action('<id>').patch({ name: 'Create HubSpot contact' })
删除操作
membrane action delete <selector> [--connectionId <id>] [--integrationId <id>] [--json]

按 ID 或键归档可重复使用的操作。归档后的操作不会再显示在 list-actions 中,也无法再通过 act 调用。Membrane 默认执行软删除——使用原始 API 可恢复(POST /actions/:id/restore)或永久清除。

输入

  • id — 操作的数据库 ID(或 CLI <selector>,它接受 ID 或键)。

作用域提示(仅限 CLI)

  • --connectionId / --integrationId — 当某个键同时存在于连接级实例和集成级操作时,用于区分。传入数据库 ID 时无需使用。

适用场景

  • 清理未成功的实验性操作。
  • 移除已被更新键取代的过时集成级操作。

代码示例

# Shell
curl -s -X DELETE "$API/actions/<id>" -H "Authorization: Bearer $TOKEN"
// SDK
import { MembraneClient } from '@membranehq/sdk'
const membrane = new MembraneClient({ token })
await membrane.action('<id>').archive()
获取操作运行日志
membrane action-run-log get <actionRunId> [--details]

获取 Membrane 对单次操作运行的记录。每个 act 响应都会包含一个 actionRunId(成功和 4xx 错误响应中均会提供)——将其传入此处,即可查看实际发生的情况。

输入

  • actionRunId — act 返回的 ID。
  • details(可选)— 为 true 时,还会包含详细的运行内容:与外部应用的原始 HTTP 交互、映射后的输入和输出。

响应

{
  "id": "...", "status": "success" | "error",
  "action": { ... }, "connection": { ... }, "integration": { ... },
  "input": ..., "output": ..., "error": ...,
  "details"?: { "request": {...}, "response": {...}, ... }
}

摘要字段会告知你调用的结构。如果操作失败,请查看 details,了解 Membrane 发出的确切 HTTP 请求以及外部应用返回的内容。

代码示例

# summary
curl -sG $API/action-run-logs/<runId> -H "Authorization: Bearer $TOKEN"

# raw HTTP exchange (request/response content)
curl -sG $API/action-run-logs/<runId>/details -H "Authorization: Bearer $TOKEN"

评论

支持 Markdown