Hmbown/CodeWhale · error · anyhow::Error

Invalid MCP command

Error message

Invalid MCP command

What it means

When an entry targets a local server via `command`, the command string must be non-empty after trimming and must contain no control characters (which could smuggle newlines/escapes into process spawning). Anything failing those checks throws this error.

Solutions

  1. Set a concrete non-empty executable path or name (e.g. `npx`, `/usr/local/bin/server`)
  2. Remove embedded newlines/control characters from the command string; move argument logic into `args`
  3. Verify the entry: trimmed command length > 0 and no control chars

Example fix

// before
{"mcpServers":{"fs":{"command":"npx\n -y server-fs"}}}
// after
{"mcpServers":{"fs":{"command":"npx","args":["-y","server-fs"]}}}
Defensive patterns

Strategy: validation

Validate before calling

function commandOk(c) { return typeof c === "string" && c.trim().length > 0 && !/[\u0000-\u001f\u007f]/.test(c); }

Try / catch

catch, then print the command with control characters escaped to locate the offending byte

Prevention

When it happens

Trigger: `command` is empty or whitespace-only; `command` contains \n, \r, \t, or other control characters (e.g. a command pasted with an embedded newline).

Common situations: Copy-pasting a command from a rendered page that includes line breaks; a generated config with an unset command placeholder; templates with empty command defaults.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/mcp/external_import.rs:201

            fields.keys().all(|key| ALLOWED.contains(&key.as_str())),
            "Source contains unsupported MCP fields; review it at its source"
        );
        if let Some(oauth) = fields.get("oauth").filter(|v| !v.is_null()) {
            anyhow::ensure!(
                oauth
                    .as_object()
                    .is_some_and(|map| map.keys().all(|key| key == "client_id")),
                "Source contains unsupported OAuth fields"
            );
        }
        let server: McpServerConfig = serde_json::from_value(config)
            .map_err(|_| anyhow::anyhow!("Invalid MCP entry; contents omitted"))?;
        anyhow::ensure!(
            server.command.is_some() != server.url.is_some(),
            "MCP entry must have one target"
        );
        if let Some(command) = &server.command {
            anyhow::ensure!(
                !command.trim().is_empty() && !command.chars().any(char::is_control),
                "Invalid MCP command"
            );
        }
        if let Some(url) = &server.url {
            let parsed =
                reqwest::Url::parse(url).map_err(|_| anyhow::anyhow!("Invalid MCP URL"))?;
            anyhow::ensure!(
                matches!(parsed.scheme(), "http" | "https")
                    && parsed.host_str().is_some()
                    && parsed.username().is_empty()
                    && parsed.password().is_none(),
                "Unsupported MCP URL"
            );
        }
        super::validate_mcp_transport(server.transport.as_deref())
            .map_err(|_| anyhow::anyhow!("Unsupported MCP transport"))?;
        let hard_blocked = !server.is_enabled();

View on GitHub (pinned to 73e0f67d83)