Kuberwastaken/claurst · error
mcpServers. is missing command/url
Error message
mcpServers.{name} is missing command/url What it means
parse_mcp_servers requires every MCP server entry to define at least one of "command" (stdio server) or "url" (HTTP/SSE server), both as strings. An object with neither is unusable and produces this error naming the server.
Solutions
- Add "command": "<executable>" for a stdio server, or "url": "<endpoint>" for a remote server
- Rename tool-specific keys (cmd/serverUrl/endpoint) to command/url expected by the importer
- Quote the command value as a string rather than a number or array
Example fix
// before
{ "mcpServers": { "fs": { "cmd": "npx" } } }
// after
{ "mcpServers": { "fs": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-fs"] } } } Defensive patterns
Strategy: validation
Validate before calling
for (const [name, e] of Object.entries(cfg.mcpServers ?? {})) {
if (typeof e.command !== 'string' && typeof e.url !== 'string') throw new Error(`mcpServers.${name} needs string "command" or "url"`);
} Type guard
const isRunnableServer = (e) => !!e && typeof e === 'object' && (typeof e.command === 'string' || typeof e.url === 'string');
Try / catch
try { importConfig(path) } catch (e) { if (String(e).includes('is missing command/url')) { console.error('Add a string "command" or "url" to the named server entry'); process.exitCode = 1; } else { throw e; } } Prevention
- Every server entry needs either command (stdio) or url (remote) as a plain string
- Translate field names when copying from other tools (cmd -> command, serverUrl -> url)
- Test-import each server entry individually before committing the config
When it happens
Trigger: Importing an mcpServers entry like {"fs": {"args": ["x"]}} or {"fs": {"command": 123}} (command present but not a string) so both extracted values are None.
Common situations: Config from another tool using different field names (e.g. "cmd", "endpoint", "serverUrl"), a server entry left empty after removing a key, or command defined as a non-string (number/array).
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- mcpServers must be an object
- mcpServers. must be an object
- hooks. hook is missing command
- Bridge session registration failed: authentication error
- hooks must be an object
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/df9488f7aa6ea3b7.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/core/src/import_config.rs:663
let Some(obj) = value.as_object() else {
return Err(anyhow!("mcpServers must be an object"));
};
let mut servers = Vec::new();
for (name, entry) in obj {
let entry_obj = entry
.as_object()
.ok_or_else(|| anyhow!("mcpServers.{name} must be an object"))?;
let command = entry_obj
.get("command")
.and_then(Value::as_str)
.map(ToString::to_string);
let url = entry_obj
.get("url")
.and_then(Value::as_str)
.map(ToString::to_string);
if command.is_none() && url.is_none() {
return Err(anyhow!("mcpServers.{name} is missing command/url"));
}
let args = entry_obj
.get("args")
.and_then(Value::as_array)
.map(|items| {
items
.iter()
.filter_map(Value::as_str)
.map(ToString::to_string)
.collect::<Vec<_>>()
})
.unwrap_or_default();
let env = entry_obj
.get("env")
.and_then(Value::as_object)
.map(|map| {
map.iter()
.filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))View on GitHub (pinned to b0637c97ec)