跳转至

A2C-SMCP 数据结构定义

本文档定义了 A2C-SMCP 协议的所有数据结构,基于 a2c_smcp/smcp.py 中的 TypedDict 定义。

通用约定

  • 所有数据结构使用 JSON 对象表示
  • 字段命名使用 snake_case 风格
  • 可选字段使用 NotRequired 标注
  • 时间相关字段:超时使用整数秒

基础数据结构

AgentCallData

Agent 发起调用的基础数据,被多个请求结构继承。

class AgentCallData(TypedDict):
    agent: str      # Agent 名称/标识
    req_id: str     # 请求 ID,用于去重和关联

取消事件复用本结构server:tool_call_cancel / notify:tool_call_cancel 直接复用 AgentCallData,其中 req_id MUST 等于被取消的原 client:tool_callreq_id(唯一定位在途调用),且不含 computer 字段。详见 事件 §server:tool_call_cancel


工具相关结构

ToolCallReq

工具调用请求,继承自 AgentCallData

class ToolCallReq(AgentCallData):
    agent: str          # Agent 名称
    req_id: str         # 请求 ID
    computer: str       # 目标 Computer 名称
    tool_name: str      # 工具名称
    params: dict        # 工具调用参数
    timeout: int        # 超时时间(秒)

SMCPTool

SMCP 协议中的工具定义,用于工具列表返回。

class SMCPTool(TypedDict):
    name: str                           # 工具名称
    description: str                    # 工具描述
    params_schema: dict                 # 参数 JSON Schema
    return_schema: dict | None          # 返回值 JSON Schema(可选)
    meta: NotRequired[Attributes | None]  # 工具元数据(可选)

说明: 当 Computer 管理多个 MCP Server 时,可能存在工具名称冲突。此时可通过 meta 中的 alias 字段设置别名进行区分。

SMCPTool.meta 序列化规范

SMCPTool.meta 是工具的元数据字段,类型为 Attributes = Mapping[str, AttributeValue]。由于 AttributeValue 仅支持简单类型(str | bool | int | float | Sequence[str] 等),所有复杂结构必须序列化为 JSON 字符串后存入

meta 的命名空间结构

meta 是一个多方写入的字段,不同来源的数据通过命名空间 key 隔离:

Key 写入方 说明
a2c_tool_meta A2C 系统(Computer) A2C 配置的工具元数据(tags、auto_apply、alias 等)
MCP_TOOL_ANNOTATION A2C 系统(Computer) MCP 标准工具注解(destructive、readOnlyHint 等)
其他任意 key MCP Server 自身 MCP Server 在 Tool._meta 中设置的原生元数据

为什么不将 a2c_tool_meta 展开到 meta 顶层?

MCP 协议的 Tool._meta 是一个开放字段,MCP Server 可以自由设置任意 key-value。 如果将 tagsauto_apply 等 A2C 字段直接展开到 meta 顶层,一旦某个 MCP Server 也在 _meta 中使用了同名 key(如 tags),就会产生命名冲突,Agent 端无法区分 该字段来自 A2C 配置还是 MCP Server 自身。

此外,ToolMeta 中包含 ret_object_mapper: dict 等嵌套类型,无法直接作为 AttributeValue 存入。使用二级 key + JSON 字符串可以保证所有字段类型统一。

a2c_tool_meta 的值格式

meta["a2c_tool_meta"] 的值是一个 JSON 字符串(不是 dict),Agent 端需要 json.loads() 后使用:

# Agent 端解析示例
import json

for tool in tools:
    meta = tool.get("meta", {})
    if "a2c_tool_meta" in meta:
        tool_meta = json.loads(meta["a2c_tool_meta"])
        tags = tool_meta.get("tags")          # list[str] | None
        auto_apply = tool_meta.get("auto_apply")  # bool | None
        alias = tool_meta.get("alias")        # str | None

完整 JSON 示例

场景 1: default_tool_meta = null(未配置元数据)

