跳到主要内容

第 11 章:Skills、Runtime 能力、MCP 与 Connected Apps

11.1 四种容易混淆的能力

Multica 里的“Agent 能做什么”来自四个来源:

能力本质生命周期
Workspace Skill数据库存储的说明与文件包跟随工作区与 Agent 绑定
Runtime-local SkillDaemon 所在机器已有的 Skill跟随机器、Provider 和本地目录
MCP Server可调用工具的协议端点Agent 静态配置或任务级 Overlay
Connected App用户已授权的 SaaS 能力跟随 Owner 的连接和本次 Run

Skill 主要告诉 Agent 如何工作;MCP 提供真正的工具调用;Connected App 是动态生成 MCP Session 的上层产品概念。把它们合成一个“插件列表”会看不懂权限和缓存。

11.2 Workspace Skill 的数据模型

数据库查询入口在 server/pkg/db/queries/skill.sql,Handler 在:

核心关系是:

  • Skill:名称、描述、主 SKILL.md 内容、来源;
  • Skill File:References、Scripts、Templates 等支持文件;
  • Agent Skill:Agent 与 Skill 的绑定;
  • Built-in Skill:随 Server 发布、可按规则装入工作区。

Agent 不直接持有一份可变副本,而是引用 Skill。这样修订、权限、去重与版本归因有统一事实源。

11.3 Import 的资源上限

当前 Handler 明确限制:

  • 单文件最多 1 MiB;
  • 支持文件总计最多 8 MiB;
  • 支持文件最多 256 个;
  • 路径必须安全,不能越过 Skill 根;
  • 嵌套目录里的其他 SKILL.md 不当普通支持文件导入。

这些限制既防内存和网络滥用,也保证每次 Claim 不会携带一个近似仓库大小的能力包。

11.4 内容寻址 Bundle

server/pkg/skillbundle/hash.go 为主内容和每个支持文件计算 SHA-256,并得到整体 Bundle Hash。

Server Claim 可以只发 SkillRefData

  • Source / ID;
  • Name / Description;
  • Bundle Hash / Size / File Count;
  • 各文件 Path / SHA-256 / Size。

Daemon 命中缓存便不下载正文;未命中才通过受控 API Resolve Bundle。

11.5 Daemon 缓存不是盲信磁盘

server/internal/daemon/skill_cache.go 的缓存键包含 Workspace 与引用信息,并在 Load 后调用 validateSkillBundle

  • ID、Source、Hash 必须匹配;
  • 文件路径必须安全;
  • 逐文件摘要必须一致;
  • Bundle 元信息必须与 Ref 对齐。

每个 Ref 还有进程内锁,避免并发任务同时下载和覆盖同一 Bundle。坏缓存按 Miss 处理,不把篡改内容直接注入 Agent。

11.6 下载超时随 Bundle 尺寸缩放

ensureTaskSkillBundles 在 Task Start 前解析所有引用。

当前超时策略:

  • 最少 30 秒;
  • 按保守 50 KiB/s 估算;
  • 最多 5 分钟。

这比固定短超时适合 8 MiB 上限的合法 Bundle,也防止一个卡死下载永久占用 Prepare Lease。

11.7 Skill 物化

解析完成后,execenv/context.go 把 Skill 写入 Provider 可发现目录:

  • 主文件固定为 SKILL.md
  • 支持文件保留相对目录;
  • 路径经过清洗;
  • 所有写入进入 Sidecar Manifest;
  • 名称转换成文件系统安全 Slug。

如果导入内容没有有效 YAML Frontmatter,Daemon 用 ensureSkillFrontmatter 补出 name 与 description。修复发生在物化层,原始数据库内容仍可追踪。

11.8 Runtime-local Skill

用户的 Claude、Codex 或其他 Provider Home 里可能已经装有 Skills。Daemon 通过 local_skills.go 枚举:

  • Provider 专用 Skill Roots;
  • 通用 ~/.agents/skills
  • Claude Plugin 中带命名空间的 Skills;
  • 受支持目录深度内的合法 SKILL.md

Runtime-local Skill 不上传为默认全文。Daemon 先报告摘要,用户可显式 Import 到 Workspace,或让 Runtime 自己按 Provider 规则发现。

11.9 本地枚举同样有限额

本地扫描沿用:

  • 1 MiB 单文件;
  • 8 MiB Bundle;
  • 256 文件;
  • 有限目录深度;
  • Symlink/Visited 防循环;
  • 隐藏、构建产物等忽略规则。

本机文件仍是不可信输入。没有限制的递归扫描会让一个损坏 Plugin 或巨大依赖目录拖垮心跳任务。

11.10 禁用 Runtime Skill

Workspace Agent 可以携带 DisabledRuntimeSkills,身份由 Runtime ID、Provider、Root、Key 共同确定。

runtime_skill_policy.go 按 Provider 选择隐藏办法:

  • 有些 Provider 可通过独立 Home 隔离;
  • 有些需要复制/链接时排除目录;
  • 有些只能在生成配置中撤掉发现路径。

这不是删除用户机器上的 Skill,而是限制当前 Agent 进程的可见性。

11.11 Agent 静态 MCP 配置

Agent 的 mcp_config 使用统一外形:

{
"mcpServers": {
"name": {
"command": "example",
"args": ["serve"]
}
}
}

这一 JSON 是控制平面的规范形状。Daemon 再按 Provider 生成:

  • Codex Config;
  • Cursor MCP 文件;
  • OpenCode/OpenClaw 配置;
  • Hermes/ACP Session 参数;
  • 支持直接参数的 Provider 调用选项。

“统一存储”并不意味着“统一落盘格式”。

11.12 任务级 MCP Overlay

