fix(mcp): 修正 Claude Code / Cursor 客户端配置格式(联网核实 2026)
依据 2026 官方文档联网核实所有 MCP 客户端配置格式,发现两处错误: 1. Claude Code .mcp.json:type 值应为 "http",不是 "streamable-http"。 Claude Code 用 streamable-http 会静默失败/卡在 connecting 状态。 2. Cursor ~/.cursor/mcp.json:不应带 type 字段——Cursor 按远程 URL 自动 识别 streamable-http,仅需 url + headers。此前 cursor_json 直接 clone claude_code_json,继承了错误的 type 字段。 其余配置核实无误:Cline(streamableHttp 驼峰 + disabled/autoApprove)、 Claude Code CLI(--transport http --header)、Oh-My-Pi(transport 字段)。 改动: - 拆分 cursor_json,不再 clone claude_code_json - 更新对应 doc 注释(config.rs / mcp_tokens.rs DTO) - claude_code 测试断言改为 type=="http" - cursor_json_equals_claude_code_json → cursor_json_has_no_type_field
This commit is contained in:
parent
78fb51a310
commit
53376a0373
@ -269,9 +269,9 @@ fn row_to_mcp_token_meta(row: &tokio_postgres::Row) -> McpToken {
|
||||
/// 为可两端共享的 DTO,让 WASM 前端能经 server fn 拿到配置字符串。
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct McpClientConfigs {
|
||||
/// Claude Code(`.mcp.json` / `~/.claude.json`)与 Cursor(`~/.cursor/mcp.json`)。
|
||||
/// Claude Code(`.mcp.json` / `~/.claude.json`)。`type: "http"`(非 `streamable-http`)。
|
||||
pub claude_code_json: String,
|
||||
/// Cursor 专用变体(与 claude_code_json 相同,单独列出便于标注)。
|
||||
/// Cursor(`~/.cursor/mcp.json`)。不带 `type` 字段,按 URL 自动识别 streamable-http。
|
||||
pub cursor_json: String,
|
||||
/// Cline(`cline_mcp_settings.json`)。
|
||||
pub cline_json: String,
|
||||
|
||||
@ -16,11 +16,12 @@ use serde::Serialize;
|
||||
/// `token` 形如 `ygg_...`,已嵌入各片段的 `Authorization` 头中。
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ClientConfigs {
|
||||
/// Claude Code(`.mcp.json` / `~/.claude.json`)与 Cursor(`~/.cursor/mcp.json`)。
|
||||
/// `type: "streamable-http"`,`url`,`headers.Authorization`。
|
||||
/// Claude Code(`.mcp.json` / `~/.claude.json`)。注意 `type` 值是 `"http"`
|
||||
///(不是 `"streamable-http"`——Claude Code 用 `"streamable-http"` 会静默失败/卡在
|
||||
/// "connecting",2026 官方文档明确要求 `http`)。字段:`type`,`url`,`headers.Authorization`。
|
||||
pub claude_code_json: String,
|
||||
/// Cursor 专用变体(`type: "streamable-http"`)——与 claude_code_json 实际相同,
|
||||
/// 单独列出便于 UI 分别展示与标注文件路径。
|
||||
/// Cursor(`~/.cursor/mcp.json`)。与 Claude Code 的关键差异:**不带 `type` 字段**——
|
||||
/// Cursor 按远程 URL 自动识别 streamable-http;仅需 `url` + `headers.Authorization`。
|
||||
pub cursor_json: String,
|
||||
/// Cline(`cline_mcp_settings.json`)。`type: "streamableHttp"`(注意驼峰,非 `sse`),
|
||||
/// 额外带 `disabled` / `autoApprove` 字段。
|
||||
@ -54,18 +55,26 @@ pub fn generate_client_configs(base_url: &str, token: &str) -> ClientConfigs {
|
||||
let mcp_url = join_mcp_url(base_url);
|
||||
let auth_header = format!("Bearer {token}");
|
||||
|
||||
// --- Claude Code / Cursor:type = "streamable-http" ---
|
||||
// --- Claude Code:type = "http"(非 "streamable-http",否则静默连接失败) ---
|
||||
let claude_code_json = serde_json::json!({
|
||||
"mcpServers": {
|
||||
SERVER_NAME: {
|
||||
"type": "streamable-http",
|
||||
"type": "http",
|
||||
"url": mcp_url,
|
||||
"headers": { "Authorization": auth_header }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// --- Cursor:不带 type 字段,按 URL 自动识别 streamable-http ---
|
||||
let cursor_json = serde_json::json!({
|
||||
"mcpServers": {
|
||||
SERVER_NAME: {
|
||||
"url": mcp_url,
|
||||
"headers": { "Authorization": auth_header }
|
||||
}
|
||||
}
|
||||
});
|
||||
// Cursor 与 Claude Code 格式一致(type 都接受 streamable-http),仅展示标签不同。
|
||||
let cursor_json = claude_code_json.clone();
|
||||
|
||||
// --- Cline:type = "streamableHttp"(驼峰),带 disabled / autoApprove ---
|
||||
let cline_json = serde_json::json!({
|
||||
@ -149,14 +158,19 @@ mod tests {
|
||||
v["mcpServers"]["yggdrasil"]["headers"]["Authorization"],
|
||||
format!("Bearer {TOKEN}")
|
||||
);
|
||||
assert_eq!(v["mcpServers"]["yggdrasil"]["type"], "streamable-http");
|
||||
assert_eq!(v["mcpServers"]["yggdrasil"]["type"], "http"); // 非 "streamable-http"(会静默失败)
|
||||
assert_eq!(v["mcpServers"]["yggdrasil"]["url"], "https://rua.plus/mcp");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cursor_json_equals_claude_code_json() {
|
||||
fn cursor_json_has_no_type_field() {
|
||||
let cfg = generate_client_configs(BASE, TOKEN);
|
||||
assert_eq!(cfg.cursor_json, cfg.claude_code_json);
|
||||
let v: serde_json::Value = serde_json::from_str(&cfg.cursor_json).unwrap();
|
||||
let entry = &v["mcpServers"]["yggdrasil"];
|
||||
// Cursor 按 URL 自动识别远程端点,不带 type 字段。
|
||||
assert!(entry.get("type").is_none(), "cursor 配置不应含 type 字段");
|
||||
assert_eq!(entry["url"], "https://rua.plus/mcp");
|
||||
assert_eq!(entry["headers"]["Authorization"], format!("Bearer {TOKEN}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user