Hmbown/CodeWhale · error · anyhow::Error
MCP server '{name}' config must have either 'command' or 'ur
Error message
MCP server '{name}' config must have either 'command' or 'url' What it means
McpServerConfig must describe either a local subprocess (command, with optional args/env/cwd) or a remote endpoint (url). connect_with_policy tries url first, then command, and when both are absent there is nothing to build a transport from (crates/tui/src/mcp.rs:1639-1648). add_server_config pre-rejects the same shape at config-write time with its own message.
Source
Thrown at crates/tui/src/mcp.rs:1647
if let Err(e) = http.try_establish_session().await {
tracing::debug!(
target: "mcp",
server = %name,
error = %e,
"session-establishment GET skipped; proceeding with POST initialize"
);
}
Box::new(http)
}
} else if let Some(command) = &config.command {
Box::new(StdioTransport::spawn(
&name,
command,
&config,
cancel_token.clone(),
)?)
} else {
anyhow::bail!("MCP server '{name}' config must have either 'command' or 'url'");
};
// Revalidate after transport construction as well: remote setup may
// await DNS/TLS/SSE preflight, and a concurrent process can revoke the
// receipt during that interval. Initialization and catalog discovery
// never start under a stale generation.
if let Some(source) = config.reviewed_plugin.as_ref() {
source.validate_before_use(&name, "initialize")?;
}
let authority_watch = authority_watch.map(PendingAuthorityWatch::disarm);
let mut conn = Self {
name: name.clone(),
transport,
tools: Vec::new(),
resources: Vec::new(),
resource_templates: Vec::new(),
prompts: Vec::new(),
request_id: AtomicU64::new(1),View on GitHub (pinned to 8880682c63)
Solutions
- Add "command": "..." (plus args) for a stdio server, or "url": "https://..." for a remote server, to the entry.
- Check key spelling exactly - command and url; unrecognized alternatives silently deserialize to None.
- Validate the file shape against a freshly generated template (mcp init/config template) after editing.
Example fix
// before
"fetch": { "args": ["-y", "mcp-server-fetch"] }
// after
"fetch": { "command": "npx", "args": ["-y", "mcp-server-fetch"] } Defensive patterns
Strategy: validation
Validate before calling
fn server_config_has_transport(cfg: &McpServerConfig) -> bool {
cfg.command.is_some() || cfg.url.is_some()
}
// Run over the parsed config before handing it to the connection manager:
for (name, cfg) in &config.servers {
assert!(server_config_has_transport(cfg), "server '{name}' needs 'command' or 'url'");
} Prevention
- Use exactly the keys command and url; unknown keys are silently ignored.
- Validate config files after hand edits against the generated template.
- Prefer the mcp add path, which pre-rejects entries missing both fields.
When it happens
Trigger: A servers entry containing only args/env/headers with no command or url; serde defaults leaving both None after a typo'd key like "cmd" or "executable" (unknown keys are ignored).
Common situations: Hand-edited MCP config with key typos; config ported from another client's schema where the fields nest differently; partial edits that deleted the command line.
Related errors
- MCP config path cannot be empty
- MCP config path cannot contain '..' components
- invalid environment placeholder in MCP config value
- Unsupported MCP transport '{transport}'. Supported values: s
- Provide either a command or URL for MCP server '{name}'.
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/1eeefce162ddcf19.
Report an issue: GitHub.