跳转至

v0.2.1 适配指南:SKILL 通道 + 通用二进制传输

协议版本:0.2.0 → 0.2.1 目标读者:python-sdk / rust-sdk 实现工程师 发布节奏加性升级 + 一处条件性行为变更——SKILL / 通用二进制传输为全新通道(greenfield,无迁移);client:tool_call 仅在二进制内容超内联预算时行为改变(小结果线路兼容)

兼容性边界(先读这条)

  • 纯文本 / 小二进制(≤ 内联预算)线路完全兼容:旧 SDK 不实现新通道也能继续跑原有 tool_call / 配置 / desktop 流程
  • 大二进制会"静默截断"旧 Agent:若工具返回 / SKILL 资源含超预算二进制,未适配的 Agent SDK 会拿到空 data/blob(句柄在 _meta 里被忽略)。所以大二进制正确性要求 Agent + Computer SDK 同步升级
  • SKILL 通道是新增能力,不实现则该能力缺失,但不影响存量功能

Greenfield 阅读提示

本指南假设 SDK 已实现 0.2.0。SKILL / blob-transfer 两章按 Greenfield 实现规范读(无旧形式可迁移);client:tool_call 一章是对既有事件结果处理的增量改造,重点章节。


1. 摘要

0.2.1 引入两条全新通道并把它们与既有 tool_call 在二进制传递上统一契约

  1. SKILL 通道(全新):Agent 发现并使用 Computer 纳管的 SKILL 包。新事件 client:get_skills / client:get_skill,更新通知 server:update_skills / notify:update_skills。多 source(mcp: / marketplace: / user),合成 name 命名空间,包根沙箱。
  2. 通用二进制传输(全新):client:get_blob + 不透明无状态 blob_handle生产者-消费者模型。任何通道把大/二进制内容以句柄旁路,Agent 经统一 client:get_blob 分块拉取,sha256 自证完整性。
  3. get_skill hybrid:文本且 ≤ 内联预算 → 内联 body;二进制 / 过大文本 → blob_handle
  4. tool_call 二进制一致性唯一触及既有事件):CallToolResult 超预算二进制 content item 清空内联、经 _meta.a2c_blob_handle同一 blob-transfer 契约。
  5. 错误码扩展:新增 4016 / 4017 / 40184014 复用于 SKILL name 未注册;SKILL 通道不使用 4015

核心改动一句话版

Computer 端新增 SKILL 纳管 + 一个通用字节泵;Agent 端新增 SKILL 发现/读取 + 一个"见到 blob_handle 就分块拉取并校验 sha256"的统一消费例程,并把它接到 get_skilltool_call 两处。


2. 新增事件常量

SDK 的事件常量模块(python-sdk a2c_smcp/smcp.py、rust-sdk crates/smcp/src/lib.rsMUST 增补:

常量(建议名) 事件名 方向 必须实现方
GET_SKILLS_EVENT client:get_skills Agent→Server→Computer Computer 处理 / Server 路由
GET_SKILL_EVENT client:get_skill Agent→Server→Computer Computer 处理 / Server 路由
GET_BLOB_EVENT client:get_blob Agent→Server→Computer Computer 处理 / Server 路由
UPDATE_SKILLS_EVENT server:update_skills Computer→Server Server 处理
UPDATE_SKILLS_NOTIFICATION notify:update_skills Server→房间广播 Agent 接收

server:update_skills / notify:update_skills 复用 UpdateComputerConfigReq{computer: str}),与 server:update_config 同构——无需新结构。

Server 若按事件白名单路由,需把上述 client:* 加入白名单;若已是通用 client:* 转发则零改动。Server 重组 blob,按 computer 逐条 ack 转发即可。


3. 新增数据结构

完整定义见 数据结构;此处给 SDK 落型摘要(字段语义以 spec 为准)。

SKILL

  • A2CSkillRefname(必选,合成全局唯一,跨工具对齐裸名:marketplace <plugin>:<skill> / user 裸名 / mcp mcp:<server>:<skill>)/ source(必选,完整 provenance)/ uri?(仅 MCP)/ path必选,Computer 本地绝对目录——staging 落盘是所有 source 统一第一步,恒存在)/ description(必选)/ license? compatibility? allowed_tools? skill_metadata?(frontmatter 派生)/ version?非 frontmatter:marketplace 取 plugin.json、mcp 取 _meta、user 缺省)。mcp_server 字段(source-agnostic,勿臆造)。
  • GetSkillsReqagent, req_id, computerGetSkillsRetskills: list[A2CSkillRef], req_id(排除孤儿,不排序去重)。
  • GetSkillReqagent, req_id, computer, name, rel_path?(POSIX 相对路径,缺省 "SKILL.md")。注意:无 chunk 字段——分块属 blob-transfer,不在 get_skill。
  • GetSkillRetname, rel_path, mime_type, total_size, sha256,外加 body?blob_handle? 恰一存在req_id

