warpdotdev/warp · error · anyhow::Error
MCP server '{name}' config must be a JSON object
Error message
MCP server '{name}' config must be a JSON object What it means
Thrown by mcp_specs_from_mcp_servers when an entry in the agent config file's `mcp_servers` map has a value that is not a JSON object. Each server's value must be a map (containing optionally `warp_id`, or `command`/`url` fields); strings, arrays, numbers, or booleans are rejected at config parse time.
Source
Thrown at app/src/ai/agent_sdk/config_file.rs:120
/// Convert an unwrapped `mcp_servers` map into runtime MCP specs for AgentDriver.
///
/// Behavior:
/// - Entries with a UUID `warp_id` become `MCPSpec::Uuid`.
/// - Entries with any other non-empty `warp_id` (e.g. `"linear"`) become `MCPSpec::WellKnown`
/// when `FeatureFlag::WellKnownMcpIds` is enabled (and are rejected otherwise);
/// the server owns the set of recognized ids and unknown ids are skipped at resolution.
/// - Entries with `command`/`url` remain as inline JSON (`MCPSpec::Json`) containing the unwrapped server map.
pub fn mcp_specs_from_mcp_servers(
mcp_servers: &Map<String, Value>,
) -> anyhow::Result<Vec<MCPSpec>> {
let mut uuids: Vec<uuid::Uuid> = Vec::new();
let mut well_known: Vec<String> = Vec::new();
let mut json_map: Map<String, Value> = Map::new();
for (name, config) in mcp_servers {
let obj = config
.as_object()
.ok_or_else(|| anyhow::anyhow!("MCP server '{name}' config must be a JSON object"))?;
if let Some(warp_id) = obj.get("warp_id").and_then(Value::as_str) {
if let Ok(uuid) = uuid::Uuid::parse_str(warp_id) {
uuids.push(uuid);
} else if !FeatureFlag::WellKnownMcpIds.is_enabled() {
return Err(anyhow::anyhow!(
"MCP server '{name}' field 'warp_id' must be a UUID"
));
} else if warp_id.trim().is_empty() {
return Err(anyhow::anyhow!(
"MCP server '{name}' field 'warp_id' must be non-empty"
));
} else {
well_known.push(warp_id.to_string());
}
} else {
json_map.insert(name.clone(), config.clone());
}View on GitHub (pinned to e72fd7aacb)
Solutions
- Make every mcp_servers value a JSON object, e.g. {"github": {"url": "https://mcp.github.com"}} instead of a bare string.
- Validate the config with a JSON schema or jq before launching: jq -e '.mcp_servers | to_entries | all(.value | type == "object")' config.json.
- Check for stray commas/brackets from hand edits that can collapse an object into a scalar.
Example fix
// before (config file)
{
"mcp_servers": {
"github": "https://mcp.github.com"
}
}
// after
{
"mcp_servers": {
"github": { "url": "https://mcp.github.com" }
}
} Defensive patterns
Strategy: type-guard
Validate before calling
for (name, config) in &mcp_servers {
if !config.is_object() {
anyhow::bail!("mcp_servers.{name} must be a JSON object, got {}", json_type(config));
}
} Type guard
fn is_valid_mcp_entry(v: &serde_json::Value) -> bool {
v.as_object().is_some()
} Try / catch
match mcp_specs_from_mcp_servers(&mcp_servers) {
Err(err) if err.to_string().contains("config must be a JSON object") => {
// point the user at the offending server name parsed from the message
}
rest => rest,
} Prevention
- Never use string shorthand for MCP servers in Warp configs — always a nested object.
- Validate configs with jq or a JSON schema in CI before the agent loads them.
- Beware tool-specific config dialects when copy-pasting between Claude Desktop/Cursor/Warp.
When it happens
Trigger: Loading an agent config file whose mcp_servers looks like {"github": "https://mcp.github.com"} (string shorthand) or {"github": ["npx", "server"]} (array) — any Value::as_object() == None for a server entry triggers this error before any MCP connection is attempted.
Common situations: Hand-editing the config and using the shorthand accepted by other tools (Claude Desktop, Cursor) that allow string URLs; copy-pasting a JSON5/YAML-converted config where nesting got flattened; or a malformed merge leaving a scalar in place of the object.
Related errors
- MCP server '{name}' field 'warp_id' must be non-empty
- MCP server '{name}' field 'warp_id' must be a UUID
- Expected JSON object with 'reviews' key
- No environments are configured for this account. You can cre
- File is too large ({}MB). Maximum size is 10MB.
AI-assisted analysis of warpdotdev/warp@e72fd7aacb (2026-08-16).
Data as JSON: /api/errors/34ed943174fcb37f.
Report an issue: GitHub.