Connected Apps 等动态能力只应活在本次 Task。Server 在 Claim 时生成 Overlay,并由 mcp_overlay.go 与 Agent 静态配置做浅合并。

合并契约:

  • 只在 mcpServers 内按 Server Name 合并;
  • 名称冲突时 Overlay 胜出;
  • Agent 其他顶层字段原样保留;
  • Overlay 不得偷偷引入其他顶层键;
  • Overlay 非法时返回错误,同时不静默丢掉原 Agent 配置;
  • 空值和 JSON null 都按“无托管配置”处理。

Overlay 胜出是必要的:动态 Session URL/Token 比管理员留下的静态占位配置更新、更具体。

11.13 Connected App 的真实授权主体

Claim 会把 Connected App 的名称与能力摘要写进 Brief,但真正工具连接由 Server 在任务级构造。

当前实现要分清两条链:

  • Task Initiator 决定“谁发起了这个请求”,用于审计、Prompt 归因和入口权限判断;
  • Agent Owner / Runtime Owner 决定“运行时可使用谁的已连接应用”。

因此,同事 @ 一个共享 Agent 并不会借出自己的 Gmail/Calendar 连接;Agent 使用 Owner 已建立且允许的连接。源码与相关测试把 Owner Scope 作为当前行为,不能仅凭 Initiator 字段推断凭据切换。

11.14 Composio Overlay

Composio 客户端实现位于 server/pkg/composio;工作区集成 Handler 在 integrations_composio.go

大体流程:

  1. 用户为 Toolkit 建立 Connected Account;
  2. Workspace/Agent 允许该 Toolkit;
  3. Task Claim 检查 Agent 与调用入口权限;
  4. Server 为 Owner 创建短期 MCP Session;
  5. Session Endpoint 进入任务 Overlay;
  6. Daemon 只把 Overlay 注入当前 Provider 环境;
  7. Task 结束后不把动态 Session 写回 Agent 静态配置。

Brief 只展示应用元数据,不嵌入秘密 Token。

11.15 Runtime MCP 与浏览器能力

Daemon 还会按 Runtime/Provider 叠加机器能力,相关入口包括:

这类配置描述本机浏览器、Provider 支持或 Runtime Profile 的能力。它与用户 Connected App 不同:前者属于执行节点,后者属于用户授权。

最终配置需要同时满足 Provider Capability、Agent Allowlist、Task Overlay 和 Runtime 可用性。

11.16 能力装配顺序

注意:Skill 文件和 MCP 配置走不同路径。一个 Skill 可以指导 Agent 调某个 CLI,但不会自动授予 MCP 凭据;一个 MCP Server 可调用,也不代表 Agent 知道何时正确使用,除非 Brief/Skill 提供语义。

11.17 Provider 差异

能力交付常见差异:

  • 支持原生 Skills 的 Provider 可直接发现目录;
  • 不支持者需要把 Skill 摘要写入 Brief;
  • Codex 使用隔离的 Codex Home 和配置层;
  • Cursor/OpenCode/OpenClaw 有各自 Sidecar Schema;
  • ACP Provider 可能在 session/new 时声明 MCP Servers;
  • 部分 Provider 不支持某类 MCP Transport。

所以新 Provider 不能只实现消息解析,还要回答:

  1. 它从哪里读系统说明?
  2. 它怎样发现 Skills?
  3. 它怎样接收 MCP?
  4. 它是否会读到用户全局能力,从而绕过禁用策略?

11.18 安全边界

能力注入遵循六条底线:

  1. 动态凭据不写回数据库中的 Agent 静态 JSON;
  2. 不把 Daemon 长期 Token 交给 Agent;
  3. Task Overlay 只对本次 Run 可见;
  4. 路径、大小、文件数、摘要全部校验;
  5. Agent 自定义参数不能覆盖 Multica 管理的 MCP/协议参数;
  6. Brief 中只列能力说明,不打印秘密。

即便 Provider CLI 能执行任意代码,也应让“它获得什么身份”保持最小化和可审计。

11.19 缓存一致性与更新

Bundle Hash 是不可变版本键。修改 Skill 后:

  • Server 产生新 Hash;
  • 新 Claim 携带新 Ref;
  • Daemon Cache Miss 并下载新 Bundle;
  • 老任务仍使用旧快照;
  • GC 可清理不再引用的缓存版本。

这比“按 Skill ID 覆盖当前目录”可靠:并发中的两个 Task 不会互相换掉说明文件。

11.20 排障顺序

当 Agent 说“找不到某能力”时,按层排查:

  1. Skill/Connected App 是否绑定到正确 Agent;
  2. Claim Payload 是否含 Skill Ref、Connected App、MCP;
  3. Daemon 是否成功 Resolve 与校验 Bundle;
  4. 执行目录是否物化 Skill;
  5. 禁用策略是否隐藏了 Runtime Skill;
  6. 合并后的 MCP 是否保留静态 Server;
  7. Provider Sidecar/Session 参数是否生成;
  8. Provider 是否声明支持对应 Transport;
  9. Brief 是否告诉 Agent 何时用它。

直接从 UI 猜通常会漏掉 Claim 快照与 Provider 交付两层。

11.21 本章结论

Multica 的能力系统不是把工具列表塞给模型,而是:

  • 用 Skill 组织可复用工作方法;
  • 用内容寻址 Bundle 安全分发;
  • 用 Runtime-local Discovery 尊重本机生态;
  • 用 MCP 统一工具入口;
  • 用 Connected App 管理用户授权;
  • 用任务级快照把能力锁定在一次可审计运行里。

这套结构的价值,是让能力可组合,但身份、版本和生命周期不混在一起。