{"record":{"id":"f9d14dd6ab398a13","repo":"BigPizzaV3/CodexPlusPlus","slug":"json-mcp","errorCode":null,"errorMessage":"JSON 里没有找到任何 MCP 服务器","messagePattern":"JSON 里没有找到任何 MCP 服务器","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/mcp_config.rs","lineNumber":282,"sourceCode":"    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    };\n\n    if servers.is_empty() {\n        anyhow::bail!(\"JSON 里没有找到任何 MCP 服务器\");\n    }\n\n    let mut import = McpJsonImport::default();\n    for (id, config) in servers {\n        let Value::Object(config) = config else {\n            import.warnings.push(format!(\"已跳过 {id}：配置不是对象\"));\n            continue;\n        };\n        match json_server_to_toml(&id, &config, &mut import.warnings) {\n            Ok(toml_body) => import.entries.push(McpJsonEntry { id, toml_body }),\n            Err(error) => import.warnings.push(format!(\"已跳过 {id}：{error}\")),\n        }\n    }\n\n    if import.entries.is_empty() {\n        anyhow::bail!(\"JSON 里没有可导入的 MCP 服务器\");\n    }\n    Ok(import)","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/mcp_config.rs#L264-L300","documentation":"parse_mcp_servers_json resolves the servers map from one of the supported wrapper shapes and then requires it to contain at least one server entry. If the resolved map is empty — e.g. {\"mcpServers\":{}} — there is nothing to import and the function bails.","triggerScenarios":"Calling parse_mcp_servers_json with '{\"mcpServers\":{}}', '{\"servers\":{}}', or '{}' (an object that is neither wrapper nor single server, resolving to an empty map).","commonSituations":"Config file was emptied by another tool; user deleted all servers in the source app then exported; keys exist but values were stripped by an export filter.","solutions":["Include at least one server entry under mcpServers/servers before importing.","If the object is meant as a single server, give it a command or url field so looks_like_single_server recognizes it.","Check the source config actually contains servers before triggering import."],"exampleFix":"// before\n{\"mcpServers\":{}}\n// after\n{\"mcpServers\":{\"fetch\":{\"command\":\"uvx\",\"args\":[\"mcp-server-fetch\"]}}}","handlingStrategy":"validation","validationCode":"function hasServers(text) {\n  const v = JSON.parse(text);\n  const map = v.mcpServers ?? v.servers ?? (isSingleServer(v) ? {single: v} : v);\n  return map && Object.keys(map).length > 0;\n}","typeGuard":null,"tryCatchPattern":"match parse_mcp_servers_json(&text) {\n    Ok(import) => apply(import),\n    Err(e) if e.to_string().contains(\"没有找到任何\") => ui.hint(\"配置里没有任何服务器条目\"),\n    Err(e) => return Err(e),\n}","preventionTips":["Verify Object.keys on the servers map before calling import","Don't export empty config files as import sources","Check the source app still has servers configured","Treat {} and {\"mcpServers\":{}} as invalid input at the UI layer"],"tags":["json","mcp","empty","validation"],"backgroundTag":"empty-result-set","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"}