Activepieces MCP Server 深度指南:将工作流项目暴露为类型化 MCP 工具服务
2026/9/15 20:01:09 网站建设 项目流程

Activepieces MCP Server 深度指南:将工作流项目暴露为类型化 MCP 工具服务

【免费下载链接】activepiecesAI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces

Activepieces 的 MCP Server 能力将整个项目(flows、connections、tables、runs)通过标准 Model Context Protocol 暴露给 AI 客户端(Claude Desktop、Claude Code、Cursor、Windsurf、Codex 等),让 Agent 可以通过类型化工具接口直接读取和操作自动化资产。本文基于 mcp-server.md 并结合仓库源码,完整讲解其数据模型、工具体系、OAuth 认证机制、平台级多项目上下文切换,以及一线运维中容易踩中的关键陷阱,帮助你安全地在 CE / EE / Cloud 各版本中启用并管控这一能力。

概览:一个项目即一个 MCP Server

Activepieces 以每项目一条记录的方式暴露 MCP 能力:一个McpServer记录对应一个项目(projectId上存在 UNIQUE 约束),记录中包含 72 字符的token字段与disabledTools[](JSONB,可空)开关列表。该能力在 Community Edition、Enterprise Edition 与 Activepieces Cloud 中均可用。

从上层视角看,McpServer由 Fastify 插件mcpServerModule注册,模块入口在 mcp/mcp-module.ts,最终从 app.ts 挂载进应用:

export const mcpServerModule: FastifyPluginAsyncZod = async (app) => { await app.register(mcpServerController, { prefix: '/v1/projects/:projectId/mcp-server' }) await app.register(mcpPlatformController, { prefix: '/v1/mcp-server' }) }

每次请求到来时,服务端通过mcpServerService.buildServer()按请求动态构建一个McpServer实例,构建顺序为:server 元数据 → 动态 flow 工具 → 可控 + 锁定静态工具 → 空的 resources/prompts(协议合规占位),核心逻辑见 mcp-server-builder.ts。

词汇表:先统一领域术语

该功能涉及若干容易混淆的术语,仓库内部文档对此有严格约定(见 mcp-server.md):

术语含义注意点
Grant(授权)mcp_oauth_token表中的一行,表示某位用户对某个已注册客户端的当前有效授权;是 Connect 页面列出与吊销的最小单位域模型中名为McpOAuthGrant,由/v1/mcp-oauth/grants提供
Client(客户端注册)mcp_oauth_client表中的一行注册记录不是稳定身份:Claude Code、Codex 每次登录都会重新执行 DCR,同一"产品"会产生多行,同一用户重复认证会产生多个 grant。不要用"client"指代被吊销的对象
Connection(连接)属于 piece 认证体系(AppConnection),与 MCP 无关代码中禁止使用"MCP connection";界面上的 "Connections" 标签与/mcp-server/connectionsURL 是刻意采用的用户文案,代码里(app/routes/mcp-server/grants/)统一叫 grant
Pieces(标签页)已连接客户端在一个项目内可调用的 piece 动作,对应/mcp-server/pieces标签仅限 piece 动作,不包含 flow / table / run 工具;"Reach" 只是文案动词,不是标签名

"Reach" 作为标签名已被废弃(读起来像名词却不指代任何对象);"Tools"(在项目设置中指可锁定/可控制的工具列表)、"Capabilities"(过度承诺,暗示含非 piece 工具)、"Actions"(指 flow 步骤)、"Permissions"(RBAC 语义,且该页只是镜像、无可编辑项)均不适宜作为该标签的名称。

实体与数据模型

MCP 相关的核心实体定义在 packages/core/shared/src/lib/automation/mcp/ 下的mcp.tsmcp-oauth.ts

McpServer 记录

