{"record":{"id":"b185868155ae2495","repo":"BigPizzaV3/CodexPlusPlus","slug":"command-url","errorCode":null,"errorMessage":"缺少 command 或 url","messagePattern":"缺少 command 或 url","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/mcp_config.rs","lineNumber":375,"sourceCode":"        json_map(config, \"headers\")\n    } else {\n        Vec::new()\n    };\n\n    if let Some(timeout) = config.get(\"startup_timeout_sec\").and_then(Value::as_f64) {\n        form.startup_timeout_sec = format_number(timeout);\n    }\n    if let Some(enabled) = config.get(\"enabled\").and_then(Value::as_bool) {\n        form.enabled = enabled;\n    }\n\n    form.transport = if form.url.is_empty() {\n        McpTransport::Stdio\n    } else {\n        McpTransport::Http\n    };\n    if form.transport == McpTransport::Stdio && form.command.trim().is_empty() {\n        anyhow::bail!(\"缺少 command 或 url\");\n    }\n\n    build_mcp_toml_body(&form)\n}\n\nfn json_string(config: &Map<String, Value>, key: &str) -> String {\n    config\n        .get(key)\n        .and_then(Value::as_str)\n        .unwrap_or_default()\n        .to_string()\n}\n\nfn json_map(config: &Map<String, Value>, key: &str) -> Vec<McpKeyValue> {\n    config\n        .get(key)\n        .and_then(Value::as_object)\n        .map(|map| {","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/mcp_config.rs#L357-L393","documentation":"json_server_to_toml infers the transport from the url field: empty url means Stdio, which requires a command to launch. If the entry has neither a url nor a non-empty command, no executable transport can be built and the conversion bails; this surfaces either directly or as a per-entry warning ('已跳过 {id}: ...') during JSON import.","triggerScenarios":"A server object in the JSON lacks both 'command' and 'url' (or both are empty/whitespace), e.g. {\"myserver\":{\"args\":[...]}} passed through parse_mcp_servers_json.","commonSituations":"Copied config from a tool that uses different key names (e.g. 'cmd', 'endpoint', 'serverUrl'); entry describing a disabled placeholder; hand-written JSON missing the launch field.","solutions":["Add a 'command' field (with args if needed) for stdio servers.","Add a 'url' field for HTTP servers instead.","Rename incorrectly cased keys (command/url must be lowercase, exact).","Remove placeholder entries that have no launch configuration."],"exampleFix":"// before\n{\"myserver\":{\"args\":[\"--port\",\"8080\"]}}\n// after\n{\"myserver\":{\"command\":\"node\",\"args\":[\"server.js\",\"--port\",\"8080\"]}}","handlingStrategy":"validation","validationCode":"function entryHasTransport(cfg) {\n  return (typeof cfg.url === 'string' && cfg.url.trim() !== '') ||\n         (typeof cfg.command === 'string' && cfg.command.trim() !== '');\n}","typeGuard":"function isStdioServer(c) { return typeof c.command === 'string' && c.command.trim() !== ''; }\nfunction isHttpServer(c) { return typeof c.url === 'string' && c.url.trim() !== ''; }","tryCatchPattern":"match json_server_to_toml(&config) {\n    Ok(body) => /* ... */,\n    Err(e) if e.to_string().contains(\"缺少 command 或 url\") => warn!(\"跳过 {id}: {e}\"),\n    Err(e) => return Err(e),\n}","preventionTips":["Require command or url in the server editor form before save","Map alternate key names (cmd, serverUrl, endpoint) to command/url on import","Lint pasted JSON against the expected server schema","Drop or comment out placeholder entries with no transport"],"tags":["mcp","validation","json","missing-field"],"backgroundTag":"missing-required-argument","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"}