A2C-SMCP 事件定义¶
本文档定义了 A2C-SMCP 协议的所有事件,基于 a2c_smcp/smcp.py 中的实际实现。
命名空间¶
所有事件在以下命名空间中传输:
事件分类规则¶
| 前缀 | 方向 | 说明 | 实现要求 |
|---|---|---|---|
client: |
Agent → Server → Computer | 工具操作类,由 Server 路由到指定 Computer | Computer 必须实现 |
server: |
客户端 → Server | 房间管理、状态更新类 | Server 必须实现 |
notify: |
Server → 广播 | 通知类,由 Server 广播到房间 | Agent/Computer 选择性接收 |
连接握手¶
Agent 或 Computer 在通过 Socket.IO 连接到 Server 时分两个位置携带参数:
- URL query 中的
a2c_version(协议版本号)—— 由 Server 在 HTTP 中间件层校验,先于 Socket.IO 处理 auth对象中的role(业务身份)—— 由 Server 在connecthandler 中处理
参数位置¶
| 位置 | 字段 | 必需 | 说明 |
|---|---|---|---|
| URL query | a2c_version |
是 | 协议版本号,如 0.2.0 |
| auth 对象 | role |
是 | "agent" 或 "computer" |
客户端连接示例¶
# Python 客户端
import socketio
from a2c_smcp import PROTOCOL_VERSION
sio = socketio.AsyncClient()
await sio.connect(
f"wss://server.example.com?a2c_version={PROTOCOL_VERSION}",
socketio_path="/smcp",
auth={"role": "agent"},
)
// TypeScript 客户端
import { io } from "socket.io-client";
import { PROTOCOL_VERSION } from "@a2c-smcp/client";
const socket = io("wss://server.example.com", {
path: "/smcp",
query: { a2c_version: PROTOCOL_VERSION },
auth: { role: "agent" },
});
Server 校验行为¶
版本校验必须在 HTTP 中间件层完成,不能依赖 Socket.IO 的 connect handler。这保证业务代码无法影响协议校验的正确性。
- 缺失
a2c_version→ HTTP 400,body{"code": 400, "message": "Missing a2c_version query parameter"} a2c_version格式非法 → HTTP 400,body{"code": 400, "message": "Invalid a2c_version: ..."}a2c_version与 Server 不兼容 → HTTP 400,body 见4008 Protocol Version Mismatch- 校验通过 → Server 将
a2c_version存入 Socket.IO session,供server:list_room查询使用;连接进入 Socket.IO 层,connecthandler 处理auth.role
校验规则(MAJOR.MINOR.PATCH 语义、v0.x 与 v1.0+ 的区别、中间件实现示例)详见 协议版本与握手。
客户端错误处理¶
Socket.IO 客户端遇到 HTTP 400 会触发 connect_error 事件并携带响应 body。SDK 必须解析 body、识别 code: 4008 并抛出专属异常:
import json
@sio.on("connect_error")
async def on_connect_error(data):
payload = data if isinstance(data, dict) else (
json.loads(data) if isinstance(data, str) else None
)
if payload and payload.get("code") == 4008:
raise ProtocolVersionError(
server_version=payload.get("server_version"),
client_version=payload.get("client_version"),
)
完整事件列表¶
Client 事件(Agent → Computer)¶
这些事件由 Agent 发起,通过 Server 路由到指定的 Computer 执行。
| 事件常量 | 事件名称 | 描述 | 请求数据结构 | 响应数据结构 |
|---|---|---|---|---|
TOOL_CALL_EVENT |
client:tool_call |
工具调用请求 | ToolCallReq |
CallToolResult |
GET_CONFIG_EVENT |
client:get_config |
获取 Computer 配置 | GetComputerConfigReq |
GetComputerConfigRet |
GET_TOOLS_EVENT |
client:get_tools |
获取工具列表 | GetToolsReq |
GetToolsRet |
GET_DESKTOP_EVENT |
client:get_desktop |
获取桌面信息 | GetDeskTopReq |
GetDeskTopRet |
GET_DPE_EVENT |
client:get_dpe |
把 DPE URI 解析为 Agent 可访问的 URI | GetDPEReq |
GetDPERet |
GET_RESOURCES_EVENT |
client:get_resources |
透明转发指定 MCP Server 的 resources/list(含 cursor 翻页) |
GetResourcesReq |
GetResourcesRet |
Server 事件(客户端 → Server)¶
这些事件由 Agent 或 Computer 发起,由 Server 处理。
| 事件常量 | 事件名称 | 发起方 | 描述 | 数据结构 |
|---|---|---|---|---|
JOIN_OFFICE_EVENT |
server:join_office |
Agent/Computer | 加入房间 | EnterOfficeReq |
LEAVE_OFFICE_EVENT |
server:leave_office |
Agent/Computer | 离开房间 | LeaveOfficeReq |
UPDATE_CONFIG_EVENT |
server:update_config |
Computer | 配置更新通知请求 | UpdateComputerConfigReq |
UPDATE_TOOL_LIST_EVENT |
server:update_tool_list |
Computer | 工具列表更新通知请求 | UpdateToolListNotification |
UPDATE_DESKTOP_EVENT |
server:update_desktop |
Computer | 桌面更新通知请求 | UpdateComputerConfigReq |
CANCEL_TOOL_CALL_EVENT |
server:tool_call_cancel |
Agent | 取消工具调用 | AgentCallData |
LIST_ROOM_EVENT |
server:list_room |
Agent | 列出房间内所有会话 | ListRoomReq |
Notify 事件(Server → 广播)¶
这些事件由 Server 广播到房间内的所有成员。
| 事件常量 | 事件名称 | 描述 | 数据结构 |
|---|---|---|---|
ENTER_OFFICE_NOTIFICATION |
notify:enter_office |
成员加入房间通知 | EnterOfficeNotification |
LEAVE_OFFICE_NOTIFICATION |
notify:leave_office |
成员离开房间通知 | LeaveOfficeNotification |
UPDATE_CONFIG_NOTIFICATION |
notify:update_config |
配置更新通知 | UpdateMCPConfigNotification |
UPDATE_TOOL_LIST_NOTIFICATION |
notify:update_tool_list |
工具列表更新通知 | UpdateToolListNotification |
UPDATE_DESKTOP_NOTIFICATION |
notify:update_desktop |
桌面更新通知 | UpdateComputerConfigReq |
CANCEL_TOOL_CALL_NOTIFICATION |
notify:tool_call_cancel |
工具调用取消通知 | AgentCallData |
事件详细说明¶
工具调用事件¶
client:tool_call¶
Agent 向指定 Computer 发起工具调用。
请求流程:
Agent ──[client:tool_call]──→ Server ──[转发]──→ Computer
│
Agent ←──────────────────────────────────────── 返回结果
请求数据 (ToolCallReq):
{
"agent": str, # Agent 标识
"req_id": str, # 请求 ID(用于去重和取消)
"computer": str, # 目标 Computer 名称
"tool_name": str, # 工具名称
"params": dict, # 工具调用参数
"timeout": int # 超时时间(秒)
}
响应: 返回 MCP CallToolResult 结构。
client:get_tools¶
获取指定 Computer 的可用工具列表。
请求数据 (GetToolsReq):
响应数据 (GetToolsRet):
Agent 端解析 meta 的注意事项
SMCPTool.meta 中的 a2c_tool_meta 字段值为 JSON 字符串,需要 json.loads() 后
才能访问 tags、auto_apply 等字段。详见 SMCPTool.meta 序列化规范。
client:get_desktop¶
获取指定 Computer 的桌面信息(窗口资源聚合视图)。
请求数据 (GetDeskTopReq):
{
"agent": str, # Agent 标识
"req_id": str, # 请求 ID
"computer": str, # 目标 Computer 名称
"desktop_size": int, # 可选:限制返回的桌面内容数量
"window": str # 可选:指定获取的 WindowURI
}
响应数据 (GetDeskTopRet):
client:get_dpe¶
把一个 DPE URI 转成 Agent 可访问的 URI。Computer 调本地注册的 DPE Resolver hook 完成转换;Resolver 业务自决(上传对象存储 / 落本地缓存 / 任意 URI scheme)。Agent 拿到访问 URI 后用应用层协议(HTTP / file / ...)自取实际内容——A2C 协议不承载 DPE 内容。
请求数据 (GetDPEReq):
{
"agent": str, # Agent 标识
"req_id": str, # 请求 ID
"computer": str, # 目标 Computer 名称
"uri": str, # dpe://host/doc-ref(doc-ref 可单段或分段路径)
"timeout": int # 可选:秒,默认实现自定
}
路由说明:DPE URI 是自包含寻址凭据——Computer 通过 URI 中的 host 反查目标 MCP Server。host 跨 Server SHOULD 唯一(非 MUST);冲突时 Computer 在注册阶段记 WARN、路由按"先注册优先"。详见 DPE 文档协议 - host 路由策略。
响应数据 (GetDPERet):
{
"uri": str, # Resolver 输出的访问 URI(任意 scheme:https / file / 业务自定义)
"mime_type": str, # 可选
"size": int, # 可选:字节数
"req_id": str
}
错误码:未注册 Resolver → 4011 DPE Resolver Not Configured;URI 非法 → 4012 Invalid DPE URI;Resolver 执行失败(含 host 反查无结果、上游 MCP Server 不可用、上传异常等) → 4013 DPE Resolution Failed。
详见 DPE 文档协议。
client:get_resources¶
枚举指定 MCP Server 暴露的 Resource 列表——透明转发 MCP 标准 resources/list,不做协议级过滤。Agent 据此发现 dpe / window / 业务自定义 scheme 的资源。
支持 MCP 标准 cursor 翻页:Agent 首次调用不带 cursor,响应中含 next_cursor 时继续翻页;无 next_cursor 表示已到末尾。
请求数据 (GetResourcesReq):
{
"agent": str, # Agent 标识
"req_id": str, # 请求 ID
"computer": str, # 目标 Computer 名称
"mcp_server": str, # 必填:目标 MCP Server 名称(来自 client:get_config 返回的 servers 字典 key)
"cursor": str # 可选:MCP 标准 cursor 翻页;首次不传或传 null
}
响应数据 (GetResourcesRet):
{
"resources": list[Resource], # MCP 标准 Resource 列表(含任意 scheme,业务自决过滤)
"next_cursor": str, # 可选:有则继续翻页;无则结束
"req_id": str
}
Computer 处理流程:
- 校验
mcp_server是否在已注册的 MCP Server 列表中——不存在返回404 Not Found(错误信息含 mcp_server 名称) - 透明转发到对应 MCP Server 的
resources/list(cursor=...) - 直接返回
{resources, next_cursor},不做任何 scheme / 元数据层面的过滤
典型 Agent 使用流程(DPE 文档发现):
1. Agent → client:get_config(computer)
← 拿到所有 MCP Server 配置(servers 字典 key 即 server names)
2. 对每个 server name:
Agent → client:get_resources(mcp_server=name, cursor=None)
← {resources, next_cursor}
while next_cursor: 继续翻页
3. Agent 自己过滤(按 scheme / _meta / annotations / 名称等)
4. Agent → client:get_dpe(uri=dpe://...) → 拿到访问 URI → 应用层 fetch
设计原则:
- Computer 是 MCP 标准
resources/list的透明转发层——不做 scheme / 元数据过滤、不做跨 Server 聚合 - 保留 MCP 标准 cursor 翻页能力——Agent 按需翻页,不强制全量加载
- 业务方拿到 Resource 后自决过滤逻辑(
dpe:///window:/// 业务自定义 scheme /_meta字段 / 名称匹配)
详见 DPE 文档协议 - 文档发现。
client:get_config¶
获取指定 Computer 的完整配置信息。
请求数据 (GetComputerConfigReq):
响应数据 (GetComputerConfigRet):
{
"inputs": list[MCPServerInput] | None, # 输入定义列表
"servers": dict[str, MCPServerConfig] # MCP Server 配置映射
}
房间管理事件¶
server:join_office¶
Agent 或 Computer 请求加入房间。
请求数据 (EnterOfficeReq):
响应: (bool, str | None) - 成功标志和错误信息。
Server 处理规则: - Agent: 检查房间是否已有其他 Agent,若有则拒绝 - Computer: 若已在其他房间,自动离开旧房间
server:leave_office¶
Agent 或 Computer 请求离开房间。
请求数据 (LeaveOfficeReq):
server:list_room¶
Agent 查询指定房间内的所有会话信息。
请求数据 (ListRoomReq):
响应数据 (ListRoomRet):
其中 SessionInfo:
{
"sid": str, # 会话 ID
"name": str, # 会话名称
"role": Literal["computer", "agent"], # 角色
"office_id": str, # 所属房间 ID
"a2c_version": str # 协议版本号(Server 从 URL query 记录)
}
peer 版本可见性
a2c_version 由 Server 在 HTTP 握手校验阶段从 URL query 提取并存入 session。同一房间内所有成员的 a2c_version 必然兼容(Server 已在 HTTP 层校验),因此该字段不用作二次校验,仅用于展示与诊断:
- UI 侧显示房间成员的版本
- 日志/监控侧便于故障定位
详见 协议版本与握手。
配置与状态更新事件¶
server:update_config¶
Computer 通知 Server 其配置已更新,Server 随后广播 notify:update_config。
请求数据 (UpdateComputerConfigReq):
server:update_tool_list¶
Computer 通知 Server 其工具列表已更新,Server 随后广播 notify:update_tool_list。
请求数据 (UpdateToolListNotification):
server:update_desktop¶
Computer 通知 Server 其桌面内容已更新,Server 随后广播 notify:update_desktop。
触发条件(由 Computer 端检测):
- MCP Server 发出
ResourceListChangedNotification且window://URI 集合发生变化 - MCP Server 发出
ResourceUpdatedNotification且目标 URI 为window://
请求数据: 复用 UpdateComputerConfigReq(与 server:update_config 共享同一数据结构):
Server 处理: 接收后向该 Computer 所在房间广播 notify:update_desktop。
详见 Desktop 桌面系统 中的 更新机制。
通知事件¶
notify:enter_office¶
Server 广播:有成员加入房间。
数据结构 (EnterOfficeNotification):
{
"office_id": str,
"computer": str | None, # 加入的 Computer 名称(若为 Computer)
"agent": str | None # 加入的 Agent 名称(若为 Agent)
}
Agent 响应建议: 收到此通知后,应自动调用 client:get_tools 获取新 Computer 的工具列表。
notify:leave_office¶
Server 广播:有成员离开房间。
数据结构 (LeaveOfficeNotification):
notify:update_config¶
Server 广播:某 Computer 的配置已更新。
数据结构 (UpdateMCPConfigNotification):
Agent 响应建议: 收到此通知后,可调用 client:get_config 获取最新配置。
notify:update_tool_list¶
Server 广播:某 Computer 的工具列表已更新。
数据结构 (UpdateToolListNotification):
Agent 响应建议: 收到此通知后,应调用 client:get_tools 刷新本地工具列表。
notify:update_desktop¶
Server 广播:某 Computer 的桌面内容已更新。
数据结构: 复用 UpdateComputerConfigReq(与 notify:update_config 结构一致):
Agent 响应建议: 收到此通知后,推荐自动调用 client:get_desktop 获取最新桌面。
详见 Desktop 桌面系统 中的 完整生命周期时序图。
notify:tool_call_cancel¶
Server 广播:某工具调用已被取消。
数据结构 (AgentCallData):
事件流程图¶
工具调用流程¶
sequenceDiagram
participant A as Agent
participant S as Server
participant C as Computer
A->>S: client:tool_call (ToolCallReq)
S->>C: client:tool_call (转发)
C->>S: 工具执行结果
S->>A: 返回结果
动态工具发现流程¶
sequenceDiagram
participant A as Agent
participant S as Server
participant C as Computer
Note over C: Computer 加入房间
C->>S: server:join_office
S->>A: notify:enter_office
A->>S: client:get_tools
S->>C: client:get_tools (转发)
C->>S: 工具列表
S->>A: GetToolsRet
Note over A: 注册新工具
配置更新流程¶
sequenceDiagram
participant A as Agent
participant S as Server
participant C as Computer
Note over C: 配置变更
C->>S: server:update_config
S->>A: notify:update_config
A->>S: client:get_tools (可选)
S->>C: 转发
C->>S: 工具列表
S->>A: GetToolsRet
桌面更新流程¶
sequenceDiagram
participant M as MCP Server
participant C as Computer
participant S as Server
participant A as Agent
Note over M: 窗口资源变化
M->>C: ResourceListChangedNotification
C->>C: 比较 window:// URI 集合
C->>S: server:update_desktop
S->>A: notify:update_desktop
A->>S: client:get_desktop
S->>C: client:get_desktop (转发)
C->>C: organize_desktop()
C->>S: GetDeskTopRet
S->>A: GetDeskTopRet
详见 Desktop 桌面系统。
DPE 文档访问流程¶
sequenceDiagram
participant M as MCP Server
participant C as Computer
participant S as Server
participant A as Agent
participant Store as 业务存储<br/>(OSS / 本地 / etc)
Note over A: Agent 通过任意方式得到 dpe URI
A->>S: client:get_dpe(dpe://host/doc-1)
S->>C: 路由到目标 Computer
C->>M: resources/read(dpe://host/doc-1)
M->>C: ResourceContents
C->>C: 调 DPE Resolver Hook(业务实现)
C->>Store: 上传 / 持久化(业务自决)
Store->>C: 访问 URI
C->>S: GetDPERet(uri=https://...)
S->>A: GetDPERet
A->>Store: 用应用层(HTTP/file/...)拉取实际内容
详见 DPE 文档协议。
实现要求¶
Server 必须实现¶
- 所有
server:*事件的处理 - 所有
client:*事件的路由转发 - 所有
notify:*事件的广播
Computer 必须实现¶
- 所有
client:*事件的处理(作为接收方) - 房间管理事件 (
server:join_office,server:leave_office)
Agent 应该实现¶
notify:enter_office- 自动获取新 Computer 的工具notify:leave_office- 清理离开 Computer 的工具notify:update_config/notify:update_tool_list- 刷新工具列表
参考¶
- 事件常量定义:
a2c_smcp/smcp.py - Server 实现:
a2c_smcp/server/namespace.py - Agent 客户端:
a2c_smcp/agent/client.py - Computer 客户端:
a2c_smcp/computer/socketio/client.py