Hmbown/CodeWhale · error · anyhow::Error

Invalid MCP entry; contents omitted

Error message

Invalid MCP entry; contents omitted

What it means

Each imported server entry must be a JSON object. If a value in the server map (or an array element) is a string, number, array, or null, checked_source throws this error instead of attempting to import it. Contents are omitted from the message.

Solutions

  1. Expand shorthand entries into full objects with a command field: "fs": {"command": "npx", "args": [...]}
  2. Remove or replace null/placeholder entries in the source map before importing
  3. If the source uses string shorthands, convert them manually — the importer only accepts object entries

Example fix

// before (string shorthand entry)
{ "mcpServers": { "fs": "npx -y fs" } }
// after
{ "mcpServers": { "fs": { "command": "npx", "args": ["-y", "fs"] } } }
Defensive patterns

Strategy: validation

Validate before calling

fn entries_are_objects(path: &std::path::Path) -> bool {
    std::fs::read_to_string(path).ok()
        .and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok())
        .and_then(|v| v.get("mcpServers").or_else(|| v.get("servers")).cloned())
        .and_then(|m| m.as_object().cloned())
        .map(|m| m.values().all(|e| e.is_object()))
        .unwrap_or(false)
}

Type guard

fn as_server_object(v: &serde_json::Value) -> Option<&serde_json::Map<String, serde_json::Value>> {
    v.as_object()
}

Try / catch

match discover(&path) {
    Err(e) if e.to_string().contains("Invalid MCP entry") => {
        eprintln!("a server entry is not a JSON object; expand shorthand strings to objects");
    }
    r => r?,
}

Prevention

When it happens

Trigger: discover / discover_from_json_file where any entry under mcpServers/servers (or any element of a top-level array) is not a JSON object — e.g. "fs": "npx some-server" shorthand, or null placeholders.

Common situations: Importing a config that uses string shorthand for commands; entries left as null after manual cleanup; a list of names rather than a map of objects; copy-paste dropped an object's braces.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

            .or_else(|| value.get("servers"))
            .is_some_and(Value::is_object)
            || value.is_array(),
        "Source has no supported MCP server map"
    );
    let mut out = Vec::new();
    for (name, mut config) in extract_servers_map(&value) {
        anyhow::ensure!(
            super::mcp_name_is_command_safe(&name) && name.len() <= 128,
            "Source contains an unsupported server name"
        );
        if value.is_array()
            && let Some(map) = config.as_object_mut()
        {
            map.remove("name");
        }
        let fields = config
            .as_object()
            .ok_or_else(|| anyhow::anyhow!("Invalid MCP entry; contents omitted"))?;
        const ALLOWED: &[&str] = &[
            "command",
            "args",
            "env",
            "cwd",
            "url",
            "allow_private_network",
            "transport",
            "connect_timeout",
            "execute_timeout",
            "read_timeout",
            "disabled",
            "enabled",
            "required",
            "enabled_tools",
            "disabled_tools",
            "headers",
            "env_headers",

View on GitHub (pinned to 73e0f67d83)