xai-org/grok-build · error

failed to serialize MCP server {name}: {e}

Error message

failed to serialize MCP server {name}: {e}

What it means

merge_mcp_servers could not convert a server's JSON config value into a TOML value (toml::Value::try_from failed). TOML cannot represent every JSON shape, e.g. top-level arrays or nulls, so the server entry is rejected rather than written incorrectly.

Source

Thrown at crates/codegen/xai-grok-shell/src/claude_import.rs:987

/// Merge MCP server configs into `[mcp_servers]`. Existing servers are NOT overwritten.
fn merge_mcp_servers(
    table: &mut TomlMap<String, TomlValue>,
    servers: &[(&str, &McpServerConfig)],
) -> anyhow::Result<usize> {
    let mcp = table
        .entry("mcp_servers")
        .or_insert_with(|| TomlValue::Table(TomlMap::new()));
    let mcp_table = mcp
        .as_table_mut()
        .ok_or_else(|| anyhow::anyhow!("[mcp_servers] is not a table"))?;

    let mut count = 0;
    for (name, config) in servers {
        // Don't overwrite existing server entries.
        if !mcp_table.contains_key(*name) {
            let serialized = toml::Value::try_from(*config)
                .map_err(|e| anyhow::anyhow!("failed to serialize MCP server {name}: {e}"))?;
            mcp_table.insert(name.to_string(), serialized);
            count += 1;
        }
    }
    Ok(count)
}

/// Merge a list of path strings into `[paths] <key>` (an array of strings).
///
/// Existing entries are preserved; new entries that aren't already present
/// are appended. Returns the number of newly added entries.
fn merge_paths(
    table: &mut TomlMap<String, TomlValue>,
    key: &str,
    new_paths: &[&str],
) -> anyhow::Result<usize> {
    let paths = table
        .entry("paths")

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Remove/null-out unsupported values (nulls) in the server's JSON config before importing.
  2. Provide a TOML-compatible object (strings, numbers, booleans, arrays, tables only) for the server config.
  3. Skip the failing server and add its entry manually under [mcp_servers.<name>] in the config.

Example fix

// before (claude_desktop_config.json)
{ "mcpServers": { "fs": { "env": null } } }

// after
{ "mcpServers": { "fs": { "env": {} } } }
Defensive patterns

Strategy: try-catch

Validate before calling

fn toml_compatible(v: &serde_json::Value) -> bool {
    match v {
        serde_json::Value::Null => false,
        serde_json::Value::Array(a) => a.iter().all(toml_compatible),
        serde_json::Value::Object(o) => o.values().all(toml_compatible),
        _ => true,
    }
}

Type guard

fn is_toml_representable(v: &serde_json::Value) -> bool {
    toml::Value::try_from(v).is_ok()
}

Try / catch

match apply_import(items, &path) {
    Err(e) if e.to_string().contains("failed to serialize MCP server") => {
        let name = /* extract from message */;
        eprintln!("Server '{name}' has TOML-unsupported values (likely nulls); fix its JSON config");
    }
    r => r?,
}

Prevention

When it happens

Trigger: apply_items_to_config imports an MCP server whose config object contains a null value or another TOML-unsupported shape, causing toml::Value::try_from to fail.

Common situations: Claude JSON config has "env": null or a null array element; server config with keys TOML cannot map directly.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/9d40f7ec854806e7. Report an issue: GitHub.