feat(mcp): 客户端配置新增 Oh-My-Pi 片段

在 /admin/mcp 客户端配置展示区增加 Oh-My-Pi(omp)的 MCP 配置片段,
位于 Cline 与「通用」之间。

Oh-My-Pi 的 mcpServers 条目与 Claude Code/Cursor 的关键差异:字段名
是 transport(而非 type),值仍为 streamable-http,auth 同样走
headers.Authorization。配置文件落在 ~/.pi/agent/mcp.json(全局)或
.pi/mcp.json(项目级)。

改动:
- ClientConfigs / McpClientConfigs 新增 omp_json 字段
- generate_client_configs 生成 omp 片段
- ConfigCard 增加 ConfigSnippet 行
- 新增测试 omp_json_uses_transport_field_not_type,并纳入 pretty 校验循环

格式依据:2026 pi.dev / oh-my-pi 官方文档(联网核实)。
This commit is contained in:
xfy 2026-07-28 15:04:33 +08:00
parent 063ad6e606
commit 78fb51a310
3 changed files with 32 additions and 1 deletions

View File

@ -275,6 +275,8 @@ pub struct McpClientConfigs {
pub cursor_json: String,
/// Cline`cline_mcp_settings.json`)。
pub cline_json: String,
/// Oh-My-Pi`~/.pi/agent/mcp.json` / `.pi/mcp.json`)。字段名 `transport`,非 `type`。
pub omp_json: String,
/// 通用原始 JSON单 server entry
pub generic_json: String,
/// Claude Code CLI 一行命令。
@ -300,6 +302,7 @@ pub async fn get_mcp_client_configs(token: String) -> Result<McpClientConfigs, S
claude_code_json: configs.claude_code_json,
cursor_json: configs.cursor_json,
cline_json: configs.cline_json,
omp_json: configs.omp_json,
generic_json: configs.generic_json,
claude_cli: configs.claude_cli,
})

View File

@ -25,6 +25,9 @@ pub struct ClientConfigs {
/// Cline`cline_mcp_settings.json`)。`type: "streamableHttp"`(注意驼峰,非 `sse`
/// 额外带 `disabled` / `autoApprove` 字段。
pub cline_json: String,
/// Oh-My-Pi`~/.pi/agent/mcp.json` 全局 / `.pi/mcp.json` 项目级)。与其它客户端的
/// 关键差异:字段名是 `transport`(而非 `type`),值仍为 `streamable-http`。
pub omp_json: String,
/// 通用原始 JSON一个 server entry 的纯净形式,供其它兼容客户端粘贴。
pub generic_json: String,
/// Claude Code CLI 一行命令:`claude mcp add --transport http <name> <url> --header ...`。
@ -77,6 +80,17 @@ pub fn generate_client_configs(base_url: &str, token: &str) -> ClientConfigs {
}
});
// --- Oh-My-Pi字段名是 transport非 type值仍为 streamable-http ---
let omp_json = serde_json::json!({
"mcpServers": {
SERVER_NAME: {
"transport": "streamable-http",
"url": mcp_url,
"headers": { "Authorization": auth_header }
}
}
});
// --- 通用:单个 server entry 的纯净形式 ---
let generic_json = serde_json::json!({
"type": "streamable-http",
@ -94,6 +108,7 @@ pub fn generate_client_configs(base_url: &str, token: &str) -> ClientConfigs {
claude_code_json: pretty_json(&claude_code_json),
cursor_json: pretty_json(&cursor_json),
cline_json: pretty_json(&cline_json),
omp_json: pretty_json(&omp_json),
generic_json: pretty_json(&generic_json),
claude_cli,
}
@ -167,6 +182,18 @@ mod tests {
assert_eq!(v["url"], "https://rua.plus/mcp");
}
#[test]
fn omp_json_uses_transport_field_not_type() {
let cfg = generate_client_configs(BASE, TOKEN);
let v: serde_json::Value = serde_json::from_str(&cfg.omp_json).unwrap();
let entry = &v["mcpServers"]["yggdrasil"];
// 关键差异:字段名是 transport非 type
assert_eq!(entry["transport"], "streamable-http");
assert!(entry.get("type").is_none(), "omp 配置不应含 type 字段");
assert_eq!(entry["url"], "https://rua.plus/mcp");
assert_eq!(entry["headers"]["Authorization"], format!("Bearer {TOKEN}"));
}
#[test]
fn claude_cli_one_liner_contains_url_and_header() {
let cfg = generate_client_configs(BASE, TOKEN);
@ -179,7 +206,7 @@ mod tests {
fn all_json_is_pretty_indented() {
let cfg = generate_client_configs(BASE, TOKEN);
// pretty JSON 至少含一个换行 + 缩进(非单行紧凑形式)。
for s in [&cfg.claude_code_json, &cfg.cursor_json, &cfg.cline_json, &cfg.generic_json] {
for s in [&cfg.claude_code_json, &cfg.cursor_json, &cfg.cline_json, &cfg.omp_json, &cfg.generic_json] {
assert!(s.contains('\n'), "JSON 应是 pretty-printed: {s}");
assert!(s.contains(" "), "JSON 应含 2 空格缩进: {s}");
}

View File

@ -506,6 +506,7 @@ fn ConfigCard() -> Element {
ConfigSnippet { title: "Claude Code.mcp.json / ~/.claude.json".to_string(), content: c.claude_code_json.clone() }
ConfigSnippet { title: "Cursor~/.cursor/mcp.json".to_string(), content: c.cursor_json.clone() }
ConfigSnippet { title: "Clinecline_mcp_settings.json".to_string(), content: c.cline_json.clone() }
ConfigSnippet { title: "Oh-My-Pi~/.pi/agent/mcp.json 或 .pi/mcp.json".to_string(), content: c.omp_json.clone() }
ConfigSnippet { title: "通用(单 server entry".to_string(), content: c.generic_json.clone() }
ConfigSnippet { title: "Claude Code CLI".to_string(), content: c.claude_cli.clone() }
}