export const McpServer = z.object({ ...BaseModelSchema, platformId: z.nullable(ApId), projectId: z.nullable(ApId), type: z.enum([McpServerType.PLATFORM, McpServerType.PROJECT]), token: ApId, // 72 字符,注意:实际不参与认证(见下文 Gotchas) disabledTools: z.array(z.string()).nullable(), })
  • token为 72 字符随机串;
  • disabledTools为 JSONB 数组,null/[]均表示所有可控工具全部启用;
  • 类型分为PROJECT(项目级)与PLATFORM(平台级)两种;
  • 配置更新仅接受disabledTools字段(UpdateMcpServerRequest)。

getOrCreate的默认创建逻辑(mcp-service.ts)会写入token: apId(72)disabledTools: [],并通过 UNIQUE 约束下的并发兜底(冲突时回查已存在记录)保证每项目/每平台只有一条。

工具定义

McpToolDefinition是工具注册的通用载体(mcp.ts):

export type McpToolDefinition = { title: string description: string inputSchema: Record<string, z.ZodTypeAny> annotations?: { readOnlyHint?: boolean destructiveHint?: boolean idempotentHint?: boolean openWorldHint?: boolean } permission?: Permission execute: (args: Record<string, unknown>) => Promise<McpToolResult> }

inputSchema直接使用 Zod shape(与 MCP 协议期望一致),permission字段声明该工具所需的 RBAC 权限,运行时由permissionChecker.wrapExecute统一包裹。

工具体系:锁定、可控、搜索与动态 Flow 工具

工具按性质划分为四类,注册逻辑集中在 tools/index.ts,构建期过滤逻辑在 mcp-server-builder.ts。

1. 锁定工具(Locked Tools)

只要 MCP 启用就必然注册、无法被disabledTools关闭的工具,主要为只读发现类:

ap_list_flowsap_flow_structureap_read_step_codeap_read_step_settingsap_validate_flowap_research_piecesap_get_piece_propsap_resolve_property_optionsap_resolve_property_chainap_validate_step_configap_list_connectionsap_list_ai_modelsap_list_tablesap_find_recordsap_list_runsap_get_runap_setup_guide(完整清单见LOCKED_TOOL_NAMES)。

注意:锁定列表中还包含ap_search_actionsap_search_triggers,但这两个工具仅在环境变量AP_TOOL_SEARCH_ENABLED开启时才真正注册(见下),因此它们的锁定条目在开关关闭时是"惰性"的。

2. 工具搜索工具(Tool-search Tools)

ap_search_actions/ap_search_triggers提供对动作/触发器目录的语义搜索(基于 pgvector),并在关键词匹配不足时回退到关键字地板(keyword-floor)机制。注册条件严格受AP_TOOL_SEARCH_ENABLED控制:

...(isToolSearchEnabled() ? [apSearchActionsTool(mcp, log), apSearchTriggersTool(mcp, log)] : []),

该环境变量是总开关与回滚路径:关闭时工具根本不注册(未注册的工具不可能被强制打开),设置面板则通过TOOL_SEARCH_ENABLED标志展示对应条目。

3. 可控工具(Controllable Tools)

通过项目的disabledTools逐项开关,覆盖 flow/step/branch 管理、发布、table 与 record 操作、测试与 run 管理(见ALL_CONTROLLABLE_TOOL_NAMES):ap_build_flowap_create_flowap_duplicate_flowap_rename_flowap_update_triggerap_add_stepap_update_stepap_delete_stepap_add_branchap_update_branchap_delete_branchap_lock_and_publishap_change_flow_statusap_delete_flowap_manage_notesap_create_tableap_delete_tableap_manage_fieldsap_insert_recordsap_update_recordap_delete_recordsap_test_flowap_test_stepap_retry_runap_run_action

构建期的过滤逻辑为:LOCKED_TOOL_NAMES中的工具或不在disabledTools中的工具才被注册:

const disabledToolSet = new Set(mcp.disabledTools ?? []) const tools = allTools.filter(t => LOCKED_TOOL_NAMES.includes(t.title) || !disabledToolSet.has(t.title))

4. 动态 Flow 工具(Dynamic Flow Tools)

每个启用了 MCP 触发器 piece(@activepieces/piece-mcp)的 flow都会成为一个可调用工具,命名为{toolName}_{flowId[0..4]};执行时通过 webhook 提交(returnsResponse为 true 时同步等待响应,否则异步)。触发器配置由extractMcpTriggerInput读取(mcp-server-builder.ts):

export function extractMcpTriggerInput(flow: PopulatedFlow): { toolName?: string, toolDescription: string, mcpInputs: McpProperty[], returnsResponse: boolean } { const mcpTrigger = flow.version.trigger.settings as McpTrigger return { toolName: mcpTrigger.input?.toolName, toolDescription: mcpTrigger.input?.toolDescription ?? '', mcpInputs: mcpTrigger.input?.inputSchema ?? [], returnsResponse: mcpTrigger.input?.returnsResponse ?? false, } }

McpProperty支持Text / Boolean / Date / Number / Array / Object六种输入类型(见 mcp-piece.ts)。动态 flow 工具统一声明FLOW_TOOL_ANNOTATIONS = { readOnlyHint: false, destructiveHint: false, openWorldHint: true },因为执行真实 flow 会改变第三方系统状态。只有FlowStatus.ENABLED的 flow 才会被列出(registerFlowTools中的过滤),且调用前会先做permissionChecker.check(Permission.WRITE_RUN, toolName)检查。实际执行走webhookService.handleWebhook,超时受FLOW_TIMEOUT_SECONDS系统属性控制。

工作原理:协议端点、认证与传输

协议端点

MCP 主协议端点为域名根路径的POST /mcp,另有POST /mcp/platform(平台级,StreamableHTTP),均在server.ts中注册;项目级配置走项目 API(GET/POST /v1/projects/:projectId/mcp-server)。Fastify 启用了ignoreTrailingSlash: true,因此/mcp//mcp命中同一路由且不会产生 301

认证:仅 OAuth

MCP 协议认证只接受 OAuthresolveIdentity仅当mcpOAuthTokenService.verifyAccessTokenAuthorization: Bearer中的值验证为 audience 为JwtAudience.MCP_OAUTH_ACCESS的签名 JWT 时才放行。不存在静态 token 认证器,也没有?token=查询参数路径

OAuth 侧是完整的 OAuth 2.0 PKCE 流程(metadata、authorize、token、revoke),实现位于 mcp/oauth/:

mcp-oauth/ ├── client/ # DCR 注册(register)、client identity 推导 ├── code/ # authorize 页面、授权码实体与服务 ├── metadata/ # OAuth 授权服务器元数据 ├── token/ # 令牌签发、grants 列表、吊销 ├── mcp-oauth-validation.ts ├── mcp-oauth.pkce.ts

授权服务器元数据会向客户端宣告支持的端点;401 响应携带符合 RFC 9728 的WWW-Authenticate: Bearer resource_metadata="…"头。OAuth 发现 URL 通过domainHelper.getPublicUrlFromRequest构建,因此子路径托管(subpath-hosted)的实例会通告正确的前缀;宿主机根路径的.well-known/oauth-*仍须由运维把流量转发到 Activepieces。

三种传输与 AI pieces

AI pieces 通过SIMPLE_HTTPSTREAMABLE_HTTPSSE三种传输消费 MCP 工具,相关类型与客户端身份枚举(claudeclaude-codechatgptcursorvscodecodexgemini-cliopencodewindsurfunknown)定义在 mcp-oauth.ts。

Embed SDK 集成

Embed SDK(packages/ee/embed-sdk/src/index.ts)新增三个公开方法:

  • authorizeMcp()— 在嵌入环境中发起 OAuth 授权同意流程;
  • mcpSettings()— 读取/管理 MCP 设置;
  • generateMcpToken()免 OAuth 流程地铸造{ mcpServerUrl, mcpToken },背后由POST /v1/projects/:projectId/mcp-server/token支撑,签发的是15 分钟有效期、项目级作用域的短期 token(实现见 mcp-server-controller.ts 的issueInternalAccessToken)。

配置入口:项目级与平台级 API

项目级控制器(mcp-server-controller.ts)提供:

方法路由权限说明
GET/v1/projects/:projectId/mcp-serverREAD_MCP获取项目 MCP 配置(含 flow 列表)
POST/v1/projects/:projectId/mcp-serverWRITE_MCP更新disabledTools
POST/v1/projects/:projectId/mcp-server/rotateWRITE_MCP轮换 token(注意:见 Gotchas)
POST/v1/projects/:projectId/mcp-server/tokenREAD_MCP生成 15 分钟短期 MCP token 与 URL

更新请求体:

{ "disabledTools": ["ap_test_flow", "ap_run_action"] }

平台级控制器(mcp-platform-controller.ts)提供GET/POST /v1/mcp-serverPOST /v1/mcp-server/rotate,且仅平台管理员可访问(platformAdminOnly)。

平台级多项目上下文切换

/mcp/platform是平台级端点:它注册ap_set_project_context工具,其余非平台级工具每次调用都会从 Redis 重新读取"已选项目",因为传输层是无状态的(sessionIdGenerator: undefined,每次 POST 都新建McpServer,没有可承载选择的会话)。

选择键的格式为:

mcp-project-selection:client:{platformId}:{userId}:{clientId}

其中clientId从访问令牌中读取。历史上该键是…:user:{platformId}:{userId}(GIT-1831 之前),导致同一个平台级授权上的两个客户端(如 Claude Code 与 LibreChat 都指向/mcp/platform)互相覆盖对方的项目选择,表现为"对一个明明存在、REST 读取正常的 flow 间歇性报 Flow not found"。改为clientId后仍有两点固有行为:客户端重新执行 DCR 登录时选择会重置;同一注册的多个实例共享同一份选择。无状态请求上没有任何字段能区分这两种情况。实现见 mcp-project-selection.ts,TTL 为 24 小时。

ProjectSelectionScope曾携带{ conversationId }变体,PR #13356(fbfbcd7578)已移除其唯一调用方,改为使用会话自身的 PostgresprojectId;内部聊天从不写入该键(ap_set_project_contextCHAT_HIDDEN_TOOL_NAMES中)。不要为外部客户端重新引入会话作用域——它们永远不会发送x-ap-conversation-id

权限模型:RBAC 与提示注解的边界

RBAC 权限检查

在 CLOUD / ENTERPRISE 版本中,每个工具调用都会经过resolvePermissionChecker(mcp-permissions.ts):根据调用用户在项目中的角色权限集合判定,无权限时返回isError: true的拒绝消息;用户在该项目中没有角色时,凡声明了permission的工具一律拒绝。CE 版本使用ALLOW_ALLcheck: () => null)。

const EDITION_REQUIRES_RBAC = [ApEdition.CLOUD, ApEdition.ENTERPRISE].includes(system.getEdition())

三类安全提示注解

每个注册的工具都必须声明全部三个安全提示readOnlyHintdestructiveHintopenWorldHintMcpToolDefinition.annotations是可选字段,buildToolConfig会原样透传——因此遗漏某个 hint 是静默的:MCP 客户端会回退到协议默认值,但 ChatGPT Apps 提交审核会把任何缺失的 hint 视为阻塞项。

最容易遗漏的是两条动态路径(它们在内部直接构建工具配置,而不是从McpToolDefinition出发):registerFlowTools(每个启用 MCP 触发器的 flow 对应一个工具)与registerPlaceholderTools(未选择项目状态——这也是外部评审者最先见到的状态)。占位工具按清单分别注解:锁定名使用只读三元组,可控名使用destructive: true, openWorld: true——占位工具永远不能把自己宣传得比它所代表的真实工具更安全。

关于openWorldHint的关键语义:它表示工具能否改变第三方系统状态,而不是"是否发起出站调用"。执行真实连接器步骤的工具必须声明它:ap_test_flowap_test_stepap_retry_runap_run_action,以及全部动态 flow 工具。仅为填充下拉菜单而调用已连接账号的只读工具(ap_get_piece_propsap_resolve_property_optionsap_resolve_property_chain)则不需要。ap_retry_run曾在此处误标false——重试会重跑已发布 flow,可能重发同一条 Slack 消息或重复一次出站写入,因此必须为true

最后牢记:这些提示只是给客户端的建议性元数据,绝非强制执行。授权始终由permissionChecker.wrapExecute与每个工具的permission决定;修改注解只改变"客户端被告知什么",不改变"调用者实际被允许做什么"。

关键陷阱(Gotchas)

以下来自项目内部一线经验,直接影响安全与可用性:

  1. mcp_server.token是死字段——没有任何代码读取它。它由getOrCreate默认值与/rotate两个路由(mcpServerService.rotateToken/rotatePlatformToken)写入,但没有任何认证器查询它——"轮换"轮换的是一个不授予任何权限的秘密。它仍存在于公开的McpServerzod schema 上,因此 API 仍在输出一个形似凭据、却不认证任何东西的 72 字符串。不要把它当凭据使用,也不要让自托管用户这么做。设置面板与事实保持一致(mcp-credentials.tsx只渲染 URL 与"Authentication is handled via OAuth",从不显示 token)。删除该列、两个路由与 schema 字段属于破坏性 API 响应变更,尚未实施。

  2. mcp_oauth_token.clientKey在登录时一次性决定exchangeCode通过mcpOAuthClientIdentity从注册的 redirect URI 推导它,因此 grants 列表可以在 SQL 中过滤分组,而不是把平台上每一行mcp_oauth_client载入内存重新推导。两个后果:日后改进该启发式不会为既有 grants 重新贴标签(它们 30 天过期,活跃客户端会在下次刷新时重新标记,从而回填 NULL 键);NULL不是第三种状态——它表示"在该列存在之前登录",在所有地方都显示为unknown,包括?clientKeys=unknown过滤器。

  3. Claude Code 与 Codex 每次登录都会重新执行 DCR,注册的正是它们即将绑定的临时回环端口(http://localhost:<port>/callbackhttp://127.0.0.1:<port>/callback/<callback_id>)。因此精确字符串匹配的validateRedirectUri有效、无需 RFC 8252 的端口无关匹配——但每次登录都会新铸一行mcp_oauth_clientclientId,所以clientId不是"某个已连接客户端"的稳定身份,且这些行会无限累积(2026-08-23 于 Claude Code 2.1.235、Codex 0.149.0 实测)。

  4. client_id仍按^[A-Za-z0-9_-]{1,64}$校验时,绝不要通告client_id_metadata_document_supported。Claude Code 偏好 Client ID Metadata Document(其client_id是 URL),只有在服务端对 CIMD 保持沉默时才回退到 DCR。通告该能力却不放宽client_id形状会直接破坏 Claude Code 登录。

  5. 对 MCP 客户端而言,一个静态的Authorization头比没有更糟。Codex 中设置bearer_token_env_varAuthorization头会短路到 bearer 认证、完全跳过 OAuth 发现;Claude Code 中被拒绝的Authorization头表现为连接失败而非回退到 OAuth。因此一个半成品静态 token 路径会静默禁用本来可用的 OAuth 路径。另外:headless/CI(claude -p、SDK)没有/mcp面板,目前没有受支持的连接方式。

  6. Flow 归属ap_create_flow/ap_build_flow/ap_duplicate_flow会盖上ownerId(OAuth 用户)与createdBy: { type: 'MCP', id }

  7. 遥测去重MCP_SERVER_CONNECTED通过telemetryDedupe.onceToday去重为每人/每 server/每天最多一条——它是"日活"信号而非请求量;按次调用走MCP_TOOL_CALLED

  8. MCP URL 必须无需重定向即可直达。跨域的301/302/307/308会在所有符合规范的客户端中剥掉Authorization头,且"跨域"包含 scheme——所以代理处纯httphttps的规范化与 apex→www 一样致命。它失败得"看似正常":发现过程由请求推导(networkUtils.getRequestBaseUrl读取x-forwarded-proto/host),OAuth 登录在规范源上完成,而客户端继续向它拿到的 URL POST,造成永久401或反复重认证,而不是干净的错误。Activepieces 自身从不在该处重定向——仅有的前缀是/mcp/mcp/platform,且 Fastify 开启ignoreTrailingSlash: true——所以这永远是运维的代理配置问题,且服务端无法检测(代理替它应答了重定向前的那次请求)。

  9. DCR 在token_endpoint_auth_method缺省时必须签发 client secret。RFC 7591 §2 规定缺省值默认为client_secret_basic而非none;Microsoft Copilot Studio 没有 client secret 会直接拒绝 DCR。把缺省方法默认成none看似解决了"公共客户端被发了 secret"的矛盾,却用错误的方式解决了它:破坏 Copilot,并让省略该字段的客户端永远够不到client_secret_basic。正确做法是默认client_secret_basic并持续签发 secret。

  10. **x-ap-conversation-id头(EE 聊天)**可将 server 重绑到某会话的项目,但仅在作用域与 token 匹配时生效——它永远无法扩大授权范围。

  11. 禁用ap_run_action后目录仍完全可浏览,且无法隐藏。piece 发现类工具(ap_research_piecesap_search_actionsap_search_triggersap_get_piece_props)在LOCKED_TOOL_NAMES中,disabledTools无法关闭——只有执行器ap_run_action可控。因此关闭运行动作的项目,已连接客户端仍能枚举其理论上可调用的每个 piece 与动作。这一不对称正是 Pieces 标签在列表顶部警示而非隐藏行的原因。注意失败形态:被禁用的工具从不registerTool,客户端拿到的是协议层的 unknown-tool 错误,而非工具内部的权限拒绝——"每次调用都失败"的说法方向正确但差了一层。

Pieces 标签的服务端搜索

Pieces 标签的搜索在服务端完成,且只有在pieceDisplayName是 Fuse 键时才能工作。/v1/pieces?searchQuery=会把每个 piece 的actions替换为匹配子集(searchForSuggestion)——对展示"每 piece 动作数与破坏性徽标"的页面这听起来是致命的——但searchForSuggestion搜索['pieceDisplayName', 'displayName', 'description'],因此查询piece名会匹配其内部每个动作,行内列表依旧完整。另有两点保证安全:toPieceMetadataModelSummary从搜索前的audiencePieces计算summary.actions,总数永不会被查询收窄;搜索时标签会强制展开每一行,渲染的计数与下方列表可见地一致。热门优先排序只用于未搜索视图——套用到搜索结果会丢弃 Fuse 的相关性排名。行级分组与计数在客户端由piecesUtils.toReachablePieces完成,这是一个带独立单元测试的纯函数。

设置面板与前端

前端设置面板位于 packages/web/src/app/components/project-settings/mcp-server/,负责凭据展示、flows-as-tools 与工具开关;Connect / Pieces / Grants 三个标签在 packages/web/src/app/routes/mcp-server/;独立 OAuth 同意页在 packages/web/src/app/routes/mcp-authorize/;嵌入场景的embedded-mcp-*对话框在 packages/web/src/app/routes/embed/。builder 中单工具测试对话框在 mcp-tool-testing-dialog.tsx。注意ALL_CONTROLLABLE_TOOL_NAMES与前端mcp-tools-metadata.ts中的TOOL_CATEGORIES必须保持同步,否则新增工具不会出现在设置面板中。

关键文件索引

  • 入口模块与路由:mcp/mcp-module.ts、mcp-server-controller.ts、mcp-platform-controller.ts
  • 服务与构建器:mcp-service.ts、mcp-server-builder.ts
  • 工具定义:mcp/tools/
  • OAuth 流程:mcp/oauth/
  • 权限与项目选择:mcp-permissions.ts、mcp-project-selection.ts
  • 共享类型:packages/core/shared/src/lib/automation/mcp/
  • 设置面板与标签页:mcp-server/、routes/mcp-server/
  • Embed SDK:packages/ee/embed-sdk/src/index.ts
  • 外部 MCP server 作为 agent 工具(代理探针,非 AP-as-server 功能):packages/web/src/features/agents/agent-tools/

使用建议小结

  • 认证:统一走 OAuth(PKCE),不要为客户端配置静态 bearer 头,也不要引导用户读取mcp_server.token
  • 安全最小化:通过disabledTools关闭ap_run_action等高风险可控工具,同时理解锁定工具仍会完整暴露 piece 目录;
  • 代理:确保/mcp/mcp/platform.well-known/oauth-*无重定向直达,且 scheme 与 host 保持不变;
  • 平台级使用:多客户端共享/mcp/platform时注意项目选择按clientId隔离,重新登录会重置选择;
  • 工具注解:所有工具(尤其是动态 flow 工具与占位工具)必须补齐三个安全 hint,否则可能被客户端审核流程拒绝。

【免费下载链接】activepiecesAI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询