Hmbown/CodeWhale · error

.inputSchema must be a valid object-shaped MCP input schema

Error message

{context}.inputSchema must be a valid object-shaped MCP input schema

What it means

parse_tool_entry requires an advertised tool's inputSchema to be a valid object-shaped MCP input schema. If the field is present but fails valid_tool_input_schema (not an object, or wrong shape), parsing bails. A missing inputSchema is only tolerated in the explicit legacy mode where the server also omitted the whole initialize capabilities object; a server advertising tools must send schemas.

Solutions

  1. Fix the server to emit a well-formed object schema: {"type":"object","properties":{...}} for every advertised tool.
  2. Update the server library/version so inputSchema serializes correctly.
  3. If the server is genuinely legacy (omits initialize capabilities), confirm the legacy mode path is actually enabled — a standard capabilities advertisement must include valid schemas.
  4. Validate the server's raw tools/list JSON out-of-band (curl/pipe) to see the exact malformed inputSchema value.

Example fix

// before (server output)
{"name":"search","inputSchema":"object"}
// after
{"name":"search","inputSchema":{"type":"object","properties":{}}}
Defensive patterns

Strategy: validation

Validate before calling

fn has_valid_input_schema(tool: &serde_json::Value) -> bool {
    tool.get("inputSchema").map(valid_tool_input_schema).unwrap_or(false)
}
// pre-screen each tools/list entry before passing to the manager

Type guard

fn well_formed_object_schema(v: &serde_json::Value) -> bool {
    v.is_object() && v.get("type").and_then(|t| t.as_str()) == Some("object")
}

Try / catch

match manager.list_tools_with_input_schemas(server) {
    Err(e) if e.to_string().contains("inputSchema must be a valid") => {
        tracing::error!("server {server} advertises tools with malformed inputSchema: {e}");
        // disable tools for this server or switch to legacy mode if truly legacy
    }
    other => other,
}

Prevention

When it happens

Trigger: list_tools_with_input_schemas parses a tool entry where "inputSchema" exists but is not a valid object schema — e.g. inputSchema is a string, array, null, or an object missing type/properties shape requirements.

Common situations: Server advertises the tools capability but sends partial/broken schemas; hand-written server emits JSON-Schema drafts the validator rejects; proxy/中间 layer truncates or re-serializes schemas incorrectly; legacy server partially upgraded (advertises tools but omits well-formed schemas).

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at crates/mcp/src/stdio_client.rs:148

fn parse_tool_entry(
    server_name: &str,
    tool: &Value,
    index: usize,
    allow_legacy_schema_omission: bool,
) -> Result<(McpToolDescriptor, Value)> {
    let context = format!("MCP server '{server_name}': tools/list tools[{index}]");
    let fields = tool
        .as_object()
        .with_context(|| format!("{context} must be an object"))?;
    let tool_name = fields
        .get("name")
        .and_then(Value::as_str)
        .with_context(|| format!("{context}.name must be a string"))?
        .to_string();
    let description = optional_string_field(fields, "description", &context)?;
    let input_schema = match fields.get("inputSchema") {
        Some(schema) if valid_tool_input_schema(schema) => schema.clone(),
        Some(_) => bail!("{context}.inputSchema must be a valid object-shaped MCP input schema"),
        None if allow_legacy_schema_omission => {
            // Servers that omit the entire initialize capabilities object are
            // already identified as legacy by `validate_initialize_result`.
            // Preserve omission compatibility only for that explicit mode;
            // a standard advertised tools capability must send inputSchema.
            json!({"type": "object", "properties": {}})
        }
        None => bail!("{context}.inputSchema is required for an advertised MCP tool"),
    };
    Ok((
        McpToolDescriptor {
            server_name: server_name.to_string(),
            // The manager owns qualification; report the raw name and let it
            // build `mcp__server__tool`.
            qualified_name: tool_name.clone(),
            tool_name,
            description,
        },

View on GitHub (pinned to 73e0f67d83)