{"record":{"id":"ac70e2b4e829813b","repo":"BigPizzaV3/CodexPlusPlus","slug":"mcp-json","errorCode":null,"errorMessage":"请先粘贴 MCP 配置 JSON","messagePattern":"请先粘贴 MCP 配置 JSON","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/mcp_config.rs","lineNumber":261,"sourceCode":"        }\n    }\n\n    let text = doc.to_string();\n    Ok(if text.trim().is_empty() {\n        String::new()\n    } else {\n        ensure_trailing_newline(text)\n    })\n}\n\n/// 解析社区文档里常见的 MCP JSON。\n///\n/// 接受四种外层形状：`{\"mcpServers\":{…}}`（Claude 标准）、`{\"servers\":{…}}`\n/// （VS Code）、裸的 `{id: cfg}`，以及单个 server 对象（此时 id 留空由调用方填）。\npub fn parse_mcp_servers_json(json: &str) -> anyhow::Result<McpJsonImport> {\n    let trimmed = json.trim();\n    if trimmed.is_empty() {\n        anyhow::bail!(\"请先粘贴 MCP 配置 JSON\");\n    }\n    let root: Value = serde_json::from_str(trimmed).context(\"不是合法的 JSON\")?;\n    let Value::Object(root) = root else {\n        anyhow::bail!(\"JSON 顶层必须是对象\");\n    };\n\n    let servers = if let Some(Value::Object(map)) = root.get(\"mcpServers\") {\n        map.clone()\n    } else if let Some(Value::Object(map)) = root.get(\"servers\") {\n        map.clone()\n    } else if looks_like_single_server(&root) {\n        // 单个 server 对象，没有 id，用占位 id 让用户在界面上改\n        let mut map = Map::new();\n        map.insert(\"mcp-server\".to_string(), Value::Object(root));\n        map\n    } else {\n        root\n    };","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/mcp_config.rs#L243-L279","documentation":"parse_mcp_servers_json rejects empty input before attempting to parse JSON. Pasting nothing (or only whitespace) means there is nothing to import, so the function fails fast with this user-facing message instead of a confusing JSON syntax error.","triggerScenarios":"Calling parse_mcp_servers_json(\"\") or with a whitespace-only string (\" \\n\\t\").","commonSituations":"User clicks Import in the manager UI without pasting config; clipboard copy failed silently; a script passes an unset/empty variable holding the JSON.","solutions":["Paste a valid MCP servers JSON document before calling import.","Guard the call site: if input.trim().is_empty(), show the prompt/error without calling the parser.","If the JSON is read from a file, check the file is non-empty before reading it into the argument."],"exampleFix":"// before\nparse_mcp_servers_json(userInput)\n// after\nif (userInput.trim().length === 0) { showError(\"请先粘贴 MCP 配置 JSON\"); return; }\nparse_mcp_servers_json(userInput);","handlingStrategy":"validation","validationCode":"if (!jsonText || jsonText.trim().length === 0) {\n  throw new Error(\"请先粘贴 MCP 配置 JSON\");\n}","typeGuard":null,"tryCatchPattern":"match parse_mcp_servers_json(&input) {\n    Ok(import) => apply(import),\n    Err(e) => ui.show_import_error(&e.to_string()), // shows '请先粘贴 MCP 配置 JSON'\n}","preventionTips":["Disable the Import button until the textarea is non-empty","Check clipboard paste actually delivered content","Trim user input before validation and calls","When reading from files, verify file size > 0 first"],"tags":["validation","mcp","json","empty-input"],"backgroundTag":"empty-required-field","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}