{
  "name": "hello",
  "description": "Say hello to someone.",
  "params_schema": {
    "properties": {
      "name": { "default": "World", "title": "Name", "type": "string" }
    },
    "title": "helloArguments",
    "type": "object"
  },
  "return_schema": {
    "properties": {
      "result": { "title": "Result", "type": "string" }
    },
    "required": ["result"],
    "title": "helloOutput",
    "type": "object"
  },
  "meta": {}
}

场景 2: default_tool_meta = {"tags": ["browser"], "auto_apply": true}

{
  "name": "browser_navigate",
  "description": "Navigate to a URL.",
  "params_schema": { "..." : "..." },
  "return_schema": { "..." : "..." },
  "meta": {
    "a2c_tool_meta": "{\"auto_apply\": true, \"alias\": null, \"tags\": [\"browser\"], \"ret_object_mapper\": null}"
  }
}

注意 a2c_tool_meta 的值类型

a2c_tool_meta 的值是 JSON 字符串,不是嵌套对象。 对其执行 json.loads() 后得到如下 dict:

{
  "auto_apply": true,
  "alias": null,
  "tags": ["browser"],
  "ret_object_mapper": null
}

ToolMeta

工具元数据配置。此结构定义了 a2c_tool_meta JSON 字符串解析后的字段。

class ToolMeta(TypedDict, total=False):
    auto_apply: NotRequired[bool | None]
    # 是否自动应用(跳过二次确认)

    ret_object_mapper: NotRequired[dict | None]
    # 返回值对象映射,用于统一不同 MCP 工具的返回格式

    alias: NotRequired[str | None]
    # 工具别名,用于解决不同 Server 下的工具重名冲突

    tags: NotRequired[list[str] | None]
    # 工具标签,用于分类

GetToolsReq

获取工具列表请求。

class GetToolsReq(AgentCallData):
    agent: str      # Agent 名称
    req_id: str     # 请求 ID
    computer: str   # 目标 Computer 名称

GetToolsRet

获取工具列表响应。

class GetToolsRet(TypedDict):
    tools: list[SMCPTool]   # 工具列表
    req_id: str             # 请求 ID

CallToolResult 结果级 A2C 标记

client:tool_call 的响应是 MCP 标准 CallToolResult(A2C 不可改其结构)。A2C 通过 CallToolResult结果级元数据字段承载扩展标记。

元数据落位规则:结果级 meta vs 子级 _meta

A2C 在 MCP 结构上扩展元数据时,线上字面 key 取决于层级——以下约定与全部既有用法一致:

层级 字面 wire key 示例
结果级CallToolResult / SMCPTool 顶层) meta meta.a2c_cancelledmeta.a2c_timeout、授权失败的 meta.error_code4006/4007)、SMCPTool.meta.a2c_tool_meta
子级CallToolResult content item)/ MCP Resource / Tool _meta content item _meta.a2c_blob_handle通用二进制传输)、Resource._meta.fullscreenDesktop)、Resource._meta.versionSKILL

新增结果级标记 MUSTmeta子级 / Resource 级标记 MUST_meta

取消 / 超时标记(均为结果级 meta 下的 a2c_* 命名空间键):

key 类型 必选性 含义
meta.a2c_cancelled bool 取消时 MUSTtrue 标识该结果由 notify:tool_call_cancel 中断产生(区别于普通失败/超时)
meta.a2c_cancel_reason str SHOULD(可选) 诊断性原因,由 Computer 填写(当前恒为 "agent_requested");仅供观测,不承载控制逻辑
meta.a2c_timeout bool 超时时 SHOULDtrue 标识该结果为超时返回(区别于取消)

被取消时,Computer 对原 client:tool_call 的 ack 返回(参考实现 python,reference impl):

CallToolResult(
    content=[TextContent(text="Tool call cancelled", type="text")],
    isError=True,
    meta={"a2c_cancelled": True, "a2c_cancel_reason": "agent_requested"},
)

Producer / Consumer 约定

Producer(Computer)MUST 把标记写在结果级 meta。Consumer(Agent)SHOULDmeta / _meta 两种线上 key 宽松读取(不同 SDK 序列化路径可能不同),并优先按本规范的结果级 meta 取值。配套时序见 事件 §notify:tool_call_cancel


房间管理结构

连接握手参数

Socket.IO 连接阶段的参数分两个位置承载,遵循"协议层放 URL、业务层放 auth"的分层原则:

URL Query 参数(协议层)

参数 必需 说明
a2c_version 协议版本号,格式 MAJOR.MINOR.PATCH,如 0.2.0

由 Server 在 HTTP 中间件层 校验;不兼容时返回 HTTP 400 携带 4008 错误 body。具体规则见 协议版本与握手

auth 对象(业务层,ConnectAuth)

Socket.IO 连接握手阶段的 auth 对象。所有客户端(Agent / Computer)在连接 Server 时必须提供。由 Server 在 connect handler(业务代码)中处理。

协议层最小契约

协议在 ConnectAuth 上的唯一规范要求role 字段:

class ConnectAuth(TypedDict):
    role: Literal["computer", "agent"]  # 必需:客户端角色
强度 行为约束
MUST Client 提供 role 字段,取值为 "agent""computer";Server 据此路由
协议未表态 其他字段(如 token、api_key、business_meta 等)——是否存在、是否被消费由业务层(Server 实现方)自决
SHOULD 避免把"已有协议事件承载的字段"重复塞进 auth。例如 office_id / name 已由 EnterOfficeReq 承载,重复放入 auth 会造成职责重叠与一致性维护负担;建议 auth 仅承载真正的认证数据(token / API key / 用户标识等)
业务层扩展空间

connect handler 是 Server 实现方编写的业务代码,A2C-SMCP 协议不约束业务层在 auth 中放什么、怎么校验。常见扩展场景:

  • 鉴权 token:auth = {"role": "agent", "token": "..."}
  • 多租户标识:auth = {"role": "computer", "tenant_id": "..."}
  • 网关签名:auth = {"role": "agent", "signature": "..."}

这些都属于业务层职责,协议不评价。SDK 实现方提供的默认行为应只发送 role,并提供回调让用户注入业务字段。

为什么 a2c_version 不在 auth 里

协议版本校验必须在任何业务代码之前完成,不能受 connect handler 实现的影响。因此 a2c_version 放在 URL query(HTTP 层),由独立中间件校验;auth 仅承载业务层数据。详见 协议版本与握手 § 设计取向

EnterOfficeReq

加入房间请求。不再携带版本信息——版本校验已在连接建立前完成(HTTP 层),身份通过 auth.role 声明。

class EnterOfficeReq(TypedDict):
    role: Literal["computer", "agent"]  # 角色类型
    name: str                           # 名称
    office_id: str                      # 房间 ID

LeaveOfficeReq

离开房间请求。

class LeaveOfficeReq(TypedDict):
    office_id: str      # 房间 ID

EnterOfficeNotification

成员加入房间通知。

class EnterOfficeNotification(TypedDict, total=False):
    office_id: str              # 房间 ID
    computer: str | None        # 加入的 Computer 名称(若为 Computer)
    agent: str | None           # 加入的 Agent 名称(若为 Agent)

LeaveOfficeNotification

成员离开房间通知。

class LeaveOfficeNotification(TypedDict, total=False):
    office_id: str              # 房间 ID
    computer: str | None        # 离开的 Computer 名称
    agent: str | None           # 离开的 Agent 名称

ListRoomReq

列出房间内会话请求。

class ListRoomReq(AgentCallData):
    agent: str          # Agent 名称
    req_id: str         # 请求 ID
    office_id: str      # 房间 ID

SessionInfo

会话信息。

class SessionInfo(TypedDict, total=False):
    sid: str                                # 会话 ID
    name: str                               # 会话名称
    role: Literal["computer", "agent"]      # 角色
    office_id: str                          # 所属房间 ID
    a2c_version: str                        # 协议版本号(Server 在 HTTP 握手阶段从 URL query 记录)

a2c_version 字段

  • Server 在客户端 Socket.IO 连接的 HTTP 校验阶段从 URL query 的 a2c_version 读取并存入 Session
  • 同房间内 a2c_version 必然兼容(Server 在 HTTP 层已校验);此字段仅用于展示与诊断(如 UI 显示成员版本、日志定位)
  • 不用于二次校验
  • 详见 协议版本与握手

