Hmbown/CodeWhale · error

MCP server ' ' has no command configured

Error message

MCP server '{}' has no command configured

What it means

`spawn_with_timeouts` refuses to launch an MCP server subprocess whose configured `command` is empty or whitespace-only. There is no executable to spawn, so the client fails immediately with a clear config error instead of an obscure spawn failure deeper in `Command::new`. This is a configuration precondition check.

Solutions

  1. Set a valid, absolute path to the MCP server executable in the `command` field of the server's config.
  2. If the command comes from an environment variable, verify that variable is set in the environment the client runs in.
  3. If the server should run via a runtime (node/python), point command at the runtime binary and put the script in `args`.
  4. Validate config entries at load time so empty commands are rejected before they reach spawn.

Example fix

// before: config with empty command
{"name":"filesystem","command":"","args":["-y","@modelcontextprotocol/server-filesystem","/tmp"]}
// after: real executable
{"name":"filesystem","command":"npx","args":["-y","@modelcontextprotocol/server-filesystem","/tmp"]}
Defensive patterns

Strategy: validation

Validate before calling

fn validate_mcp_config(cfg: &McpServerConfig) -> Result<(), String> {
    if cfg.command.trim().is_empty() {
        return Err(format!("server '{}' has empty command", cfg.name));
    }
    Ok(())
}
// call for every entry when configs load
for c in &configs { validate_mcp_config(c)?; }

Try / catch

match spawn_with_timeouts(&config, hs, req).await {
    Err(e) if e.to_string().contains("no command configured") => {
        eprintln!("fill in the `command` field for server '{}' in your config", config.name);
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling `spawn_with_timeouts` (typically via client construction) with an `McpServerConfig` whose `command` field is `""`, contains only spaces, or was never set (defaulting to empty).

Common situations: A config file entry for the server missing the `command` key; an environment-variable interpolation that resolved to empty (e.g. `${MY_MCP_BIN}` unset); copy-pasting a server entry and forgetting to fill in the command; a tool generating configs that writes blank commands when discovery fails.

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@433685b202 (2026-09-15). Data as JSON: /api/errors/274e41a73daad165. Report an issue: GitHub.

Appendix: source

Thrown at crates/mcp/src/stdio_client.rs:501

impl ChildProcessMcpClient {
    /// Spawn `config.command` with `config.args`/`config.env` and complete the
    /// MCP handshake.
    ///
    /// Returns `Err` — never a degraded-but-usable client — when the command
    /// cannot be executed, exits immediately, or does not answer `initialize`
    /// within `HANDSHAKE_TIMEOUT`.
    pub fn spawn(config: &McpServerConfig) -> Result<Self> {
        Self::spawn_with_timeouts(config, HANDSHAKE_TIMEOUT, REQUEST_TIMEOUT)
    }

    fn spawn_with_timeouts(
        config: &McpServerConfig,
        handshake_timeout: Duration,
        request_timeout: Duration,
    ) -> Result<Self> {
        let server_name = config.name.clone();
        if config.command.trim().is_empty() {
            bail!("MCP server '{server_name}' has no command configured");
        }

        let mut command = Command::new(&config.command);
        command
            .args(&config.args)
            .envs(&config.env)
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            // The child's diagnostics belong on our stderr: stdout is the
            // JSON-RPC channel and must not be polluted, and swallowing the
            // child's stderr is how a misconfigured server becomes a silent
            // one.
            .stderr(Stdio::inherit());

        // Own descendants too: shell/package launchers can exit before the
        // actual server, leaving it alive with inherited protocol pipes.
        #[cfg(unix)]
        command.process_group(0);

View on GitHub (pinned to 433685b202)