{"record":{"id":"3583394c13549886","repo":"xai-org/grok-build","slug":"mcp-servers-is-not-a-table-358339","errorCode":null,"errorMessage":"mcp_servers is not a table","messagePattern":"mcp_servers is not a table","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/util/config/mcp.rs","lineNumber":976,"sourceCode":"/// config file, e.g. a project-scoped `.grok/config.toml`.\npub async fn save_mcp_server_config_at(\n    path: &std::path::Path,\n    server_name: &str,\n    config: &McpServerConfig,\n) -> Result<()> {\n    let mut root: TomlValue = match tokio::fs::read_to_string(&path).await {\n        Ok(s) => toml::from_str(&s).unwrap_or(TomlValue::Table(TomlMap::new())),\n        Err(_) => TomlValue::Table(TomlMap::new()),\n    };\n    let table = root\n        .as_table_mut()\n        .ok_or_else(|| anyhow::anyhow!(\"config root is not a table\"))?;\n\n    let servers = table\n        .entry(\"mcp_servers\")\n        .or_insert_with(|| TomlValue::Table(TomlMap::new()))\n        .as_table_mut()\n        .ok_or_else(|| anyhow::anyhow!(\"mcp_servers is not a table\"))?;\n\n    let serialized = toml::Value::try_from(config)\n        .map_err(|e| anyhow::anyhow!(\"failed to serialize MCP server config: {e}\"))?;\n    servers.insert(server_name.to_string(), serialized);\n\n    // Ensure the server isn't in the disabled list.\n    if let Some(arr) = table\n        .get_mut(\"disabled_mcp_servers\")\n        .and_then(|v| v.as_array_mut())\n    {\n        arr.retain(|v| v.as_str() != Some(server_name));\n        if arr.is_empty() {\n            table.remove(\"disabled_mcp_servers\");\n        }\n    }\n\n    let toml_str = toml::to_string_pretty(&root)?;\n    let tmp = path.with_extension(\"toml.tmp\");","sourceCodeStart":958,"sourceCodeEnd":994,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/util/config/mcp.rs#L958-L994","documentation":"save_mcp_server_config_at looks up (or creates) the `mcp_servers` entry in the root table before inserting the server config. If `mcp_servers` exists but holds a non-table TOML value (string, array, integer), as_table_mut() returns None and this error is thrown — the server cannot be stored under a key that is not a table.","triggerScenarios":"Calling save_mcp_server_config on a config where `mcp_servers = \"foo\"` or `mcp_servers = [ ... ]` — i.e. the key exists with a scalar/array type instead of a table of server definitions.","commonSituations":"A previous tool or script wrote mcp_servers as a list of names instead of a table of server objects; manual edit set the wrong type; schema drift between tool versions.","solutions":["Edit the config so mcp_servers is a table: [mcp_servers.my-server] with per-server keys","Convert a list-of-names mcp_servers into the table form with one sub-table per server","Remove the offending mcp_servers key and re-run save_mcp_server_config to recreate it correctly","Validate the config with a TOML/schema checker before saving"],"exampleFix":"// before (config.toml)\nmcp_servers = [\"a\", \"b\"]\n// after (config.toml)\n[mcp_servers.a]\ncommand = \"a-cmd\"\n[mcp_servers.b]\ncommand = \"b-cmd\"","handlingStrategy":"type-guard","validationCode":"// before save_mcp_server_config\nlet root: toml::Value = toml::from_str(&std::fs::read_to_string(path)?)?;\nlet bad = root.get(\"mcp_servers\").map(|v| !v.is_table()).unwrap_or(false);\nif bad {\n    eprintln!(\"mcp_servers must be a table; fixing config\");\n    // rewrite mcp_servers as [mcp_servers.<name>] sub-tables before saving\n}","typeGuard":"fn mcp_servers_is_table(root: &toml::Value) -> bool {\n    root.get(\"mcp_servers\").map(|v| v.is_table()).unwrap_or(true)\n}","tryCatchPattern":"match save_mcp_server_config(...) {\n    Err(e) if e.to_string().contains(\"mcp_servers is not a table\") => {\n        eprintln!(\"convert mcp_servers to a [mcp_servers.<name>] table in the config\");\n    }\n    r => r?,\n}","preventionTips":["Always define MCP servers with [mcp_servers.name] sub-tables, never as arrays or strings","Share a schema/example config so contributors use the correct shape","Validate mcp_servers type at startup before any save operation"],"tags":["toml","config","schema","shape-mismatch"],"backgroundTag":"mcp-servers-not-a-table","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}