diff --git a/src/api/mcp_tokens.rs b/src/api/mcp_tokens.rs index ac0bd30..3d84078 100644 --- a/src/api/mcp_tokens.rs +++ b/src/api/mcp_tokens.rs @@ -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 --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}"); } diff --git a/src/pages/admin/mcp.rs b/src/pages/admin/mcp.rs index e69bfe8..e1f4549 100644 --- a/src/pages/admin/mcp.rs +++ b/src/pages/admin/mcp.rs @@ -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: "Cline(cline_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() } }