Skip to content

# Bug: Massive schema information loss in listTools operation due to unnecessary double conversion #129

@dawasserman

Description

@dawasserman

Problem Description

The listTools operation is losing critical schema information when converting from the original MCP JSON Schema to Zod objects and back to JSON Schema. This causes executeTool operations to fail frequently due to incomplete parameter validation and missing constraints.

Expected Behavior

The listTools operation should return the complete, original JSON Schema from the MCP server, preserving all constraints, defaults, enums, nested object structures, and validation rules.

Actual Behavior

The returned schema is significantly degraded, missing:

  • default values
  • minimum/maximum constraints
  • enum definitions
  • Nested object structures (converted to generic z.record(z.string(), z.any()))
  • Complex array item definitions (converted to z.array(z.any()))
  • Required fields in nested objects

Root Cause

In McpClient.node.ts lines ~431-485, the code performs an unnecessary double conversion:

Original MCP JSON Schema → Zod Objects → JSON Schema (via zodToJsonSchema)

This double conversion is lossy and degrades the schema quality.

Example

Original MCP Schema (what should be returned):

{
  "name": "hubspot-list-objects",
  "schema": {
    "type": "object",
    "properties": {
      "limit": {
        "type": "integer",
        "default": 100,
        "minimum": 1,
        "maximum": 500,
        "description": "The maximum number of results to display per page (max: 500)."
      },
      "filterGroups": {
        "type": "array",
        "description": "Groups of filters to apply (combined with OR).",
        "items": {
          "type": "object",
          "properties": {
            "filters": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "propertyName": {"type": "string"},
                  "operator": {
                    "type": "string", 
                    "enum": ["EQ", "NEQ", "LT", "LTE", "GT", "GTE", "BETWEEN"]
                  }
                },
                "required": ["propertyName", "operator"]
              }
            }
          }
        }
      }
    },
    "required": ["objectType"]
  }
}

Current Degraded Output:

{
  "name": "hubspot-list-objects", 
  "schema": {
    "type": "object",
    "properties": {
      "limit": {
        "type": "integer",
        "description": "The maximum number of results to display per page (max: 500)."
      },
      "filterGroups": {
        "type": "array",
        "description": "Groups of filters to apply (combined with OR)."
      }
    },
    "required": ["objectType"]
  }
}

Impact

  • executeTool operations fail due to incomplete schema validation
  • Users cannot rely on parameter defaults, constraints, or validation
  • Complex nested parameters become unusable
  • Poor developer experience when building workflows

Proposed Solution

Replace the double conversion with direct schema passthrough:

Current problematic code:

// Lines ~431-485 in McpClient.node.ts
const paramSchema = tool.inputSchema?.properties
  ? z.object(/* complex zod conversion */)
  : z.object({});

return new DynamicStructuredTool({
  name: tool.name,
  description: tool.description || `Execute the ${tool.name} tool`,
  schema: paramSchema, 
  func: async (params) => { /* ... */ }
});

// Later...
schema: zodToJsonSchema(t.schema as z.ZodTypeAny || z.object({}))

Proposed fix:

const aiTools = tools.map((tool: any) => ({
  name: tool.name,
  description: tool.description || `Execute the ${tool.name} tool`,
  schema: tool.inputSchema || {type: 'object', properties: {}, additionalProperties: false},
  func: async (params) => {
    try {
      const result = await client.callTool({
        name: tool.name,
        arguments: params,
      }, CallToolResultSchema, requestOptions);
      
      return typeof result === 'object' ? JSON.stringify(result) : String(result);
    } catch (error) {
      throw new NodeOperationError(
        this.getNode(),
        `Failed to execute ${tool.name}: ${(error as Error).message}`,
      );
    }
  }
}));

Steps to Reproduce

  1. Connect to any MCP server (e.g., HubSpot MCP server)
  2. Use listTools operation
  3. Compare the returned schema with the original MCP server schema
  4. Attempt to use executeTool with complex parameters
  5. Observe failures due to missing schema information

Environment

  • n8n-nodes-mcp version: 0.1.28
  • n8n version: [various]
  • MCP servers tested: HubSpot MCP server

Additional Context

This issue affects all MCP servers that provide complex schemas. The double conversion was likely intended to provide LangChain compatibility, but it's causing more problems than it solves. The original JSON Schema should be preserved for n8n's native parameter validation.

Suggested Labels

  • bug
  • high-priority
  • schema
  • breaking-change (fix may require version bump)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions