Computer MCP 配置指南¶
适用 SDK: python-sdk / rust-sdk
协议版本: 0.2.0
前置阅读
本文档是面向 SDK 开发者与使用者的配置实践指南,规范性定义以 specification/ 目录为准。阅读本文前,请先熟悉以下规范文档:
概述¶
A2C-SMCP 的 Computer 端在标准 MCP Server 配置(stdio / sse / streamable)基础上,提供了一系列增强能力,使 MCP 配置更灵活、更安全、更适合多 Server 协同场景。
核心增强点:
| 增强能力 | 说明 |
|---|---|
| Input 动态占位符 | 配置中使用 ${input:<id>} 引用运行时输入值(环境变量、用户交互、命令输出) |
| VRL 脚本转换 | 通过 VRL 脚本对工具返回值进行元数据二次加工 |
| ToolMeta 元数据 | 为工具附加别名、标签、自动确认、字段映射等元数据 |
| 访问控制 | disabled 禁用整个 Server,forbidden_tools 精确屏蔽特定工具 |
配置生命周期:
配置文件 (JSON)
│
▼
Input 占位符解析 ← 环境变量 / CLI 交互 / 命令执行
│
▼
MCP Client 创建 & 连接
│
▼
工具列表获取 → ToolMeta 注入 (meta["a2c_tool_meta"])
│
▼
工具调用 → VRL 脚本执行 → 结果存入 meta["a2c_vrl_transformed"]
│
▼
CallToolResult 返回给 Agent
配置文件格式¶
完整配置示例¶
以下是一个包含所有增强特性的完整配置 JSON 示例:
{
"inputs": [
{
"id": "FEISHU_APP_SECRET",
"type": "promptString",
"description": "飞书 MCP App Secret",
"password": true,
"default": ""
},
{
"id": "WORK_DIR",
"type": "command",
"description": "获取当前工作目录",
"command": "pwd"
},
{
"id": "LOG_LEVEL",
"type": "pickString",
"description": "日志级别",
"options": [
{ "label": "调试", "value": "DEBUG" },
{ "label": "信息", "value": "INFO" },
{ "label": "警告", "value": "WARNING" },
{ "label": "错误", "value": "ERROR" }
],
"default": "INFO"
}
],
"servers": {
"feishu-mcp": {
"type": "stdio",
"name": "feishu-mcp",
"disabled": false,
"forbidden_tools": ["dangerous_admin_tool"],
"default_tool_meta": {
"tags": ["feishu"],
"auto_apply": true
},
"tool_meta": {
"send_message": {
"auto_apply": false,
"alias": "feishu_send"
}
},
"vrl": ".processed = true\n.source = \"feishu\"",
"server_parameters": {
"command": "npx",
"args": ["-y", "@anthropic/feishu-mcp"],
"env": {
"APP_SECRET": "${input:FEISHU_APP_SECRET}",
"LOG_LEVEL": "${input:LOG_LEVEL}"
}
}
},
"browser-mcp": {
"type": "sse",
"name": "browser-mcp",
"server_parameters": {
"url": "http://localhost:8080/sse",
"headers": {
"Authorization": "Bearer ${input:FEISHU_APP_SECRET}"
}
}
}
}
}
name 与 bundle_id:声明面与身份面¶
上例中 servers 的 map key(及 name 字段)不是 server 的运行期身份——它是声明面的人写名字。两者是正交的命名空间:
servers map key / name |
bundle_id |
|
|---|---|---|
| 谁写谁读 | 人写、人读(配置撰写、展示) | 代码写、代码读(运行期传递与标识) |
| 唯一性 | 允许碰撞、永不做键/寻址、不强制唯一(身份正交性) | 运行期唯一身份:get_config.servers 的 key、工具暴露名前缀、错误码 meta.mcp_server、env 变量 server 段全用它 |
| 来源 | 用户手写 | derive-on-load 派生(见下),或显式声明 bundleId 字段;派生结果 MUST NOT 回写配置源 |
缺省派生规则:bundle_id = 显式 bundleId ?? normalize(name);normalize(name) 为空(如名字全为非 ASCII)时回落 bundle_ + 连接身份摘要。绝大多数情况下 bundle_id 就是规范化后的 name,稳定且不依赖 command/args。
何时需要显式写 bundleId:
- 两个 key 归一到同一 bundle_id(如
my.server与my_server都归一为my_server)——这是注册期硬错误(fail-fast),解法即改名或为其一显式指定bundleId; - env 变量名坍缩 fail-fast(见 环境变量命名规则);
- 名字全为非 ASCII 又希望身份可读、可预期时。
工具暴露名:聚合后进入 Agent 工具列表的名字是 {bundle_id}__{alias ?? 原始工具名}(BundleID 模型),跨 server 同名工具因此天然不冲突;每个工具的 server 归属由 SMCPTool.bundle_id 字段显式携带。
三种 Server 类型¶
| 类型 | type 值 |
通信方式 | server_parameters 关键字段 |
|---|---|---|---|
| Stdio | "stdio" |
本地子进程标准输入输出 | command, args, env, cwd |
| SSE | "sse" |
Server-Sent Events | url, headers |
| Streamable HTTP | "streamable" |
HTTP 流式传输 | url, headers |
Rust SDK 类型标签差异
Rust SDK 中 Streamable HTTP 类型的 type 值为 "http"(同时支持别名 "HTTP"),而非协议规范中的 "streamable"。使用 Rust SDK 时请注意此差异。
各类型的规范性 TypedDict 定义请参阅:数据结构 — MCP Server 配置结构。
disabled 与 forbidden_tools¶
两个字段均用于访问控制,但作用域不同:
| 字段 | 作用域 | 效果 |
|---|---|---|
disabled: true |
整个 Server | 完全跳过连接,不启动 MCP Client |
forbidden_tools |
指定工具名 | Server 正常连接,但这些工具从可用列表中移除 |
{
"name": "example-mcp",
"type": "stdio",
"disabled": false,
"forbidden_tools": ["rm_rf", "drop_database"],
"server_parameters": { "command": "example-mcp-server" }
}
尝试调用 forbidden_tools 中的工具会触发错误,不会路由到 MCP Server。
Input 动态占位符系统¶
占位符语法¶
在 server_parameters 中的任意字符串字段中使用占位符引用 Input 值:
其中 <id> 对应 inputs 数组中某项的 id 字段。占位符可出现在:
command、args中的参数值env的值urlheaders的值
纯占位符 vs 嵌入占位符:
TOKEN— 纯占位符,解析结果可以是非字符串类型(如数字、布尔值)GREETING— 嵌入占位符,解析结果始终转为字符串后拼接
Input 类型定义¶
协议规范定义了三种 Input 类型,详见 数据结构 — 输入配置结构。
| 类型 | 说明 | 关键字段 |
|---|---|---|
promptString |
文本输入提示 | default, password |
pickString |
从选项列表中选择 | options([{label, value}] 结构化列表), default |
command |
执行命令并使用其输出 | command, args |
from a2c_smcp.computer.mcp_clients.model import (
MCPServerPromptStringInput,
MCPServerPickStringInput,
MCPServerCommandInput,
)
# 密码输入
secret_input = MCPServerPromptStringInput(
id="API_SECRET",
description="API 密钥",
password=True,
)
# 选择输入(label 用于展示,value 是注入配置的实际值)
env_input = MCPServerPickStringInput(
id="ENVIRONMENT",
description="部署环境",
options=[
{"label": "开发", "value": "dev"},
{"label": "预发", "value": "staging"},
{"label": "生产", "value": "prod"},
],
default="dev",
)
# 命令输入
version_input = MCPServerCommandInput(
id="GIT_HASH",
description="当前 Git commit hash",
command="git",
args={"rev-parse": "HEAD"},
)
use smcp_computer::mcp_clients::model::{
MCPServerInput, PromptStringInput, PickStringInput, CommandInput,
};
// 密码输入
let secret = MCPServerInput::PromptString(PromptStringInput {
id: "API_SECRET".into(),
description: "API 密钥".into(),
default: None,
password: Some(true),
});
// 选择输入(label 用于展示,value 是注入配置的实际值)
let env = MCPServerInput::PickString(PickStringInput {
id: "ENVIRONMENT".into(),
description: "部署环境".into(),
options: vec![
PickStringOption { label: "开发".into(), value: "dev".into() },
PickStringOption { label: "预发".into(), value: "staging".into() },
PickStringOption { label: "生产".into(), value: "prod".into() },
],
default: Some("dev".into()),
});
// 命令输入
let version = MCPServerInput::Command(CommandInput {
id: "GIT_HASH".into(),
description: "当前 Git commit hash".into(),
command: "git rev-parse HEAD".into(),
args: None,
});
Rust SDK 扩展类型
Rust SDK 的 Input 系统额外支持 Number、Bool、FilePath 三种类型,这些是 SDK 级扩展,未包含在协议规范 TypedDict 中:
- Number — 数字输入,支持
min/max范围校验 - Bool — 布尔值输入,支持自定义
true_label/false_label - FilePath — 文件路径输入,支持
must_exist和filter校验
Input 解析策略¶
Input 值的解析遵循责任链模式,按顺序尝试多个提供者(Provider),首个成功的结果生效:
1. 环境变量 (EnvironmentInputProvider)
│ 未找到
▼
2. CLI 交互 (CliInputProvider)
│ 用户取消
▼
3. 默认值回退 (default 字段)
│ 无 default
▼
4. 首项回退(pickString 专属,MAY,不反向持久化)
这意味着在 CI/CD 环境中可以通过设置环境变量完全跳过交互式输入,而在本地开发时则会弹出交互提示。
pickString 的取值校验:已存值必须匹配至少一个 option.value,否则产出结构化 invalid_selection 错误(不回退 default 或首项);真正无用户值时才走解析链的 default → 首项回退(runtime-contract §5.12)。
环境变量命名规则(双端统一规范)¶
环境变量提供者使用以下命名规则查找对应的值(本节为双 SDK 通用规范,双端 MUST 逐字节一致):
- 前缀统一为
A2C_SMCP_(0.3.0 起;历史A2C_INPUT_前缀废止,直接硬切、无过渡期) - server 上下文段使用该 server 的
bundle_id(运行期唯一身份),不使用 display name ENV_SEGMENT规范化函数(全段统一同一个函数):保留大小写 + 将[^A-Za-z0-9_]逐字符替换为_- 保留大小写:POSIX env 名本就大小写敏感;折叠大小写会让
MyServer/myserver(两个合法且不同的 bundle_id)坍缩到同一变量名 - 字符替换:覆盖 bundle_id 中的
-,也覆盖 INPUT_ID 中可能出现的@、/(带前缀池条目如frontend@team/figma_token)
- 保留大小写:POSIX env 名本就大小写敏感;折叠大小写会让
- 坍缩 fail-fast:
ENV_SEGMENT非单射(如 bundle_ida-b与a_b均映射为a_b)。SDK MUST 在注册期计算全部活跃 env 变量名,检测到完整变量名相同(不同身份映射到同一变量名)即硬错误,提示用户为其一显式指定bundleId——与「同文件多 key 归一同 bundle_id fail-fast」同款模式。仅ENV_SEGMENT相同但 input id 不同的情形无害,不报错
示例:
| Input ID | Server 上下文(bundle_id) | 环境变量名 |
|---|---|---|
api_key |
无 | A2C_SMCP_api_key |
api_key |
feishu-mcp |
A2C_SMCP_api_key_feishu_mcp |
token |
MyServer + tool auth |
A2C_SMCP_token_MyServer_auth |
frontend@team/figma_token(带前缀池条目) |
无 | A2C_SMCP_frontend_team_figma_token |
环境变量会根据 Input 类型自动进行类型转换:
| Input 类型 | 转换规则 |
|---|---|
| String / PickString / FilePath | 直接使用字符串值 |
| Number | parse::<i64> |
| Bool | "true" / "1" / "yes" / "是" → true;"false" / "0" / "no" / "否" → false |
缓存机制¶
已解析的 Input 值会被缓存,单次渲染内相同 id 不会重复解析(同一 server 的单次 config 渲染中每个 input id 只解析一次)。缓存键中的 server 上下文 MUST 使用 bundle_id(与 env 命名一致,运行期唯一标识;使用 display name 会让同名 server 串用彼此的解析值):
- Python SDK:
InputResolver内部缓存,支持clear_cache()手动清除 - Rust SDK:
InputHandler按(input_id, bundle_id, tool_name, metadata)组合键缓存,支持with_cache(false)禁用
缓存不跨实际启动边界:每次实际 start / restart MUST 从 raw config 重新解析(runtime-contract §5.13)——用户改选后重启即生效;运行中进程不热更新。
安全注意事项¶
密码类 Input 与零凭证传播
password: true的 Input 解析值不应出现在日志输出中- Input 解析发生在 Computer 本地,解析后的值仅用于启动 MCP Server 子进程或建立连接
- 遵循 A2C-SMCP 零凭证传播原则:秘密信息始终留在 Computer 端,永远不会传递给 Agent
工具元数据 (ToolMeta)¶
ToolMeta 为每个工具附加额外的元数据,增强工具管理能力。规范性字段定义请参阅 数据结构 — ToolMeta。
字段说明¶
| 字段 | 类型 | 说明 |
|---|---|---|
auto_apply |
bool \| null |
是否跳过用户二次确认直接执行工具。null 表示使用客户端默认行为 |
alias |
str \| null |
工具别名,解决跨 Server 的同名工具冲突 |
tags |
list[str] \| null |
分类标签,便于 Agent 按类别筛选工具 |
ret_object_mapper |
dict \| null |
返回值字段映射表,将 MCP 工具原始返回字段映射到自定义字段名 |
extra 字段支持
Python SDK 的 ToolMeta 模型配置了 extra="allow",这意味着你可以在 ToolMeta 中添加任意自定义字段,它们会被完整保留并透传到 Agent 端。
default_tool_meta 与 tool_meta 合并策略¶
配置中可以同时指定 default_tool_meta(全局默认)和 tool_meta(按工具名指定);此外 MCP Server 还可在工具上声明默认 tags(Tool._meta["a2c_tool_meta"].tags,白名单仅此一项)。三者的合并遵循三层浅合并策略(规范权威见 data-structures §ToolMeta 三层合并规则):
- 如果某工具在
tool_meta中有专属配置,其非null字段覆盖default_tool_meta的同名字段 default_tool_meta中有而tool_meta中为null的字段保持默认值- Server 声明 tags 恒为最低层:配置两层未覆盖时默认生效;配置中
tags: []可显式清除下层(default_tool_meta与 Server 声明)值;Server 声明中的其它字段(如auto_apply)一律不生效(字段级过滤,tags仍生效) - 如果工具既无专属配置也无默认配置也无 Server 声明,则没有 ToolMeta
示例:
{
"default_tool_meta": {
"tags": ["browser"],
"auto_apply": true
},
"tool_meta": {
"navigate": {
"auto_apply": false
},
"screenshot": {
"tags": ["browser", "capture"],
"ret_object_mapper": {
"imageData": "screenshot_base64"
}
}
}
}
| 工具 | 合并结果 |
|---|---|
navigate |
tags=["browser"], auto_apply=false |
screenshot |
tags=["browser", "capture"], auto_apply=true, ret_object_mapper={...} |
| 其它工具 | tags=["browser"], auto_apply=true |
合并后的 ToolMeta 以 JSON 序列化后存入 SMCPTool.meta["a2c_tool_meta"]。详细的序列化规范请参阅 SMCPTool.meta 序列化规范。
Server 声明示例:Server 的工具 navigate 自带 _meta["a2c_tool_meta"] = {"tags": ["browser"]},无任何配置 → 出线 a2c_tool_meta 的 tags=["browser"];配置 tool_meta["navigate"].tags = [] → 出线 tags=[](Server 声明被显式清除)。
alias 实战:跨 Server 同名工具¶
post-BundleID,跨 Server 的工具同名本身已不构成冲突——工具暴露名恒为 {bundle_id}__{alias ?? 原始名},bundle_id 前缀在构造上保证跨 Server 唯一。alias 的作用是让暴露名的工具段更具业务语义(也可在暴露名上区分同一 server 的不同用途封装):
{
"servers": {
"web-search": {
"name": "web-search",
"type": "stdio",
"tool_meta": {
"search": { "alias": "web_search" }
},
"server_parameters": { "command": "web-search-mcp" }
},
"doc-search": {
"name": "doc-search",
"type": "stdio",
"tool_meta": {
"search": { "alias": "doc_search" }
},
"server_parameters": { "command": "doc-search-mcp" }
}
}
}
Agent 端看到的工具名是 web-search__web_search 和 doc-search__doc_search({bundle_id}__{alias};工具的 server 归属另由 SMCPTool.bundle_id 字段显式携带)。调用时 SDK 按暴露名整键查表路由回正确 MCP Server 的原始工具名。
default_tool_meta.alias 是完整替换语义
alias 写在 default_tool_meta 里会回落到该 server 的每一个未单独配置的工具——多个工具将拿到同一个 alias、撞出相同暴露名,除第一个外全部静默不可调用。alias 只应写在 tool_meta.<具体工具> 下。
ret_object_mapper 字段映射¶
不同 MCP Server 的工具返回值结构可能不一致。ret_object_mapper 提供了一个声明式字段映射表,方便前端使用统一组件进行渲染:
{
"tool_meta": {
"get_document": {
"ret_object_mapper": {
"doc_content": "content",
"doc_title": "title",
"doc_id": "id"
}
}
}
}
映射表的键是原始字段名,值是目标字段名。此映射信息随 ToolMeta 一起传递到 Agent 端,由 Agent 或前端决定是否应用。
VRL 脚本转换¶
概述¶
VRL (Vector Remap Language) 是一种用于数据转换的表达式语言。在 A2C-SMCP 中,VRL 脚本可以对 MCP 工具的 CallToolResult 进行二次加工,为返回值注入额外的元数据或进行结构化提取。
核心原则:VRL 转换是叠加式的。原始 CallToolResult 不受影响,转换结果以 JSON 字符串形式存入 meta["a2c_vrl_transformed"]。
执行流程¶
sequenceDiagram
participant Agent
participant Computer
participant MCP as MCP Server
Agent->>Computer: client:tool_call(name, params)
Computer->>MCP: call_tool(name, params)
MCP-->>Computer: CallToolResult
Note over Computer: 构造 VRL Event:<br/>CallToolResult 字段<br/>+ tool_name<br/>+ parameters
Computer->>Computer: 执行 VRL 脚本
Note over Computer: 结果存入<br/>meta["a2c_vrl_transformed"]
Computer-->>Agent: CallToolResult(原始 + VRL 结果)
VRL Event 输入格式¶
VRL 脚本接收的 Event 是 CallToolResult 的 JSON 序列化,额外注入两个上下文字段:
| 字段 | 类型 | 说明 |
|---|---|---|
content |
array |
原始 CallToolResult.content |
isError |
bool |
是否为错误结果 |
meta |
object \| null |
原始元数据 |
tool_name |
string |
被调用的工具名(注入) |
parameters |
object |
工具调用参数(注入) |
VRL 脚本可以自由读取和修改这些字段,最终的 Event 状态即为转换结果。
脚本示例¶
基础标记:
条件逻辑:
if .isError == true {
.status = "error"
.level = "warn"
} else {
.status = "success"
.level = "info"
}
基于工具名的分支处理:
if .tool_name == "search" {
.result_type = "search_result"
.query = .parameters.query
} else if .tool_name == "execute" {
.result_type = "execution_result"
.command = .parameters.cmd
}
结构化元数据提取:
.metadata = {
"processed": true,
"processor": "vrl-pipeline",
"original_content": .content[0].text
}
错误处理¶
VRL 失败不影响工具调用
VRL 脚本执行失败时,SDK 会记录警告日志,但不会影响正常的 CallToolResult 返回。这确保了 VRL 作为增强层的安全性——即使脚本有误,工具调用链路仍然完整。
语法验证¶
VRL 语法在配置加载阶段即进行校验:
这意味着 VRL 脚本错误会在 Computer 启动阶段被发现,而非在运行时首次调用工具时才暴露。
SDK 实现差异¶
| 特性 | Python SDK | Rust SDK |
|---|---|---|
| Input 类型 | promptString, pickString, command |
同左 + Number, Bool, FilePath |
| Input Provider | 简化实现(InputResolver) |
CompositeInputProvider 责任链 |
| 环境变量前缀 | A2C_SMCP_(双端统一规范,见 环境变量命名规则) |
同左(with_prefix() 仅限测试场景,生产 MUST 保持默认) |
| VRL 运行时 | vrl-python Python binding |
原生 Rust 集成(feature flag 可选) |
| Server 类型标签 | "streamable" |
"http"(alias "HTTP") |
| 健康检查 | 未内置 | HealthCheckConfig(间隔 30s,超时 5s) |
| 重连策略 | 未内置 | ReconnectPolicy(指数退避,最大 5 次) |
| 配置不可变性 | Pydantic frozen=True |
Rust 所有权模型天然保证 |
| ToolMeta extra 字段 | extra="allow" 允许自定义字段 |
严格按结构体定义 |
Rust SDK 健康检查与重连
Rust SDK 独有的 HealthCheckConfig 和 ReconnectPolicy 是 SDK 级实现,尚未纳入协议规范。它们提供了生产环境中 MCP Server 连接的自愈能力:
HealthCheckConfig:
| 字段 | 默认值 | 说明 |
|---|---|---|
interval_secs |
30 | 检查间隔(秒) |
timeout_secs |
5 | 单次检查超时(秒) |
enabled |
true | 是否启用 |
ReconnectPolicy:
| 字段 | 默认值 | 说明 |
|---|---|---|
enabled |
true | 是否启用自动重连 |
max_retries |
5 | 最大重试次数(0 = 无限) |
initial_delay_ms |
1000 | 初始延迟(ms) |
max_delay_ms |
30000 | 最大延迟(ms) |
backoff_factor |
2.0 | 退避因子 |
延迟计算公式:delay = initial_delay_ms × backoff_factor ^ retry_count,上限为 max_delay_ms。
完整配置参考¶
以下是一个可直接使用的完整配置模板,涵盖所有增强特性。字段说明见注释。
inputs 数组 — 定义所有动态占位符的来源:
[
{
"id": "API_TOKEN",
"type": "promptString",
"description": "API 访问令牌",
"password": true
},
{
"id": "WORKSPACE",
"type": "command",
"description": "自动获取工作区路径",
"command": "pwd"
},
{
"id": "MODEL",
"type": "pickString",
"description": "选择使用的模型",
"options": [
{ "label": "GPT-4", "value": "gpt-4" },
{ "label": "Claude 3", "value": "claude-3" },
{ "label": "Gemini Pro", "value": "gemini-pro" }
],
"default": "claude-3"
}
]
servers 字典 — 每个 MCP Server 的完整配置:
{
"coding-assistant": {
"type": "stdio",
"name": "coding-assistant",
"disabled": false,
"forbidden_tools": ["execute_shell"],
"default_tool_meta": {
"tags": ["coding"],
"auto_apply": true
},
"tool_meta": {
"write_file": {
"auto_apply": false,
"tags": ["coding", "filesystem"]
},
"search": {
"alias": "code_search",
"ret_object_mapper": {
"results": "matches",
"total": "match_count"
}
}
},
"vrl": "if .isError == false {\n .processed = true\n .tool_source = .tool_name\n}",
"server_parameters": {
"command": "npx",
"args": ["-y", "@anthropic/coding-mcp"],
"env": {
"API_TOKEN": "${input:API_TOKEN}",
"WORKSPACE": "${input:WORKSPACE}",
"MODEL": "${input:MODEL}"
},
"cwd": "/workspace"
}
}
}