通用二进制传输

  • BlobHandle = str:不透明、Computer 铸造、无状态可重解析(无 session / 无 TTL)。Agent MUST NOT 解析/拼接/伪造/跨 Computer 复用。
  • GetBlobReqagent, req_id, computer, blob_handle, chunk_offset?(资源字节绝对偏移,缺省 0), max_chunk_bytes?(客户建议,Computer clamp)。
  • GetBlobRetblob_handle, mime_type, total_size, sha256, chunk_offset, eof, blob(base64 本块字节), req_id

「资源字节」基准——三处口径必须一致

total_size / chunk_offset / sha256 一律基于 Agent 最终消费的资源字节:SKILL.md → frontmatter 剥离后 body;其它文件 → 原始字节;占位符 $TFROBOT_* 不展开(展开是 Agent SDK prompt 渲染层职责)。SDK 计算/校验 sha256 必须用同一口径,否则跨端校验恒失败。


4. SKILL 通道适配

4.1 Computer SDK(生产方,工作量最大)

  1. Skill Registry:把 mcp: / marketplace: / user 多 source 物化(staging 落盘)到统一本地安装目录;每包符合 marketplace SKILL v1 §2(SKILL.md 必存在,包根名 = frontmatter name)。
  2. name 合成与 lexer:分形态——marketplace <plugin>:<skill>(2 段)/ user 裸 <skill>(1 段)/ mcp mcp:<server>:<skill>(3 段);按段数 + mcp: 字面消歧(lexer 见 spec §1.4)。MCP server 段规范化 = Claude Code 通用规则([^a-zA-Z0-9_-]→_),不实现 claude.ai 特例。校验失败的 SKILL 不进 Registry(记 ERROR,不向 Agent 硬报错)。
  3. client:get_skills:从 Registry 读可用项,排除孤儿(来源断开),按发现序返回,不读 body
  4. client:get_skill:name lexer 失败 → 4016;name 不在 Registry → 复用 4014rel_path safe_joinrealpath 必须仍在包根内,否则 4017reasontraversal/forbidden/not_found);total_size 超 SDK 可配上限 → 4017 too_large(不铸句柄);仅 SKILL.md 剥 frontmatter,其它原样;文本且 ≤ 内联预算 → body,否则铸 blob_handle
  5. 变更检测 → server:update_skills:MCP ResourceListChanged/Updated(skill://)、Marketplace git 源重拉对账、User DropIn / SDK 管理 UX 任一变更触发;探测机制 SDK 自决。
  6. 安全铁律:包根绝对路径由 Registry 经 name 解析,禁止从 name/rel_path 推导;.skillenv 等敏感文件任何 rel_path 下都不可读出(命中即 4017 forbidden,不泄漏存在性)。

4.2 Agent SDK(消费方)

  1. notify:update_skills → 自动 client:get_skills 刷新清单(建议)。
  2. client:get_skill(name[, rel_path]):响应分支 body(直接用)vs blob_handle(转 §5 拉取例程)。
  3. name不透明可比较字符串,原样取自 A2CSkillRef.name不自行拼接(否则 4016)。
  4. 占位符展开($TFROBOT_*)是 Agent SDK 在 prompt 渲染层的职责,不是 Computer 协议层职责。

4.3 Server SDK

路由三个新 client:*;收 server:update_skills 向房间广播 notify:update_skills(复用既有 update 广播机制)。


5. 通用二进制传输适配(最高复用价值章节)

client:get_blob所有大/二进制内容的唯一搬运通道,被 get_skilltool_call 共用。实现一次,两处复用。

5.1 Agent SDK:统一拉取例程(务必抽成单一可复用函数)

行为契约(Python reference impl;rust-sdk 按同一契约实现,协议文档不堆多语言示例):

def drain_blob(call, computer: str, blob_handle: str) -> tuple[bytes, str]:
    """call = 已封装的 emit-with-ack(self.call / emit_with_ack)。返回 (完整字节, mime_type)。"""
    buf = bytearray()
    offset = 0
    expect_sha = expect_total = mime = None
    while True:
        ret = call("client:get_blob", {
            "agent": ..., "req_id": new_req_id(), "computer": computer,
            "blob_handle": blob_handle, "chunk_offset": offset,
        })
        # 4018 → 见 §7:invalid_handle/forbidden 不重试;gone 回生产者重取;range 修正 offset
        if expect_sha is None:
            expect_sha, expect_total, mime = ret["sha256"], ret["total_size"], ret["mime_type"]
        elif ret["sha256"] != expect_sha or ret["total_size"] != expect_total:
            return drain_blob(call, computer, blob_handle)  # 源被改写:从 0 重读
        buf += base64.b64decode(ret["blob"])
        offset = ret["chunk_offset"] + len(base64.b64decode(ret["blob"]))
        if ret["eof"]:
            break
    if hashlib.sha256(buf).hexdigest() != expect_sha:
        raise IntegrityError  # 损坏,重读
    return bytes(buf), mime

要点:chunk_offset 绝对偏移、无服务端状态 → 幂等/可续传/可并行;背压由 pull 节奏天然提供;eofoffset+本块 == total_size;空资源 = 单响应 total_size=0/eof=true

5.2 Computer SDK:句柄解析

  • 铸造:不透明、无状态、可确定性回源;绝不编码可被 Agent 利用越权的明文路径。
  • 解析(每次 get_blob):重跑铸造通道的边界校验(SKILL → §4.1 沙箱),防御纵深;失败 → 4018 forbidden;源消失 → gonechunk_offset 越界 → range;句柄无法识别 → invalid_handle
  • 单块 ≤ Server maxHttpBufferSize(计入 base64 +33% 与 envelope);max_chunk_bytes 缺省时 Computer 自定。
  • client:get_blob 不是任意文件读原语

6. client:tool_call 二进制一致性适配 ⚠️(唯一触及既有事件)

CallToolResultMCP 标准结构,schema 不可变。0.2.1 起:

  • 逐个二进制 content item(ImageContent / AudioContent / EmbeddedResource 的 blob)按与 get_skill 同一内联预算判定
  • ≤ 预算:维持原生内联 base64,行为不变(小截图零额外往返)
  • > 预算:清空该 item 的内联 data/blob,在该 item 的 _metaa2c_blob_handle,并镜像 a2c_total_size / a2c_sha256(MIME 复用 item 既有 mimeType
  • 工具自身失败仍走 MCP CallToolResult.isError引入 A2C 事件级错误码——既有不变量)

载体差异因 MCP 结构不可变所致(与 SMCPTool.metaa2c_tool_meta 命名空间旁路同构);除位置外,句柄语义/拉取/校验/错误与 get_skill 完全一致

Computer SDK 改造:tool_call 返回前,遍历 CallToolResult.content,对超预算二进制 item 执行上述替换(复用 §5.2 铸造)。

Agent SDK 改造(破坏性关键点):消费 CallToolResult必须遍历 content item 检测 _meta.a2c_blob_handle,命中则调 §5.1 drain_blob 还原字节后再交付上层。不实现 = 大二进制工具结果静默变空


7. 错误码适配

事件 触发 Agent 行为
4016 client:get_skill name 格式非法(lexer 失败) 不重试;原样用 A2CSkillRef.name
4017 client:get_skill 铸造期:details.reasontraversal/forbidden/not_found/too_large(+rel_path/total_size traversal/forbidden/too_large 不重试;not_found 回退仅用 SKILL.md
4018 client:get_blob 拉取期:details.reasoninvalid_handle/forbidden/gone/range invalid_handle/forbidden 不重试;gone 回生产者重取句柄;range 修偏移重试
4014(复用) client:get_skill name 合法但不在 Registry(不存在/卸载/孤儿) 刷新清单后重试
  • SKILL 通道不使用 4015(未声明 resources capability 的 server 在物化阶段已排除)。
  • 沿用既有扁平 ErrorPayload shape:code/message 顶层,code-specific 字段顶层,诊断在 detailsdetails MUST NOT 透传给最终用户。
  • details.reason开放枚举,SDK 解析 MUST 容忍未来新增值(默认按"不重试 + 诊断"兜底)。

8. 兼容性与发布建议

  • 版本语义:0.2.0 → 0.2.1(v0.x PATCH)。新通道纯加性;tool_call 仅大二进制路径行为变更。
  • 同步升级要求:要让大二进制(工具截图 / SKILL 二进制资源)正确,Agent SDK 与 Computer SDK MUST 同步升级;只升一端 → 大二进制空数据。纯文本/小二进制可不同步。
  • Server SDK 只需事件路由 + 广播,向后兼容,建议先行升级。
  • 建议发布顺序:Server → Computer → Agent;三方均升后大二进制能力方完整。

9. 适配检查清单

Server SDK

  • 事件常量增补 5 项;client:get_skills/get_skill/get_blob 路由;server:update_skillsnotify:update_skills 广播

Computer SDK

  • Skill Registry + 多 source staging(marketplace SKILL v1 §2 包结构)
  • name 合成 / lexer / 规范化(不实现 claude.ai 特例);校验失败不进 Registry
  • get_skills(排除孤儿)/ get_skill(4016/4017/4014、frontmatter 仅 SKILL.md、inline-vs-handle、too_large)
  • 变更检测三源 → server:update_skills
  • get_blob:句柄解析 + 重施沙箱 + 切片 + 4018;单块 ≤ Server buffer
  • tool_call:超预算二进制 content item → _meta.a2c_blob_handle(+a2c_total_size/a2c_sha256)
  • 安全:包根只经 Registry 解析;.skillenv 任何 rel_path 不可读

Agent SDK

  • 单一 drain_blob 例程(offset 循环 / eof / sha256 校验 / total_size 变更重读 / 4018 处理)
  • get_skill 分支 body vs blob_handle→drain
  • tool_call 消费遍历 content _meta.a2c_blob_handle→drain(破坏性关键点)
  • notify:update_skills → 重拉 get_skills
  • 4016/4017/4018 解析;details.reason 开放枚举兜底;details 不外泄
  • 占位符展开在 prompt 渲染层(不在协议层)

参考