ListRoomRet

列出房间内会话响应。

class ListRoomRet(TypedDict):
    sessions: list[SessionInfo]     # 会话列表
    req_id: str                     # 请求 ID

配置相关结构

UpdateComputerConfigReq

配置更新请求。同时被 Desktop 更新事件(server:update_desktop / notify:update_desktop)与 SKILL 更新事件(server:update_skills / notify:update_skills)复用。

class UpdateComputerConfigReq(TypedDict):
    computer: str       # Computer 名称

复用说明: 此数据结构被以下事件共用:

事件 用途
server:update_config Computer 配置更新请求
server:update_desktop 桌面更新通知请求
notify:update_desktop 桌面更新广播通知
server:update_skills SKILL 更新通知请求
notify:update_skills SKILL 更新广播通知

UpdateMCPConfigNotification

配置更新通知。

class UpdateMCPConfigNotification(TypedDict, total=False):
    computer: str       # Computer 名称

UpdateToolListNotification

工具列表更新通知。

class UpdateToolListNotification(TypedDict, total=False):
    computer: str       # Computer 名称

GetComputerConfigReq

获取 Computer 配置请求。

class GetComputerConfigReq(AgentCallData):
    agent: str          # Agent 名称
    req_id: str         # 请求 ID
    computer: str       # 目标 Computer 名称

GetComputerConfigRet

获取 Computer 配置响应。

class GetComputerConfigRet(TypedDict):
    inputs: NotRequired[list[MCPServerInput] | None]    # 输入定义列表
    servers: dict[str, MCPServerConfig]                 # MCP Server 配置映射

Desktop 相关结构

GetDeskTopReq

获取桌面信息请求。

class GetDeskTopReq(AgentCallData, total=True):
    agent: str                      # Agent 名称
    req_id: str                     # 请求 ID
    computer: str                   # 目标 Computer 名称
    desktop_size: NotRequired[int]  # 可选:限制返回的桌面内容数量
    window: NotRequired[str]        # 可选:指定获取的 WindowURI(完全相等匹配;未命中返回空,非错误)

Desktop

桌面内容类型别名。每个 Desktop 条目是一个字符串,代表一个窗口的渲染结果。

Desktop: TypeAlias = str

