返回文档
指南 07

MCP Marketplace

MCP Marketplace 允许从精心维护的目录安装、启用和 scoping 外部 MCP server。MCP server 暴露 agent ReAct 可在聊天期间调用的 tool,无需代码即可扩展能力。

3 分钟阅读

MCP Marketplace 允许从精心维护的目录安装、启用和 scoping 外部 MCP server。MCP server 暴露 agent ReAct 可在聊天期间调用的 tool,无需代码即可扩展能力。

系统横跨:Prisma 模型(McpCatalogEntry、MCPConnection、ChatMCPPin)、route(marketplace.ts、mcp.ts)、agent skill registry(skills.ts)、service(health-check scheduler、DLP filter、audit logging)。

概念

  • `McpCatalogEntry` - 目录模板(27 个 seed 条目:12 个 placeholder + 15 个 curated)。字段:name、url、transportType(sse/streamable-http)、headers(JSON string)、description、category、version、author、verificationTier(official/verified_community/unverified)、healthStatus(healthy/stale/down)、lastHealthCheck、lastHealthError、lastCommitDate、consecutiveFailures。
  • `MCPConnection` - workspace-scoped 连接。source(manual/marketplace)、catalogEntryId(来自 marketplace 时的 FK)、enabled、headers(JSON string)、workspaceId/projectId。
  • `ChatMCPPin` - 按 chat scoping。@@unique([chatId, connectionId])。unpin 时 hard-deleted。

目录(browse + install)

browse(列表和详情)只需 JWT 认证;写操作需要 authMiddleware + requireAdmin。前端在客户端过滤 search/category。

方法PathAuth说明
GET/api/mcp-marketplaceJWT列表;query ?workspaceId(添加 isInstalled)
GET/api/mcp-marketplace/:entryIdJWT条目详情
POST/api/mcp-marketplaceJWT + admin创建目录条目
POST/api/mcp-marketplace/:entryId/installJWT + admin安装到 workspace
POST/api/mcp-marketplace/:entryId/uninstallJWT + admin卸载

Install flow(marketplace.ts:165)

  1. Validate installMcpServerSchema({ workspaceId: uuid, name?, headers? });override headers 在 write 侧用 mcpHeadersSchema 校验(D-12:hop-by-hop blocklist、name regex、size limits)。
  2. Check 条目存在(404)、尚未安装(409,过滤器 source: "marketplace" - 具有相同 catalogEntryId 的手动连接不会阻止)。
  3. 创建 MCPConnection:source="marketplace"、catalogEntryId、url/transportType 从目录复制、enabled=true、name = override || catalog.name。
  4. logEvent("mcp_connection", ..., "mcp.installed", ...)。
  5. Fire-and-forget connectMCPServer(connection.id) - 失败仅记录日志,不传播(仍返回 201)。

Uninstall flow(marketplace.ts:291)

  1. 通过 { catalogEntryId, workspaceId, source: "marketplace" } 查找 MCPConnection(没有则 404)。
  2. disconnectMCPServer + unregisterSkillsForConnection(移除前缀为 mcp_<conn>_ 的 skill)。
  3. Hard delete MCPConnection(无 soft delete)。
  4. logEvent("mcp.uninstalled")。

Re-install = 重新执行 install flow(新的 MCPConnection 带新 ID)。

手动连接(CRUD)

/api/mcp-connections(mcp.ts,authMiddleware + requireAdmin):GET、GET /statuses(从 activeConnections Map 实时读取,非 DB)、POST 创建、PUT /:id(enabled 时自动 disconnect/reconnect)、DELETE /:id、POST /:id/toggle、POST /:id/test(10s 超时,返回 { success, toolCount?, error? })。

marketplace 安装的连接与手动连接在 CRUD 层面无法区分 - source 字段区分来源。

