MCP Server 配置规范¶
范围:TFRobot Plugin 内
mcp-servers/子树的目录约定与配置 schema 摘要。 读者:Plugin 作者(撰写指南)+ 接入方实施工程师(加载使用层最小契约)。 Schema 权威:A2C-SMCP 协议规范 § MCP Server 配置结构(当前对齐版本 v0.3.2)。
0. 总览¶
0.1 这是什么¶
TFRobot Plugin 可在 <plugin>/mcp-servers/ 下声明若干 MCP Server 配置;每个配置是一份独立 JSON 文件,结构遵守 A2C-SMCP MCPServerConfig 联合类型。Robot 在加载 Plugin 时把这些配置交给 Computer 侧,由 Computer 通过 A2C-SMCP 暴露给 LLM 工具循环。
0.2 协议边界¶
| 维度 | 本规范 | A2C-SMCP |
|---|---|---|
Plugin 内 mcp-servers/ 目录约定 |
✅ 规定 | 不涉及 |
| 单份 MCP Server 配置 JSON 结构 | ❌ 摘要 / 速查 | ✅ 权威 |
占位符语法(${input:<id>} / ${env:<name>} / 预定义变量) |
❌ 摘要 / 速查 | ✅ 权威 |
| MCP 连接生命周期、协议握手、tool 调用语义 | 不涉及 | ✅ 权威 |
本规范不引入:MCP 嵌入 plugin.json(保持文件式而非内嵌)、跨 plugin 共享 inputs.json(限 plugin 范围)、Schema 拓展(完全等同 A2C-SMCP v0.3.2)。
1. 目录约定¶
<plugin>/
.tfrobot-plugin/plugin.json
mcp-servers/ # 可选 —— 若存在,至少含 1 个 server.json
<server-name>.json # 单个 MCP Server 配置(§2 / §3)
inputs.json # 可选 —— 占位符输入定义(§4)
| 路径 | 必填 | 内容 |
|---|---|---|
<plugin>/mcp-servers/ |
否;存在则非空 | MCP Server 配置子树 |
<plugin>/mcp-servers/<server-name>.json |
子树存在时至少 1 个 | 单个 MCP Server 配置 |
<plugin>/mcp-servers/inputs.json |
否 | 占位符输入定义数组 |
1.1 命名与对齐约束¶
| 项 | 约束 |
|---|---|
<server-name> 字符集 |
[a-z0-9-],kebab-case,不以 - 开头/结尾,无连续 --(与 SKILL 名一致) |
文件名 ↔ 配置内 name |
必须严格相等(去掉 .json 后缀)。注意 name 是纯 display、非身份(唯一身份是 bundle_id,见 §2) |
inputs.json 作用域 |
Plugin 范围内共享;该 plugin 下所有 server.json 均可引用其中的 input。入池时 id 前缀化为 <plugin>@<marketplace>/<id>(跨 plugin 消歧);server.json 内写裸 ${input:<id>},解析时经前缀回退命中 |
1.2 加载方义务¶
Robot / 加载方装载 plugin 时:
- 若
mcp-servers/存在且非空 → 枚举所有*.json(除inputs.json) - 对每份 JSON 按 §2/§3 解析为
MCPServerConfig - 收集
inputs.json(若存在);按 A2C-SMCP 约定的占位符语法(${input:<id>}/${env:<name>}/ 预定义${userHome}${pathSeparator})在传输参数 / 环境变量 / headers 等字段内做占位符展开 - 转交 Computer 侧;执行后端 / 凭证 / 进程管理由 Computer 按 A2C-SMCP 自行处理
2. 通用字段(BaseMCPServerConfig)¶
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
name |
string | 是 | MCP Server 名称(纯 display,人类可读)。必须等于文件名(去掉 .json);允许碰撞、永不做键/寻址——唯一身份是 bundle_id |
bundle_id |
string | null | 否 | MCP Server 唯一身份(软件级 BundleID,0.3.0 起)。省略时由 name 经确定性算法派生(解析后恒有值、不回写配置源);显式值须为 [A-Za-z0-9_-]、禁 . 与连续 __;同一 bundle_id 视为同一软件,不允许多开 |
type |
enum | 是 | 传输模式:stdio / streamable / sse |
disabled |
boolean | 否 | 是否禁用;加载时跳过(默认 false) |
forbidden_tools |
array<string> | 否 | 工具名黑名单(默认空数组) |
tool_meta |
object | 否 | 工具名 → ToolMeta 映射(默认空对象;见 §5) |
default_tool_meta |
ToolMeta | null | 否 | 默认 ToolMeta;具体工具未配置时使用 |
vrl |
string | null | 否 | VRL 脚本,用于动态转换工具返回值 |
envFile |
string | null | 否 | SDK 加性字段(协议未收录):VS Code 对标(v0.2.1 #65),stdio 专属;spawn 时从 .env 加载 KEY=VALUE 进 stdio server 的 env,显式 env 同名项覆盖 envFile;非 stdio(sse / http 无 env)忽略 |
oauth |
object | null | 否 | SDK 加性字段(协议未收录):streamable 专属 OAuth 配置(对齐 Rust HttpServerConfig.oauth) |
server_parameters |
object | 是 | 传输参数;类型由 type 字段分派(§3) |
type = "streamable"对应 MCP 官方的 Streamable HTTP 传输模式。
BundleID 模型:name 非身份,bundle_id 才是(0.3.0 起)
文件名 = name 的目录约定未变(文件 stem 仍强制等于 name),但 name 已降级为纯 display:允许碰撞、永不做键/寻址。MCP Server 的唯一身份是 bundle_id——client:get_config 的 servers 字典 key、SKILL mcp 形态名的 <server> 段、错误码 meta.mcp_server 全用 bundle_id。聚合后暴露给 LLM 的工具名为 {bundle_id}__{alias ?? 原始工具名}(alias 语义见 §5)。
缺省派生(bundle_id 省略时):按 Unicode 码点规范化 name——非 [A-Za-z0-9_-] 码点替换为 _、折叠连续 _、裁剪首尾 [_-];结果非空即 bundle_id;为空(全符号 / CJK / 空串)则 bundle_ + SHA-256(connection-identity 字节串)[:8] 小写 hex。算法逐字节确定、跨 SDK 一致(协议仓托管一致性测试向量),在加载/注册期完成(derive-on-load),不回写配置源(如 <server-name>.json)。
显式 bundle_id 含 . / __ / 字符集越界属非法配置——按平台诊断处理(rust 侧在 parse 期整个 plugin 判废);省略 bundle_id 不算错误(触发缺省生成)。注意规范化非单射:my server / my-server 均 → my_server,两个都叫 everything 的 server 缺省生成后会撞同一 bundle_id → 多开冲突,由配置人员显式指定 bundle_id 消歧。
3. 传输参数(按 type 分派)¶
3.1 type = "stdio"(MCPServerStdioParameters)¶
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
command |
string | 是 | 启动命令 |
args |
array<string> | 是 | 命令行参数 |
env |
object | null | 是 | 环境变量 map |
cwd |
string | null | 是 | 工作目录 |
encoding |
string | 是 | 文本编码,默认 utf-8(协议 TypedDict 必填;⚠️ 双 SDK 当前均未消费,serde 静默忽略) |
encoding_error_handler |
enum | 是 | strict / ignore / replace(同上:协议必填、SDK 暂未消费) |
3.2 type = "streamable"(MCPServerStreamableHttpParameters)¶
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
url |
string | 是 | 端点 URL |
headers |
object | null | 是 | HTTP 请求头 |
timeout |
string | 是 | HTTP 超时(ISO 8601 duration,如 "PT30S") |
sse_read_timeout |
string | 是 | SSE 读取超时(ISO 8601 duration) |
terminate_on_close |
boolean | 是 | 关闭时是否终止客户端会话 |
oauth |
object | null | 否 | SDK 加性字段(协议未收录):OAuth 配置(对齐 Rust HttpServerConfig.oauth),streamable 专属 |
3.3 type = "sse"(MCPSSEParameters)¶
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
url |
string | 是 | 端点 URL |
headers |
object | null | 是 | HTTP 请求头 |
timeout |
number | 是 | HTTP 超时(秒) |
sse_read_timeout |
number | 是 | SSE 读取超时(秒) |
4. 占位符输入(inputs.json)¶
inputs.json 是数组;每个元素是一个 input 定义,被 MCP Server 配置中的占位符引用。
占位符语法面(A2C-SMCP 权威;SDK 实际支持):
| 占位符 | 语义 |
|---|---|
${input:<id>} |
经 input 解析链取值(本节定义的 input 的注入值) |
${env:<NAME>} |
进程环境变量;缺失 → 空串 + WARN |
${userHome} / ${pathSeparator} |
预定义变量(用户主目录 / 路径分隔符) |
${workspaceFolder} |
⚠️ 已弃用(随 workdir 概念瘦身移除,仅 WARN;改用 ${input:<id>} 或绝对路径) |
未知占位符保持原样(向后兼容)。
4.1 通用基础字段(MCPServerInputBase)¶
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
id |
string | 是 | 输入 ID(占位符引用键) |
description |
string | 是 | 描述 |
type |
enum | 是 | promptString / pickString / command(协议 Literal 小写。⚠️ rust-sdk 当前仅接受 PascalCase(PromptString),按本文档示例写的配置在 Rust 侧会被判废——已知 Rust 缺口,应以协议为准,建议 Rust 补 alias) |
4.2 type = "promptString"¶
附加字段:
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
default |
string | null | 否 | 默认值 |
password |
boolean | null | 否 | 是否为密码(隐藏输入) |
4.3 type = "pickString"¶
附加字段:
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
options |
array<PickStringOption> | 是 | 可选项列表(结构化 {label, value}) |
default |
string | null | 否 | 默认值(若存在且非 None,MUST 匹配至少一个 option.value) |
PickStringOption:label(展示标签,UI 呈现)+ value(传值,注入 ${input:<id>} 的实际值)——两者解耦,选项可以是机器值(区域码 / URL / ID)而不牺牲可读性。
约束(MUST):
options至少 1 项;每个label/value非空字符串label与value均允许重复(不要求唯一);SDK / client MUST NOT 按value反推原选labeldefault显式null视为无默认(不拒绝,解析时走无值解析链)
0.3.2 破坏性变更:旧 options: list[str] 直接拒绝
0.3.2 起 options 为结构化 [{label, value}] 列表。旧形式 "options": ["a", "b"](字符串数组)是非法 config,SDK MUST 以 validation 错误拒绝(报错应指路新结构)。不提供 alias、不设迁移期——协议尚未正式上线,无存量兼容包袱。
双 SDK 采纳状态:rust-sdk / python-sdk 的 develop 目前仍为 Vec<String>(protocol #48 半部推进中,python-sdk v0.3.2 实测亦为 list[str])。文档既定策略是以 A2C-SMCP 为权威——作者应按新结构撰写;SDK 跟进后再完全互通。
4.4 type = "command"¶
附加字段:
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
command |
string | 是 | 要执行的命令 |
args |
object | null | 否 | 命令参数 map |
5. ToolMeta¶
工具级元数据,用于 §2 中的 tool_meta 与 default_tool_meta 字段:
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
auto_apply |
boolean | null | 否 | 是否自动应用(跳过二次确认) |
ret_object_mapper |
object | null | 否 | 返回值对象映射,用于统一不同 MCP 工具的返回格式 |
alias |
string | null | 否 | 工具别名。仅替换 exposed_tool_name 的工具名部分——最终暴露名恒为 {bundle_id}__{alias ?? 原始工具名},{bundle_id}__ 前缀不变(0.3.0 起跨 server 冲突已由 bundle_id 前缀结构性解决;alias 用于适配下游命名约束,如含连字符 / 保留字的工具名) |
tags |
array<string> | null | 否 | 工具标签,用于分类 |
6. 完整示例¶
6.1 stdio 模式¶
plugins/data-toolkit/mcp-servers/postgres-explorer.json:
{
"name": "postgres-explorer",
"bundle_id": "postgres-explorer",
"type": "stdio",
"disabled": false,
"forbidden_tools": ["drop_table", "truncate"],
"tool_meta": {
"describe_table": {
"auto_apply": true,
"tags": ["read-only"]
}
},
"default_tool_meta": null,
"vrl": null,
"server_parameters": {
"command": "uvx",
"args": ["mcp-server-postgres", "--host", "${input:db-host}"],
"env": {
"POSTGRES_PASSWORD": "${input:db-password}"
},
"cwd": null,
"encoding": "utf-8",
"encoding_error_handler": "strict"
}
}
bundle_id可省略(缺省生成自name,见 §2);disabled/forbidden_tools/tool_meta/default_tool_meta/vrl均有默认值,可省略。
plugins/data-toolkit/mcp-servers/inputs.json:
[
{
"id": "db-host",
"description": "PostgreSQL 主机地址",
"type": "promptString",
"default": "localhost"
},
{
"id": "db-password",
"description": "PostgreSQL 密码",
"type": "promptString",
"password": true
}
]
6.2 streamable 模式¶
<plugin>/mcp-servers/web-search.json:
{
"name": "web-search",
"type": "streamable",
"disabled": false,
"forbidden_tools": [],
"tool_meta": {},
"server_parameters": {
"url": "https://mcp.example.com/web-search",
"headers": {
"Authorization": "Bearer ${input:api-token}"
},
"timeout": "PT30S",
"sse_read_timeout": "PT5M",
"terminate_on_close": true
}
}
6.3 sse 模式¶
<plugin>/mcp-servers/log-stream.json:
{
"name": "log-stream",
"type": "sse",
"disabled": false,
"forbidden_tools": [],
"tool_meta": {},
"server_parameters": {
"url": "https://mcp.example.com/log-stream",
"headers": null,
"timeout": 30.0,
"sse_read_timeout": 300.0
}
}
7. 权威来源与版本对齐¶
| 项 | 值 |
|---|---|
| Schema 权威 | A2C-SMCP 协议规范 § MCP Server 配置结构 |
| 当前对齐版本 | A2C-SMCP v0.3.2 |
| 同步策略 | 随 A2C-SMCP 协议版本同步(以协议为权威);SDK 补丁版本(bugfix 末位)不构成同步触发 |
| 不重复设计 | 本规范不为 MCP 配置发明替代字段、替代命名、替代占位符语法;歧义以 A2C-SMCP 为权威 |
8. 不引入¶
| 不引入项 | 理由 |
|---|---|
MCP 配置内嵌 plugin.json |
文件式与 SKILL 子树设计对齐;JSON 嵌套膨胀风险 |
跨 plugin 共享 inputs.json |
限 plugin 范围;跨 plugin 共享需 registry 层支持,推迟到未来 |
| Schema 扩展(自定义传输类型、自定义占位符语法) | 完全对齐 A2C-SMCP 是核心原则 |
| MCP 连接生命周期 / tool 调用语义 | 属于 A2C-SMCP 与 MCP 协议本身,不在本规范范围 |