Hmbown/CodeWhale · error

MCP server ' ' config must have either 'command' or 'url

Error message

MCP server '{name}' config must have either 'command' or 'url'

What it means

Every MCP server config must specify exactly one launch mechanism: a local 'command' (stdio transport) or a remote 'url' (HTTP/SSE transport). When neither field is present the server cannot be constructed, and the setup bails naming the server.

Solutions

  1. Add a 'command' array for a local stdio server
  2. Add a 'url' string for a remote HTTP/SSE server
  3. Validate the mcpServers config structure (each entry needs exactly one of command/url)
  4. Check nesting/indentation so the field actually lands on the server entry

Example fix

// before
[config.mcpServers.myserver]
env = { API_KEY = "..." }
// after
[config.mcpServers.myserver]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-xyz"]
env = { API_KEY = "..." }
Defensive patterns

Strategy: validation

Validate before calling

for (name, cfg) in &config.mcpServers {
    if cfg.command.is_none() && cfg.url.is_none() {
        return Err(format!("MCP server '{name}' needs either 'command' or 'url'"));
    }
}

Type guard

fn is_launchable(cfg: &McpServerConfig) -> bool {
    cfg.command.is_some() || cfg.url.is_some()
}

Prevention

When it happens

Trigger: Defining an MCP server entry that lacks both 'command' and 'url'; e.g. an entry with only 'name'/'headers'/'env' or an empty object in the mcpServers map.

Common situations: Copy-pasting a config fragment and dropping the command/url line; YAML/TOML nesting mistakes that put command under the wrong key; a partially edited config saved mid-way.

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


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/1eeefce162ddcf19. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/mcp.rs:1816

                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 73e0f67d83)