MCP Marketplace
MCP Marketplace 允许从精心维护的目录安装、启用和 scoping 外部 MCP server。MCP server 暴露 agent ReAct 可在聊天期间调用的 tool,无需代码即可扩展能力。
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。
| 方法 | Path | Auth | 说明 |
|---|---|---|---|
| GET | /api/mcp-marketplace | JWT | 列表;query ?workspaceId(添加 isInstalled) |
| GET | /api/mcp-marketplace/:entryId | JWT | 条目详情 |
| POST | /api/mcp-marketplace | JWT + admin | 创建目录条目 |
| POST | /api/mcp-marketplace/:entryId/install | JWT + admin | 安装到 workspace |
| POST | /api/mcp-marketplace/:entryId/uninstall | JWT + admin | 卸载 |
Install flow(marketplace.ts:165)
- Validate
installMcpServerSchema({ workspaceId: uuid, name?, headers? });override headers 在 write 侧用mcpHeadersSchema校验(D-12:hop-by-hop blocklist、name regex、size limits)。 - Check 条目存在(404)、尚未安装(409,过滤器
source: "marketplace"- 具有相同catalogEntryId的手动连接不会阻止)。 - 创建
MCPConnection:source="marketplace"、catalogEntryId、url/transportType从目录复制、enabled=true、name = override || catalog.name。 logEvent("mcp_connection", ..., "mcp.installed", ...)。- Fire-and-forget
connectMCPServer(connection.id)- 失败仅记录日志,不传播(仍返回 201)。
Uninstall flow(marketplace.ts:291)
- 通过
{ catalogEntryId, workspaceId, source: "marketplace" }查找MCPConnection(没有则 404)。 disconnectMCPServer+unregisterSkillsForConnection(移除前缀为mcp_<conn>_的 skill)。- Hard delete
MCPConnection(无 soft delete)。 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(mcpHeadersSchemaD-12)、transport fallback D-09(streamable-http主选 → 4xx → SSE fallback;声明sse= 仅 SSE;5xx 不触发 fallback)、Client.listTools()→ 将每个 tool 注册为 skillmcp_<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)→ 为mcpSourcesSSE 提供 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 connection | mcp.enabled |
| Disable connection | mcp.disabled |
| Delete connection | mcp.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)
- Settings → MCP Connections(Advanced 子节)或 /mcp-marketplace(admin/superuser)。
- 浏览目录(search、category filter、tier/health badge - 客户端过滤器)。
- 安装到 workspace → "Installed" badge + 上下文菜单 Uninstall。
- (可选)通过 chat header 中的
McpPinnerPopover将特定 tool pin 到某个 chat。 - 测试连接(
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)。
交叉链接
- Endpoint:04 - API Reference
- Agent loop:05 - Feature Guide
- License/audit flag:03 - Configuration / 08 - Administration