内容格式:

  • 有文本内容时:{window:// URI}\n\n{body}(多个 TextResourceContentstext\n\n 连接)
  • 无文本内容时:仅 {window:// URI}

示例:

window://com.example.browser/main

<html>当前页面内容...</html>

详见 Desktop 桌面系统 中的完整规范。

Desktop 更新事件的数据结构

server:update_desktopnotify:update_desktop 事件均复用 UpdateComputerConfigReq 结构(仅包含 computer: str 字段),与 server:update_config 共享同一数据结构。

GetDeskTopRet

获取桌面信息响应。

class GetDeskTopRet(TypedDict, total=False):
    desktops: list[Desktop]     # 桌面内容列表(字符串形式)
    req_id: str                 # 请求 ID

Resource 发现相关结构

GetResourcesReq

枚举指定 MCP Server 的 Resource 列表请求——透明转发 MCP 标准 resources/list不做协议级过滤。Agent 据此发现 window / 业务自定义 scheme 的资源。

class GetResourcesReq(AgentCallData, total=True):
    agent: str                          # Agent 名称
    req_id: str                         # 请求 ID
    computer: str                       # 目标 Computer 名称
    mcp_server: str                     # 必填:目标 MCP Server 名称(来自 client:get_config 返回的 servers 字典 key)
    cursor: NotRequired[str]            # 可选:MCP 标准 cursor 翻页;首次不传或传 null

GetResourcesRet

class GetResourcesRet(TypedDict, total=False):
    resources: list[Resource]           # MCP 标准 Resource 列表(含任意 scheme,业务自决过滤)
    next_cursor: NotRequired[str]       # 可选:有则继续翻页;无则结束
    req_id: str                         # 请求 ID

透明转发原则

Computer 不做 scheme / 元数据层面的过滤、不做跨 Server 聚合——业务方按 Server 维度 遍历,自决过滤条件(window:// / _meta 字段 / 名称匹配等)。 保留 MCP 标准 cursor 翻页能力,业务方按需翻页,不强制全量加载。

  • mcp_server 不存在 → 4014 MCP Server Not Found
  • MCP Server 不支持 resources capability → 4015 MCP Capability Not Supported

v0.2 不返回 resourceTemplates

client:get_resources 仅对应 MCP resources/list不返回 resourceTemplates。MCP 上游有独立端点 resources/templates/list——v0.2 用户场景(window 静态资源发现等)不需要 URI 模板能力,未来 v0.3+ 如有需要将增加 client:get_resource_templates 独立事件。

A2CResource

A2C 协议定义的 Resource 类型——结构镜像 MCP Resource,但字段命名沿用 A2C snake_case 风格。

class A2CResource(TypedDict, total=False):
    uri: str                            # 必选:访问 URI(任意 scheme)
    name: NotRequired[str]              # 可选:人类可读的资源名
    description: NotRequired[str]
    mime_type: NotRequired[str]
    size: NotRequired[int]              # 可选:字节数
    annotations: NotRequired[dict]      # 透传 MCP annotations(含 audience 等)
    _meta: NotRequired[dict]            # 协议扩展点

SKILL 相关结构

SKILL 通道的请求 / 响应结构。完整通道语义(命名规则、URI、source 模式、安装生命周期、安全模型)见 SKILL 通道

A2CSkillRef

Skill 引用对象——client:get_skills 返回列表的元素。name 是协议主键(合成的全局唯一名,跨工具对齐裸名:marketplace <plugin>:<skill> / user 裸 <skill> / mcp mcp:<server>:<skill>,见 skill.md §1);Agent 把 name不透明可比较字符串,判定来源用 source 字段。

class A2CSkillRef(TypedDict):       # 默认 total=True:裸字段 = 必选,NotRequired[] = 可选
    # ── 主键 ────────────────────────────────────────────
    name: str                       # 必选:合成全局唯一名(跨工具对齐裸名)
                                    # marketplace: acme-audit:audit / user: my-helper
                                    # mcp: mcp:tfrobot-tools:code-review
    # ── 来源元数据 ───────────────────────────────────────
    source: str                     # 必选:完整来源 provenance(含 marketplace 名等)
                                    # 例:mcp:tfrobot-tools / marketplace:acme-skills / user
    uri: NotRequired[str]           # 仅 MCP 来源:skill://host/skill-name
                                    # 来源追溯用次要身份,Agent 非权威(见 skill.md §2)
    # ── 物化输出 ─────────────────────────────────────────
    path: str                       # 必选:Computer 本地绝对目录路径
                                    # staging 落盘是所有 source 的统一第一步,故恒存在
                                    # 面向 Agent SDK(脚本执行/文件访问);LLM 永不可见
    # ── SKILL.md frontmatter 派生(marketplace v1 §3.1 的 6 字段,无 version)──
    description: str                # 必选:marketplace SKILL v1 §3.1
    license: NotRequired[str]
    compatibility: NotRequired[str]
    allowed_tools: NotRequired[list[str]]   # frontmatter "allowed-tools" 规范化为 list
    skill_metadata: NotRequired[dict]       # frontmatter.metadata 透传;A2C 不解释
    # ── 包元数据派生(非 frontmatter)────────────────────
    version: NotRequired[str]               # 来源各异(见下方 note);user 源缺省/null

必选核心 = name / source / path / description(4 字段)

这四个字段跨所有 source(marketplace / user DropIn / mcp)恒存在,是 A2CSkillRef 的必选核心:

  • name / source:协议主键与来源 provenance,每条 ref 必有。
  • path:staging 物化是所有 source 的统一第一步,进入 Registry 的 SKILL 必有可读本地目录(见下方 note)。
  • description:SKILL.md frontmatter 强制字段(marketplace v1 §3.1 的 6 字段之一),任何合法 SKILL.md 必含。

其余 6 个(uri / license / compatibility / allowed_tools / skill_metadata / version)为 NotRequired。Producer(Computer)MUST 发齐 4 个必选字段;Consumer(Agent)可假定其存在,但 MUST NOT 假定任一可选字段存在。SDK 用 PEP 655 跟进时,两种等价写法皆可——本规范取 house 约定(total=True 默认 + NotRequired[]),SDK 亦可用 total=False + Required[]

path 恒存在;为何无 raw mcp_server 字段

path 必选:Computer 是 SKILL 管理者,所有 source 落地的统一第一步都是 staging 物化到本地(skill.md §4/§5),故进入 Registry 的 SKILL 必有可读本地目录,不存在"无 baseDir"形态。

不暴露原始 MCP server 名:理念 #2 要求 Agent 侧协议表面与 source 无关。raw(未规范化)server 名是 MCP 工具/资源通道的寻址键(client:get_configservers key),与 SKILL 不同层级,不反规范化进 SKILL ref。来源追溯由 source 与 MCP 来源的 uri(skill.md §2)承担。

version 的 source-of-truth(不来自 frontmatter)

marketplace SKILL v1 frontmatter 恰好 6 字段(name / description / license / compatibility / metadata / allowed-tools),无 versionA2CSkillRef.version 按来源取值,故为 NotRequired(无来源即省略):

Source version 来源
marketplace plugin.json / marketplace entry 的 version
mcp skill:// 资源的 _meta.versionskill.md §3 推荐附加字段)
user 无可靠来源 → 缺省 / null

Agent MUST NOT 假定 version 一定存在。

GetSkillsReq

获取 SKILL 清单请求。

class GetSkillsReq(AgentCallData, total=True):
    agent: str          # Agent 名称
    req_id: str         # 请求 ID
    computer: str       # 目标 Computer 名称

GetSkillsRet

获取 SKILL 清单响应——轻量元数据,不含 SKILL.md body。

class GetSkillsRet(TypedDict, total=False):
    skills: list[A2CSkillRef]   # 当前已安装且可用 SKILL(排除孤儿;不排序、不去重)
    req_id: str                 # 请求 ID

GetSkillReq

获取 SKILL 包内单个资源请求。SKILL 本质是文件夹:rel_path 缺省取包根 SKILL.md(入口),携带 rel_path 取包内其它资源,实现渐进式披露。传输机制不在此处——二进制 / 过大文本由 GetSkillRet.blob_handle通用二进制传输

class GetSkillReq(AgentCallData, total=True):
    agent: str                  # Agent 名称
    req_id: str                 # 请求 ID
    computer: str               # 目标 Computer 名称
    name: str                   # 必选:来自某 A2CSkillRef.name
    rel_path: NotRequired[str]  # 可选:SKILL 包根 POSIX 相对路径
                                # 缺省 = "SKILL.md";MUST 相对、无 ..、无绝对路径

GetSkillRet

获取 SKILL 包内单个资源响应。文本且可内联 → body 直接给出;二进制或过大文本 → blob_handleclient:get_blob 拉取——bodyblob_handle 恰一存在

class GetSkillRet(TypedDict, total=False):
    name: str                       # 回显
    rel_path: str                   # 回显(缺省请求时为 "SKILL.md")
    mime_type: str                  # 资源 MIME,如 text/markdown / image/png(确定性推断,见 SKILL §6.4)
    total_size: int                 # 资源总字节数
    sha256: str                     # 全量资源 sha256 十六进制(完整性 + 变更检测)
    body: NotRequired[str]          # 文本且 ≤ 内联预算:直接内容(与 blob_handle 恰一)
    blob_handle: NotRequired[str]   # 否则:转 client:get_blob 的不透明句柄(与 body 恰一)
    req_id: str                     # 请求 ID

「资源字节」的定义(total_size / sha256 / 传输的基准)

total_size / sha256 及(走 handle 时的)分块一律基于 Agent 最终消费的资源字节:SKILL.md → frontmatter 剥离后的 body;其它文件 → 原始文件字节。frontmatter 剥离、占位符不展开在确定资源字节时已完成。空资源 = total_size=0,文本走空 body

内联 vs handle 的判定

Computer 解析 rel_path 后:文本 MIMEtotal_size ≤ 内联预算(保证单条 ack 不超 Server buffer)→ body二进制 MIME,或文本超内联预算 → 仅给 blob_handle(不内联任何字节)。body / blob_handle 恰一;Agent SHOULDsha256 校验 body,或在 handle 路径于 eof 后校验。mime_type MUST 确定性推断、不依赖宿主 OS 的 MIME 注册表(见 SKILL §6.4)。

安全边界与 too_large

rel_path 沙箱(穿越 / .skillenv / 不存在)与绝对上限(too_large)在 get_skill 解析时 决断 → 4017铸造 handle、零字节传输。占位符不展开(Agent SDK 职责,见 SKILL §7)。详见 SKILL §9


通用二进制传输结构

通用 Agent←Computer 字节拉取通道的结构。完整语义(句柄契约、生产者-消费者模型、安全模型)见 通用二进制传输

BlobHandle

BlobHandle: TypeAlias = str   # 不透明、Computer 铸造、无状态可重解析

由某生产者通道在其响应中铸造。Agent 视为不透明:MUST NOT 解析 / 拼接 / 伪造。Computer 每次调用即时解析,无 session / 无 TTL;解析时重新施加铸造通道的鉴权(SKILL → §9 沙箱)。

载体随响应结构而异,语义与拉取契约完全一致(详见 通用二进制传输 §5):

生产者 句柄载体 对等元数据
GetSkillRet(A2C 可变) 顶层 blob_handle 顶层 total_size / sha256 / mime_type
MCP CallToolResult(标准不可变) content item _meta.a2c_blob_handle item _meta.a2c_total_size / _meta.a2c_sha256 + 既有 mimeType

_meta 旁路与 SMCPTool.meta 命名空间约定a2c_tool_meta 等)同构——A2C 在不可变 MCP 结构上扩展的既定手法。

GetBlobReq

class GetBlobReq(AgentCallData, total=True):
    agent: str                          # Agent 名称
    req_id: str                         # 请求 ID
    computer: str                       # 目标 Computer 名称
    blob_handle: str                    # 必选:来自某通道响应的不透明句柄
    chunk_offset: NotRequired[int]      # 可选:资源字节绝对偏移;缺省 0
                                        # 无状态幂等 → 可续传 / 重试 / 并行
    max_chunk_bytes: NotRequired[int]   # 可选:客户建议单块上限
                                        # Computer clamp(不超 Server buffer)

GetBlobRet

class GetBlobRet(TypedDict, total=False):
    blob_handle: str            # 回显
    mime_type: str              # 资源 MIME
    total_size: int             # 资源总字节数(首块即知;一次读取内恒定)
    sha256: str                 # 全量资源 sha256 十六进制(跨块恒定)
    chunk_offset: int           # 本块起始字节偏移
    eof: bool                   # ⟺ chunk_offset + 本块字节数 == total_size
    blob: str                   # base64,本块字节
    req_id: str                 # 请求 ID

完整性 / 一致性 / 演进缝隙

  • eof 后 Agent SHOULD 校验重组 sha256 == 响应 sha256,不符即损坏、重读。
  • sha256 / total_size 一次逻辑读取内 MUST 稳定;跨块变化 ⇒ 源被改写,Agent MUST 从 offset 0 重读。Computer SHOULD 尽力一致快照。
  • GetBlobReq / GetBlobRet 为开放 TypedDict,未来可非破坏追加 content_encoding(gzip 等,缺省 identity)/ etag;offset / total_size / sha256 基于解码后资源字节,加压缩不致歧义。
  • 句柄失效 / 源消失 / 范围越界 → 4018

MCP Server 配置结构

BaseMCPServerConfig

MCP Server 配置基类。

class BaseMCPServerConfig(TypedDict):
    name: str
    # MCP Server 名称

    disabled: bool
    # 是否禁用

    forbidden_tools: list[str]
    # 禁用的工具列表

    tool_meta: dict[str, ToolMeta]
    # 工具元数据映射(工具名 → 元数据)

    default_tool_meta: NotRequired[ToolMeta | None]
    # 默认工具元数据,当具体工具未配置时使用

    vrl: NotRequired[str | None]
    # VRL 脚本,用于动态转换工具返回值

MCPServerStdioConfig

标准输入输出模式的 MCP Server 配置。

class MCPServerStdioParameters(TypedDict):
    command: str
    # 启动命令

    args: list[str]
    # 命令行参数

    env: dict[str, str] | None
    # 环境变量

    cwd: str | None
    # 工作目录

    encoding: str
    # 文本编码,默认 utf-8

    encoding_error_handler: Literal["strict", "ignore", "replace"]
    # 编码错误处理方式


class MCPServerStdioConfig(BaseMCPServerConfig):
    type: Literal["stdio"]
    server_parameters: MCPServerStdioParameters

MCPServerStreamableHttpConfig

Streamable HTTP 模式的 MCP Server 配置。

class MCPServerStreamableHttpParameters(TypedDict):
    url: str
    # 端点 URL

    headers: dict[str, Any] | None
    # 请求头

    timeout: str
    # HTTP 超时(ISO 8601 duration 格式)

    sse_read_timeout: str
    # SSE 读取超时(ISO 8601 duration 格式)

    terminate_on_close: bool
    # 关闭时是否终止客户端会话


class MCPServerStreamableHttpConfig(BaseMCPServerConfig):
    type: Literal["streamable"]
    server_parameters: MCPServerStreamableHttpParameters

MCPSSEConfig

SSE 模式的 MCP Server 配置。

class MCPSSEParameters(TypedDict):
    url: str
    # 端点 URL

    headers: dict[str, Any] | None
    # 请求头

    timeout: float
    # HTTP 超时(秒)

    sse_read_timeout: float
    # SSE 读取超时(秒)


class MCPSSEConfig(BaseMCPServerConfig):
    type: Literal["sse"]
    server_parameters: MCPSSEParameters

MCPServerConfig

MCP Server 配置联合类型。

MCPServerConfig = MCPServerStdioConfig | MCPServerStreamableHttpConfig | MCPSSEConfig

注意: MCP Server 类型为 "stdio", "streamable", "sse" 三种,其中 "streamable" 对应 MCP 官方的 Streamable HTTP 传输模式。


输入配置结构

输入配置用于定义 MCP Server 配置中的动态占位符。

MCPServerInputBase

输入配置基类。

class MCPServerInputBase(TypedDict):
    id: str             # 输入 ID
    description: str    # 描述

MCPServerPromptStringInput

字符串输入类型。

class MCPServerPromptStringInput(MCPServerInputBase):
    type: Literal["promptString"]
    default: NotRequired[str | None]        # 默认值
    password: NotRequired[bool | None]      # 是否为密码(隐藏输入)

MCPServerPickStringInput

选择输入类型。

class MCPServerPickStringInput(MCPServerInputBase):
    type: Literal["pickString"]
    options: list[str]                      # 可选项列表
    default: NotRequired[str | None]        # 默认值

MCPServerCommandInput

命令输入类型(通过执行命令获取值)。

class MCPServerCommandInput(MCPServerInputBase):
    type: Literal["command"]
    command: str                            # 要执行的命令
    args: NotRequired[dict[str, str] | None]  # 命令参数

MCPServerInput

输入配置联合类型。

MCPServerInput = MCPServerPromptStringInput | MCPServerPickStringInput | MCPServerCommandInput

类型别名

from a2c_smcp.types import SERVER_NAME, TOOL_NAME, Attributes, AttributeValue

SERVER_NAME: TypeAlias = str    # MCP Server 名称
TOOL_NAME: TypeAlias = str      # 工具名称
AttributeValue: TypeAlias = str | int | float | bool | None
Attributes: TypeAlias = dict[str, AttributeValue]
Desktop: TypeAlias = str        # 桌面内容(字符串形式)

参考

  • 类型定义源码: a2c_smcp/smcp.py
  • Pydantic 模型: a2c_smcp/computer/mcp_clients/model.py
  • 通用类型: a2c_smcp/types.py