v0.2 协议升级指南¶
协议版本:0.1.x → 0.2.0 目标读者:python-sdk / rust-sdk 实现工程师 发布节奏:破坏性升级,需同步发布三方(Agent / Computer / MCP Server helper)
Greenfield 阅读提示
本指南默认 SDK 已实现 v0.1 完整功能并据此描述"迁移路径"。如果你所维护的 SDK 从未实现某个子系统(典型如 DPE / Window URI 解析器),那么相关章节应按 Greenfield 实现规范阅读:
- 关于"旧形式"的描述可作为反面参考(避免按 v0.1 模式实现)
- 关于"新形式"和"行为约束"是 SDK 必须实现的目标
- "迁移步骤"中提到的"修改 X 文件"对未实现的 SDK 来说是"新建 X 文件"
1. 摘要¶
v0.2 是一次破坏性协议升级,包含四组变更:
- URI 纯标识符化:
window://与dpe://都变成纯标识符,不再携带元数据 query;元数据下沉到 MCPResource.annotations+Resource._meta - DPE 协议从 Finder 中独立:v0.1 把"DPE 抽象"和"文档目录浏览器(Finder)"耦合在一起;v0.2 把 Finder 从协议范围整体移除,只保留 DPE 文档抽象作为底层原语
- DPE 内容投递重构:DPE 内容不再走 Socket.IO——Computer 端业务层注册 DPE Resolver hook,把 DPE Resource 转成 Agent 可访问的 URI(对象存储 / 本地文件 / 任意 scheme),Agent 用应用层协议(HTTP / file / ...)自取
- 协议版本握手:引入 Socket.IO connect 阶段的版本校验机制(详见 versioning.md)
- MCP 上游授权错误码:新增
4006/4007/4008/4011/4012/4013
核心改动一句话版¶
- Window/DPE URI 变成纯标识符(不再携带元数据 query)
- 元数据下沉到 MCP
Resource.annotations(标准字段:priority/audience/lastModified)+Resource._meta(A2C 扩展:fullscreen/keywords/file_type/page_count/file_uri) - Finder 整体从协议移除——
client:list_finder/server:update_finder/notify:update_finder/ListFinderReq/DPEDocumentSummary/organize_finder全部删除;文档发现/检索/聚合视图未来作为内置 MCP Server 重做,与协议核心解耦 - 新增
client:get_dpe:Agent 把 DPE URI 提交给 Computer,Computer 调本地业务 Resolver hook 转成访问 URI 返回;Resolver 实现完全交给业务方(上传 OSS / 落本地缓存 / 任意 scheme),协议不规定生命周期/可用性 4'. 新增client:get_resources:透明转发 MCP 标准resources/list给 Agent(含 cursor 翻页);mcp_server必填,Computer 不做协议级过滤;与client:get_config组合实现 Agent 端的 dpe / window / 业务自定义资源发现,替代 v0.1 Finder 聚合视图 - 新增 Socket.IO 连接必带 URL query
?a2c_version=0.2.0;Server 在 HTTP 中间件层校验,不兼容时返回 HTTP 400 +4008 - 元数据字段分工原则:MCP 标准
annotations(priority/audience/lastModified)MUST 放入annotations;A2C 自定义字段(fullscreen/ DPE 业务字段)放入_meta。Windowaudience=["assistant"]、DPEaudience=["user"]
2. 受影响范围¶
2.1 代码影响矩阵¶
Python SDK 路径基于
python-sdk仓库实际现状;Rust SDK 列仅作示意——rust-sdk实际目录结构由 rust-sdk 实现方维护,本文档不锚定具体路径,仅描述需要修改/新建的职能模块。
| 职能模块 | Python SDK 路径 | Rust SDK | 说明 |
|---|---|---|---|
| WindowURI 解析器 | a2c_smcp/computer/utils/window_uri.py |
rust-sdk 决定 | 移除 priority / fullscreen query 解析 |
| DPE URI 解析器 | a2c_smcp/computer/utils/dpe_uri.py |
rust-sdk 决定(v0.2 待新建) | URI 纯标识符化(不解析 query);拒绝 dpe://host 形式(无 doc-ref) |
| organize_desktop | a2c_smcp/computer/desktop/organize.py |
rust-sdk 决定 | 从 Resource.annotations.priority 读优先级(float [0,1]);Resource._meta.fullscreen 读全屏标记 |
| DPE Resolver Hook(新增) | a2c_smcp/computer/dpe/resolver.py |
rust-sdk 决定(v0.2 待新建) | Computer 业务层注册接口;把 DPE URI + ResourceContents 转成访问 URI |
| MCP Manager | a2c_smcp/computer/mcp_clients/manager.py |
rust-sdk 决定 | 维护 MCP Server 名称索引(client:get_resources 路由)+ host → MCP Server 反查索引(client:get_dpe 路由);host 注册时检测冲突 + 记 WARN;运行时按 first-registered-wins |
| Socket.IO Client(Agent 侧) | a2c_smcp/agent/socketio/client.py |
rust-sdk 决定 | 新增 get_dpe / get_resources 方法;移除 list_finder |
| Socket.IO Client(Computer 侧) | a2c_smcp/computer/socketio/client.py |
rust-sdk 决定 | 新增 on_get_dpe / on_get_resources 处理器;移除 on_list_finder / update_finder |
| 数据类型 | a2c_smcp/smcp.py |
rust-sdk 决定 | 新增 GetDPEReq / GetDPERet / GetResourcesReq / GetResourcesRet;删除 ListFinderReq / ListFinderRet / DPEDocumentSummary / DPEPageSummary / DPEElementDetail |
| 错误码枚举 | a2c_smcp/smcp.py::ErrorCode |
rust-sdk 决定 | 新增 4006 / 4007 / 4008 / 4011 / 4012 / 4013 |
| 协议版本常量 | a2c_smcp/__init__.py::PROTOCOL_VERSION |
rust-sdk 决定 | 新增导出;SDK 包版本 MAJOR.MINOR MUST 锁定协议 MAJOR.MINOR(见 versioning.md SDK 仓库) |
| Agent/Computer 连接逻辑 | a2c_smcp/agent/socketio/client.py、a2c_smcp/computer/socketio/client.py |
rust-sdk 决定 | 连接时 URL 拼 ?a2c_version=...,auth 默认仅传 role(业务字段由用户回调注入,详见 data-structures.md ConnectAuth);解析 HTTP 400 body 识别 4008 → 主动 disconnect() → 抛 ProtocolVersionError |
| Server 连接处理 | a2c_smcp/server/ 的 HTTP 中间件 + connect handler |
rust-sdk 决定(socketioxide 默认 HTTP 路径 /socket.io/) |
在 Socket.IO handler 之前于 HTTP 层校验 a2c_version;connect handler 处理 auth.role;server:list_room 返回含版本 |
| 测试 | tests/unit_tests/computer/、tests/integration_tests/ |
rust-sdk 决定 | 所有涉及 URI query 解析、Finder 事件、keywords 参数的测试要重写或删除;新增 get_dpe / Resolver / 版本握手矩阵测试 |
2.2 非代码影响¶
- MCP Server 示例仓库(若存在)需要更新
- 文档站点:
docs/specification/、docs/guides/、docs/appendix/faq.md已更新 - 示例 / quickstart:需要按新模式改写
2.3 不变的部分(明确告知,避免过度改动)¶
- 事件名(Desktop):
client:get_desktop/server:update_desktop/notify:update_desktop保持不变 - Window URI 与 Desktop 行为:URI 形态、组织算法不变;仅元数据声明位置从 URI query 迁移到
Resource.annotations/_meta - Room 模型 / Server 路由:完全不变
- 安全模型(零凭证传播、房间隔离):完全不变
3. 破坏性变更详表¶
| # | 变更点 | 旧形式 | 新形式 | 兼容策略 |
|---|---|---|---|---|
| B1 | Window URI query 移除 | window://host/path?priority=80&fullscreen=true |
window://host/path |
硬切换;旧 URI 解析时丢弃 query 并记录 WARN |
| B2 | Window 元数据位置 | URI query | Resource.annotations.priority(float [0,1])+ Resource._meta.fullscreen + Resource.annotations.audience=["assistant"] |
硬切换;priority 语义由 int [0,100] 迁移到 MCP 标准 float [0,1],audience 固定 ["assistant"] |
| B3 | DPE URI 收缩到 Document 层 | dpe://host/doc?format=md、dpe://host/doc/pages/N、dpe://host/doc/elements/ID |
dpe://host/doc-ref(doc-ref 可单段或分段路径) |
硬切换;DPE URI 纯文档标识符化——sub-path(pages/N、elements/ID)与所有 content-control query(format / depth / offset / limit / categories)全部移除;Page / Element 由 DPE 内容 JSON 在 Document 内部表达 |
| B4 | DPE 文档元数据位置 | Level 0/1 response body 中的扁平字段 | Resource._meta.{keywords, file_type, page_count, file_uri} + Resource.annotations.lastModified + Resource.annotations.audience=["user"] |
硬切换;audience 固定 ["user"](DPE 文档是用户数据资产) |
| B5 | Finder 整体从协议移除 | client:list_finder / server:update_finder / notify:update_finder / ListFinderReq / ListFinderRet / DPEDocumentSummary / DPEPageSummary / DPEElementDetail / organize_finder |
不再存在 | 硬删除;文档发现/检索未来作为内置 MCP Server 重做,与协议核心解耦 |
| B6 | 新增 client:get_dpe |
无(v0.1 通过 list_finder + MCP resources/read 直接返回内容) |
client:get_dpe(uri) → GetDPERet(uri=访问URI);DPE 内容不走 Socket.IO,由业务 Resolver hook 转成访问 URI(HTTP/file/任意 scheme),Agent 应用层自取 |
新增;SDK 必须实现该事件;Computer 必须暴露 Resolver 注册 API |
| B6' | 新增 client:get_resources |
无 | client:get_resources(mcp_server, cursor?) → {resources, next_cursor};透明转发 MCP 标准 resources/list,含 cursor 翻页;不做协议级过滤;为 Agent 提供资源发现入口(替代 v0.1 Finder 聚合视图) |
新增;Computer 必须实现透明转发;mcp_server 必填,不存在返回 404 |
| B7 | host 唯一性 | MUST(v0.1 后期加,跨 Server 强制唯一) | SHOULD 跨 MCP Server 唯一(非 MUST);Computer 注册时检测重复 + 记 WARN(不阻塞注册);运行时 client:get_dpe 路由按 first-registered-wins |
从硬约束放宽为软约束;旧实现不受影响 |
| B8 | Socket.IO 连接握手 | 无版本校验,任意客户端可连接 | 必须在 URL query 携带 ?a2c_version=...;Server HTTP 中间件层校验,不兼容返回 HTTP 400 + 4008 |
硬切换;旧客户端未带 a2c_version 被拒(HTTP 400) |
| N1 | MCP 上游授权错误码 | 泛化为 4003 |
4006 / 4007(独立码) |
新增;旧 Computer 返回 4003 在 Agent 侧仍可工作但体验降级 |
| N2 | DPE 资源访问错误码 | 无 | 4011 DPE Resolver Not Configured / 4012 Invalid DPE URI / 4013 DPE Resolution Failed |
新增;为 client:get_dpe 路径提供专属错误语义 |
| N3 | SessionInfo 字段扩展 | {sid, name, role, office_id} |
新增 a2c_version |
新增字段,对老客户端向后兼容(可忽略不识别字段) |
4. 详细变更规范¶
4.1 Window URI 重构¶
URI 语法¶
校验规则¶
- 解析时若 URI 含 query 部分,MUST 丢弃并记录
WARNING级日志 path段继续支持 URL 编码(c%2Fd保持为单段)host跨 MCP Server SHOULD 唯一(非 MUST);Computer 注册重复时记 WARN,不阻塞注册
元数据声明(由 MCP Server 在 resources/list 响应中设置)¶
| 字段 | 类型 | 必需 | 默认 | 说明 |
|---|---|---|---|---|
Resource.annotations.priority |
float [0, 1] |
否 | 0.0 |
MCP 标准;布局排序依据,值越大越优先,仅同一 Server 内比较 |
Resource.annotations.audience |
["assistant"] |
推荐 | — | MCP 标准;Window 面向 Agent 渲染,MCP Server 应显式声明 ["assistant"] |
Resource.annotations.lastModified |
ISO 8601 | 否 | — | MCP 标准;v0.2 Computer 透传不排序 |
Resource._meta.fullscreen |
bool |
否 | false |
A2C 扩展;全屏渲染标记 |
organize_desktop 行为变更¶
- 输入:
windows: list[tuple[SERVER_NAME, Resource, ReadResourceResult]](Resource 对象必须完整携带annotations与_meta) - 排序键改为
Resource.annotations.priority(float),fullscreen 标记改读Resource._meta.fullscreen - 不再解析 URI query
4.2 DPE URI 重构¶
URI 语法¶
// 旧(多层 URI + content-control query)
dpe://host/doc-ref?format=markdown&offset=0&limit=20
dpe://host/doc-ref/pages/N?categories=table
dpe://host/doc-ref/elements/ID
// 新(纯文档标识符,只到 Document 一层;doc-ref 可分段路径)
dpe://host/doc-ref
dpe://host/reports/2026/annual # 分段路径示例
dpe://host/src/main/java/Foo.java # 含扩展名示例
解析器行为¶
- 拒绝
dpe://host形式(无 doc-ref);返回解析失败 /4012 - doc-ref 可分段:从 host 之后第一个
/起到 URI 结尾的整段 path,整体作为文档唯一标识;不再有 sub-path(pages/N、elements/ID)概念——Page / Element 在 DPE 内容 JSON 内部表达 - 拒绝 query / fragment:解析时检测到则记录 WARN 并丢弃;Agent SDK 构造 URI 时不应附加 query 或 fragment
host跨 MCP Server SHOULD 唯一(非 MUST)——client:get_dpe通过 URI 中 host 反查 Server,重复时记 WARN + 先注册优先
文档元数据声明(替代旧的 URI query / Level 0 body)¶
每个文档作为一个 Resource 出现在 resources/list 响应中:
Resource(
uri="dpe://com.example.docs/rpt-2026",
name="2026 年度报告", # = title
description="2026 年度财务与运营报告", # = summary
mimeType="application/json",
annotations=Annotations(
audience=["user"], # DPE 文档是用户数据资产,固定 ["user"]
lastModified="2026-01-15T08:30:00Z",
),
_meta={
"keywords": ["财务", "年报"],
"file_type": "xlsx",
"page_count": 12, # 推荐:Agent 据此规划翻页
"file_uri": "file:///data/reports/2026-annual.xlsx",
},
)
不再有 v0.1 的 Level 0/聚合端点¶
dpe://host 形式(v0.1 用作 catalog 端点)整体移除。文档发现走 MCP 标准 resources/list 或未来的 Finder MCP Server。
详见 DPE 文档协议。
4.3 client:get_dpe 与 DPE Resolver Hook(新增)¶
设计动机¶
v0.1 的 DPE 内容通过 MCP resources/read 直接经 Socket.IO 返回给 Agent。对大体量文档(Excel / PDF / PPT 数十 MB)这是不可行的——Socket.IO 默认 maxHttpBufferSize 1MB;即便调大,单消息阻塞通道、Server 全量缓冲等问题都会暴露。
v0.2 把 DPE 内容投递与 A2C 传输层解耦:
Agent → client:get_dpe(uri) → Computer → DPE Resolver Hook → 访问 URI
│
▼
业务存储(OSS / 本地 / etc)
│
Agent ← 应用层协议(HTTP / file / ...)拉取 ←
A2C 协议本身不承载 DPE 内容;URI 的生命周期、可用性、签名机制全部交给业务方。
新增事件¶
| 事件常量 | 事件名称 | 数据结构 |
|---|---|---|
GET_DPE_EVENT |
client:get_dpe |
GetDPEReq → GetDPERet |
class GetDPEReq(AgentCallData, total=True):
agent: str
req_id: str
computer: str
uri: str # dpe://host/doc-ref(doc-ref 可单段或分段路径)
timeout: NotRequired[int]
class GetDPERet(TypedDict, total=False):
uri: str # 业务 Resolver 输出的访问 URI
mime_type: NotRequired[str]
size: NotRequired[int]
req_id: str
Computer 必须实现 DPE Resolver 注册 API¶
class DPEResolver(Protocol):
async def resolve(
self,
dpe_uri: str,
contents: list[ResourceContents], # Computer 已从 MCP Server 读到的内容
hint: ResolverHint, # mcp_server_name / mime_type 等上下文
) -> ResolvedResource: ...
# Computer SDK 启动时业务方注册
computer.register_dpe_resolver(MyOSSResolver())
未注册时收到 client:get_dpe 必须返回 4011 DPE Resolver Not Configured——禁止降级到 inline 透传 ResourceContents。
Computer 处理流程¶
- 接收
client:get_dpe(uri=dpe://...) - 校验 URI(dpe scheme + 非空 host + 非空 doc-ref + 无 query/fragment);非法返回
4012 - 检查 Resolver 是否注册;未注册返回
4011 - 解析 host → 定位对应 MCP Server
- 调 MCP Server 的
resources/read(uri)拿 ResourceContents - 调 Resolver
resolve(uri, contents, hint)拿访问 URI - 返回
GetDPERet(uri=访问URI, ...) - 任一步失败 → 返回
4013 DPE Resolution Failed(含 reason)
详见 DPE 文档协议。
4.4 MCP Server host 唯一性(从 MUST 放宽为 SHOULD + Computer WARN)¶
v0.2 起 host 跨 MCP Server SHOULD 唯一(非 MUST)——保留 host 作为路由依据的同时引入软约束 + 显性警告。
路径与路由依据¶
| 协议路径 | 路由依据 | host 角色 |
|---|---|---|
client:get_dpe(uri=dpe://host/...) |
URI 中的 host 反查 MCP Server 索引 | 路由关键字——保持 URI 自包含寻址能力 |
client:get_resources(mcp_server=name) |
mcp_server 字段直接定位 |
不参与路由 |
client:get_desktop |
organize_desktop 按 MCP Server 名称分组 |
仅参与单 Server 内的窗口排序 |
Computer 端实现要求(v0.2 新规约)¶
| 阶段 | 行为 |
|---|---|
| MCP Server 注册时 | Computer MUST 检测新 Server 的 host 是否与已有 Server 冲突;冲突时记 WARN 日志(含两个 Server 名称 + 共用 host);仍允许注册成功(不阻塞业务) |
client:get_dpe 路由时 |
(a) host 唯一匹配 → 路由到该 Server;(b) host 多个匹配 → first-registered-wins + WARN;(c) host 无匹配 → 返回 4013 DPE Resolution Failed |
设计取向¶
- 保留 URI 自包含寻址:Agent 持有任意 dpe URI 即可调
get_dpe,不需要外部元信息 - 软约束 + 显性 WARN:host 重复是 MCP Server 命名 bug;Computer 不阻塞注册(避免硬故障),但通过 WARN 让冲突可见
- first-registered-wins:路由确定性——后注册的同 host 资源在
get_dpe路径上被错误路由到先注册者,这是 visible 信号,促使业务方修复
实践建议¶
- MCP Server 采用反向域名风格(
com.example.<service>)天然回避冲突 - 同一 Computer 部署多个 MCP Server 时统一规划 host 命名空间
- 监控 Computer WARN 日志中的 host 冲突告警
详见 DPE host 路由策略 / Window URI 校验规则。
4.5 错误码新增¶
MCP 上游授权(4006 / 4007)¶
| 代码 | 名称 | 含义 |
|---|---|---|
4006 |
Tool Authorization Required | 工具需要 MCP 上游授权(如 OAuth),Computer 当前无有效凭证 |
4007 |
Tool Authorization Failed | 上游授权流程失败、Token 失效、刷新失败、权限不足 |
详见 error-handling.md 4006/4007 判定决策表。
DPE 资源访问(4011 / 4012 / 4013)¶
| 代码 | 名称 | 含义 |
|---|---|---|
4011 |
DPE Resolver Not Configured | Computer 未注册 DPE Resolver hook |
4012 |
Invalid DPE URI | URI 不符合 dpe:// scheme 规范 |
4013 |
DPE Resolution Failed | Resolver 执行失败(业务异常 / 上游 MCP Server 不可用 / 等) |
详见 error-handling.md DPE 资源访问错误。
4.6 协议版本握手(新增)¶
客户端¶
# python-sdk 内部实现
from a2c_smcp import PROTOCOL_VERSION
await sio.connect(
f"wss://server.example.com?a2c_version={PROTOCOL_VERSION}",
socketio_path="/smcp",
auth={"role": "agent"},
)
Server¶
HTTP 中间件层校验 a2c_version,不兼容返回 HTTP 400 + 4008。详细规范、Python/Rust 实现示例、跨语言通用约束见 versioning.md。
Client SDK 收到 4008 后必须主动 disconnect¶
防止底层库自动重连导致死循环。详见 versioning.md § 4. Client SDK MUST 主动断开连接。
5. 迁移步骤¶
5.1 MCP Server 实现者(最小改动)¶
-
Window:把 URI query 中的
priority/fullscreen改成 Resource 元数据 -
DPE:把所有 query 参数移除;把文档元数据迁到 Resource 上
-
DPE 内容形态自决:A2C 协议不规定
resources/read响应 JSON schema;与 Agent / Resolver 之间约定即可
5.2 Computer SDK 工程师¶
- WindowURI 解析器:移除 query 解析;解析时若有 query,丢弃并记录 WARN
- DPE URI 解析器:拒绝
dpe://host形式(无 doc-ref);拒绝 query 参数 - organize_desktop:从
Resource.annotations.priority读优先级;从Resource._meta.fullscreen读全屏标记 - 删除 organize_finder / Finder 事件处理:v0.2 协议无 Finder
- 新增 DPE Resolver 注册 API:暴露
register_dpe_resolver(resolver)接口 - 新增
client:get_dpe处理器:按 §4.3 处理流程 实现 - MCP Manager:维护两套索引——(a) MCP Server 名称索引(用于
client:get_resources直接定位);(b) host → MCP Server 反查索引(用于client:get_dpe按 URI 中 host 路由);注册时检测 host 重复 + 记 WARN(不阻塞);多匹配时 first-registered-wins - 错误码枚举:新增 4006 / 4007 / 4008 / 4011 / 4012 / 4013
5.3 Agent SDK 工程师¶
- 删除
list_finder方法 - 新增
get_dpe(uri)方法:返回GetDPERet;Agent 业务代码拿到访问 URI 后用应用层协议自取实际内容 - 新增
get_resources(mcp_server, cursor=None)方法:透明转发 MCPresources/list;返回{resources, next_cursor};mcp_server必填 - HTTP / file URI 内置拉取:建议 SDK 内置 http(s):// 与 file:// 的拉取能力;其他 scheme 由用户业务代码处理
- 不缓存 URI 跨 session:拿到 URI 仅在本次会话使用;过期时重新调用
get_dpe - 协议版本握手:连接 URL query 拼
?a2c_version=...;解析 HTTP 400 body 识别 4008 → 主动disconnect()→ 抛ProtocolVersionError - DPE 发现链路:典型路径是
get_config(computer) → 对每个 server name 翻页 get_resources → 按 scheme/元数据自决过滤 → get_dpe
6. 测试矩阵¶
6.1 URI 解析器¶
| 用例 | Window | DPE | 预期 |
|---|---|---|---|
| 纯标识符 URI | window://host/path |
dpe://host/doc-ref |
✅ 解析成功 |
| 带 query | window://host/path?priority=0.8 |
dpe://host/doc-ref?format=md |
✅ 丢弃 query + WARN |
| 多段路径 | window://host/a/b/c |
dpe://host/reports/2026/annual |
✅ 解析成功(doc-ref="reports/2026/annual") |
| URL 编码 | window://host/a%2Fb |
— | ✅ 单段,解码 a/b |
| 缺 host | window:///path |
dpe:///doc-ref |
❌ 解析失败 |
| DPE 缺 doc-ref | — | dpe://host |
❌ 解析失败 / 4012 |
| DPE 含 query | — | dpe://host/doc?format=md |
❌ WARN + 丢弃 query;解析后 doc-ref="doc" |
6.2 organize_desktop(与 v0.1 保持算法一致,仅元数据来源变化)¶
- 优先级排序:从
annotations.priority(float)降序 - 全屏处理:从
_meta.fullscreen,per-Server 处理 - 缺失值同义:
annotations is None与annotations.priority is None等价处理为 0.0
6.3 client:get_dpe + Resolver¶
| 用例 | 预期 |
|---|---|
| 注册 Resolver + 合法 dpe URI | ✅ 返回 GetDPERet(uri=...) |
| 未注册 Resolver | ❌ 4011 DPE Resolver Not Configured |
| 非法 URI(缺 doc-ref / 非 dpe scheme / 含 fragment 等) | ❌ 4012 Invalid DPE URI |
| Resolver 抛异常 | ❌ 4013 DPE Resolution Failed(含 reason) |
上游 MCP Server resources/read 失败 |
❌ 4013 DPE Resolution Failed |
| Resolver 返回 https URL | ✅ Agent 应用层 GET 拿到内容 |
| Resolver 返回 file URL(同机部署) | ✅ Agent 应用层读本地文件 |
6.3.1 client:get_resources(资源发现)¶
| 用例 | 预期 |
|---|---|
| 已注册的 mcp_server + cursor=None | ✅ 返回 {resources, next_cursor?}(透明转发 MCP resources/list) |
| 已注册的 mcp_server + 翻页 cursor | ✅ 拿下一页;末页 next_cursor 缺省 |
| 未注册的 mcp_server | ❌ 404 Not Found(错误信息含 server 名称) |
| Computer 不做协议级过滤 | ✅ 响应中含任意 scheme 的 Resource(dpe / window / 业务自定义),由 Agent 自决过滤 |
| 完整发现流程 | ✅ Agent 用 get_config + 多次 get_resources 翻页 + 自决过滤 → 拿到 dpe URI 列表 → get_dpe 拿访问 URI |
6.4 host 路由 / 唯一性¶
| 用例 | 预期 |
|---|---|
| 两个 MCP Server 声明不同 host | ✅ 注册成功;get_dpe 按 host 反查正确路由 |
| 两个 MCP Server 声明相同 host(同一 Computer) | ⚠️ 注册成功但记 WARN(含两个 Server 名 + 共用 host) |
host 重复时 get_dpe(uri=dpe://repeated-host/...) |
⚠️ 路由到先注册的 Server + 记 WARN;后注册者的同 URI 资源无法被命中 |
get_dpe(uri=dpe://unknown-host/...)(host 不在任何 Server 索引中) |
❌ 4013 DPE Resolution Failed |
| 单 MCP Server 内 URI 重复(MCP 标准已禁) | ❌ 由 MCP resources/list 自身校验拒绝 |
6.5 协议版本握手¶
| 场景 | Client | Server | 预期 |
|---|---|---|---|
| 完全匹配 | 0.2.0 | 0.2.0 | ✅ |
| PATCH 差异 | 0.2.1 | 0.2.0 | ✅ |
| MINOR 差异(v0.x) | 0.2.0 | 0.3.0 | ❌ HTTP 400 + 4008 |
| MAJOR 差异 | 1.0.0 | 0.2.0 | ❌ HTTP 400 + 4008 |
| 缺失 a2c_version | — | 0.2.0 | ❌ HTTP 400 |
| 非法版本号 | "abc" | 0.2.0 | ❌ HTTP 400 |
| 中间件前置性 | connect handler 抛异常 | 0.2.0 | 校验仍正常工作 |
6.6 集成测试¶
- 完整
get_dpe流程:Mock MCP Server 返回dpe://Resource → Agentclient:get_resources发现 → Agentclient:get_dpe解析 → Computer 调 Resolver → Agent 用应用层拉取 - 4008 死循环防御:底层 socketio 自动重连开启时,收到 4008 SDK 必须主动 disconnect
6.7 回归测试重点¶
- Desktop 全链路:list → notify:update_desktop → 重新 list(Window 行为完全不变,仅元数据来源变化)
- 工具调用全链路:未受 v0.2 影响,确保不破坏
7. 兼容性策略¶
7.1 协议层:硬切换¶
v0.1.x 与 v0.2.0 在 URI 形态、Finder 移除、get_dpe 新增等多处无法互通。Server 端通过 HTTP 中间件 a2c_version 校验,不兼容直接拒绝(HTTP 400 + 4008)。
不内建 v0.1 MCP Server fallback¶
v0.2 SDK MUST NOT 内建针对 v0.1 MCP Server 的 URI query fallback 解析。当 v0.2 Computer 遇到只在 URI query 中携带 ?priority= / ?fullscreen= 的 v0.1 MCP Server 时:
- 按 v0.2 校验规则丢弃 URI query 部分(仅 WARN)
- 不读取
_meta/annotations之外的元数据来源 - Desktop 中该窗口降级显示为
priority=0.0, fullscreen=false
理由:保持协议纯粹;用户感知到降级会主动升级 MCP Server,比静默兼容更早暴露问题。
7.2 数据层:Agent 视角¶
- Window 端 Agent 看到的
Desktop字符串列表形态不变 - DPE 端 Agent 拿到的是访问 URI(v0.1 没有这个语义,是新增);具体内容由应用层拉取
7.3 MCP Server 生态¶
- 旧 MCP Server(只输出 URI query 形态)在 v0.2 Computer 下 Desktop MUST 降级为
priority=0.0, fullscreen=false并 WARN - 旧 MCP Server(实现 v0.1 DPE Level 0)在 v0.2 Computer 下,Level 0 响应被忽略(Computer 不再调用)
结论:v0.2 Computer 对旧 MCP Server 宽容(降级运行),但 v0.1 Computer 无法消费 v0.2 MCP Server。升级顺序:Computer 先于 MCP Server。
7.4 建议的升级顺序¶
阶段 1:发布 v0.2 Computer SDK(含 DPE Resolver API + get_dpe + get_resources)
阶段 2:用户升级 Computer 到 v0.2,业务方注册 Resolver
阶段 3:发布 v0.2 MCP Server Helper(Window 用 annotations,DPE 用 _meta)
阶段 4:MCP Server 增量升级(Window/DPE 元数据迁移)
阶段 5:发布 v0.2 Agent SDK(含 get_dpe / get_resources 客户端 + 应用层拉取)
阶段 6:Agent 升级;旧 v0.1 客户端无法连接 v0.2 Server,由 4008 提示用户升级
8. 版本号与发布¶
8.1 语义化版本¶
按 versioning.md PATCH 自由 / MINOR 严格 / v0.x 规则:v0.1 → v0.2 是 MINOR bump(v0.x 阶段 MINOR 也是破坏性)。
8.2 发布清单¶
- 协议规范文档(
docs/specification/、docs/migrations/)已更新 - python-sdk 实现完成 + 测试通过
- rust-sdk 实现完成 + 测试通过
- python-sdk 包版本
0.2.0发布 - rust-sdk crate 版本
0.2.0发布 - CHANGELOG / 升级公告已发出
8.3 文档变更¶
docs/specification/dpe.md:新建(替代旧 finder.md)docs/specification/finder.md:已删除(Finder 移出协议范围)docs/guides/finder-sdk-guide.md:已删除docs/specification/desktop.md:已更新(priority float / audience /_meta.fullscreen)docs/specification/data-structures.md:删除ListFinderReq/ListFinderRet/DPEDocumentSummary/DPEPageSummary/DPEElementDetail;新增GetDPEReq/GetDPERetdocs/specification/events.md:删除client:list_finder/server:update_finder/notify:update_finder;新增client:get_dpedocs/specification/error-handling.md:新增 4006/4007/4008/4011/4012/4013docs/specification/architecture.md:DPE 章节重写(去掉 Finder Organizer,加入 Resolver Hook)docs/specification/versioning.md:协议依赖 / SDK 版本锁定 / 4008 disconnect / 等docs/appendix/faq.md:DPE Q&A 重写
9. 联系与反馈¶
- 协议仓库:github.com/A2C-SMCP/a2c-smcp-protocol
- python-sdk:github.com/A2C-SMCP/python-sdk
- rust-sdk:github.com/A2C-SMCP/rust-sdk
- 实现疑问 / 跨 SDK 不一致 / 协议歧义 → 在协议仓库开 issue