MCP client(agent/mcpClient.ts)

  • connectMCPServer(connectionId)(:233):ensureConnected cache-hit → read-side header validation(mcpHeadersSchema D-12)、transport fallback D-09(streamable-http 主选 → 4xx → SSE fallback;声明 sse = 仅 SSE;5xx 不触发 fallback)、Client.listTools() → 将每个 tool 注册为 skill mcp_<connectionId>_<toolName>(UUID 前缀无冲突)。
  • Per-connection mutex withConnectionLock。
  • activeConnections: Map<connectionId, MCPConnectionState>(不持久化,restart 时重建;workspace/project 的 scope 在 connect-time 捕获,用于 D-14 过滤)。
  • disconnectMCPServer(:363):delete-first + unregister + client.close()。
  • shutdownMCPConnections 用于 graceful shutdown。
  • resolveMcpSourceName(toolName) → 为 mcpSources SSE 提供 human-readable 名称。

Per-chat pinning

pin 允许限制哪些 MCP 连接向特定 chat 提供 tool。

/api/chats/:chatId/pins(mcpPins.ts):GET、POST(createMcpPinSchema)、DELETE /:pinId。

解析(agent/skills.ts:136 resolveSkillsForChat):

  • 无 pin → workspace 所有 enabled 的 MCP 连接提供 skill。
  • 有 pin → 过滤 pinned AND enabled AND 同一 workspace 的连接(双方均为 null 的 global 包含;project-scoped 排除)。
  • 所有 pinned 被禁用/out-of-scope → fallback workspace defaults + warn log。
  • Union MCP-02(strict,D-04):builtin + pinned MCP skill + 与活跃 workspace-scoped tool 的 union(pin 从不移除 tool - 只添加)。

Health check

services/mcpHealthCheckJob.ts - pg-boss cron 30-min(initMCPHealthCheckScheduler() 从 src/index.ts 启动;*/30 * * * *,pg-boss 不可用时无 fallback timer - D-02)。遍历 已安装 + enabled 且带 catalogEntryId 的连接:ping url 并指数 retry(1s/2s/4s,timeout 10s),更新 healthStatus(healthy / 第 1-2 次失败为 stale / 第 3 次起为 down)、lastHealthCheck、lastHealthError、consecutiveFailures。非阻塞:stale/down 条目仍可见并带 warning badge;reset 从一次成功 ping 开始。

Trust layer(verification tier)

verificationTier 仅 seed-data(运行时不会写入):

  • official - 由 Simmetric Chat 项目维护。
  • verified_community - 由 maintainer 批准。
  • unverified - 社区,未经审核。

lastCommitDate = 新鲜度指标。tier 仅供参考(不阻止安装),但会以 badge 形式渲染。安装 unverified/down 条目的风险由 admin 承担。

Audit logging

所有操作通过 eventLogService.logEvent 记录,entityType = "mcp_connection":

操作Event action
从 marketplace 安装mcp.installed
从 marketplace 卸载mcp.uninstalled
Enable connectionmcp.enabled
Disable connectionmcp.disabled
Delete connectionmcp.deleted

在 Community 中事件经过 logEvent(没有 Enterprise 插件时写入为 no-op,webhook dispatch 仍然活跃);启用 audit_log_immutable(Enterprise)后,事件写入不可变 audit log。用 entityType = "mcp_connection" 过滤以获得完整 audit trail。

SSE 中的 mcpSources

SSE done 包含 mcpSources: string[] - 其 tool 被调用的 MCP 连接名称。通过 resolveMcpSourceName 从 tool call 的前缀(mcp_<conn>_<tool>)推导。前端在消息上显示来源 chip;widget proxy 以 byte-verbatim 中继事件,因此数据在客户端仍然可用。

如何使用(admin)

  1. Settings → MCP Connections(Advanced 子节)或 /mcp-marketplace(admin/superuser)。
  2. 浏览目录(search、category filter、tier/health badge - 客户端过滤器)。
  3. 安装到 workspace → "Installed" badge + 上下文菜单 Uninstall。
  4. (可选)通过 chat header 中的 McpPinnerPopover 将特定 tool pin 到某个 chat。
  5. 测试连接(POST /:id/test)以验证实时 toolCount。

如何扩展

  • 新 catalog entry:POST /api/mcp-marketplace(admin)或在 Prisma 中直接 seed(seedCatalogEntries,幂等 upsert - 运行时字段如 installCount 永远不会被 re-seed 覆盖)。
  • 新 transport:扩展 buildTransport(mcpClient.ts:136)+ TransportKind。
  • 自定义 scope:getMCPToolsForWorkspace(workspaceId) 按 scope.workspaceId 过滤(包含 global)。

交叉链接