BigPizzaV3/CodexPlusPlus · error · anyhow::Error
JSON 顶层必须是对象
Error message
JSON 顶层必须是对象
What it means
After JSON syntax validation, parse_mcp_servers_json requires the top-level JSON value to be an object, because all supported wrapper shapes ({mcpServers:...}, {servers:...}, bare {id:cfg}, single server object) are objects. Arrays, strings, numbers or booleans at top level are rejected with this message.
Solutions
- Wrap the array in an object: {"mcpServers": {...entries...}}.
- If you have a list of server configs, key each one by its id inside an object before importing.
- Validate with JSON.parse and check typeof parsed === 'object' && !Array.isArray(parsed) before calling.
Example fix
// before
parse_mcp_servers_json("[{\"command\":\"npx\"}]")
// after
parse_mcp_servers_json("{\"mcpServers\":{\"my-server\":{\"command\":\"npx\"}}}") Defensive patterns
Strategy: validation
Validate before calling
function isJsonObject(text) {
try {
const v = JSON.parse(text);
return typeof v === 'object' && v !== null && !Array.isArray(v);
} catch { return false; }
} Type guard
function isRecord(v) { return typeof v === 'object' && v !== null && !Array.isArray(v); } Try / catch
match parse_mcp_servers_json(input) {
Ok(import) => apply(import),
Err(e) if e.to_string().contains("顶层必须是对象") => ui.hint("JSON 顶层需为对象,如 {\"mcpServers\":{...}}"),
Err(e) => return Err(e),
} Prevention
- Validate top-level shape with JSON.parse + Array.isArray check before import
- Convert array exports to the {"mcpServers":{...}} object form first
- Document the accepted wrapper shapes in the import UI
- Reject fragments: ensure the pasted text parses as complete JSON
When it happens
Trigger: Passing input like "[{...}]" (array of servers), "\"text\"", "123", or "true" to parse_mcp_servers_json.
Common situations: User pastes a JSON array exported by another tool instead of the object form; a script serializes only the servers list; accidental double-selection pastes a fragment starting with '['.
Related errors
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/73e9b3471c81b87e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/mcp_config.rs:265
Ok(if text.trim().is_empty() {
String::new()
} else {
ensure_trailing_newline(text)
})
}
/// 解析社区文档里常见的 MCP JSON。
///
/// 接受四种外层形状:`{"mcpServers":{…}}`(Claude 标准)、`{"servers":{…}}`
/// (VS Code)、裸的 `{id: cfg}`,以及单个 server 对象(此时 id 留空由调用方填)。
pub fn parse_mcp_servers_json(json: &str) -> anyhow::Result<McpJsonImport> {
let trimmed = json.trim();
if trimmed.is_empty() {
anyhow::bail!("请先粘贴 MCP 配置 JSON");
}
let root: Value = serde_json::from_str(trimmed).context("不是合法的 JSON")?;
let Value::Object(root) = root else {
anyhow::bail!("JSON 顶层必须是对象");
};
let servers = if let Some(Value::Object(map)) = root.get("mcpServers") {
map.clone()
} else if let Some(Value::Object(map)) = root.get("servers") {
map.clone()
} else if looks_like_single_server(&root) {
// 单个 server 对象,没有 id,用占位 id 让用户在界面上改
let mut map = Map::new();
map.insert("mcp-server".to_string(), Value::Object(root));
map
} else {
root
};
if servers.is_empty() {
anyhow::bail!("JSON 里没有找到任何 MCP 服务器");
}View on GitHub (pinned to b1ed92e5e4)