windmill-labs/windmill · error · ValueError

duplicate MCP tool name(s): {', '.join(duplicates)}

Error message

duplicate MCP tool name(s): {', '.join(duplicates)}

What it means

backend/generate_mcp_endpoints_tools/generate_mcp_tools.py's find_mcp_tools collects MCP tool names derived from spec endpoints via x-mcp-tool-name. Tool names must be unique because a token's mcp:endpoints:<name> resolves by first match — a duplicate would silently route to whichever tool is generated first, so the generator raises ValueError listing the duplicated names.

Source

Thrown at backend/generate_mcp_endpoints_tools/generate_mcp_tools.py:470

                    'path': path,
                    'method': method.upper(),
                    'parameters': operation.get('parameters', []),
                    'requestBody': operation.get('requestBody'),
                    'required_fields': operation.get('x-mcp-required-fields', []),
                    'include_fields': operation.get('x-mcp-tool-include-fields'),
                    'opaque_fields': operation.get('x-mcp-tool-opaque-fields'),
                    'include_query_params': operation.get('x-mcp-tool-include-query-params'),
                }
                tools.append(tool)

    # A tool name used to be an operationId, which OpenAPI already keeps unique.
    # `x-mcp-tool-name` gives that up, and a duplicate would be silent: a token's
    # `mcp:endpoints:<name>` resolves by first match, so which endpoint it reaches
    # would depend on the order of this file.
    names = [t['name'] for t in tools]
    duplicates = sorted({n for n in names if names.count(n) > 1})
    if duplicates:
        raise ValueError(f"duplicate MCP tool name(s): {', '.join(duplicates)}")

    return tools

def generate_typescript_code(tools: List[Dict[str, Any]], spec: Dict[str, Any], base_path: str = "") -> str:
    """Generate TypeScript code with MCP endpoint tools.

    This catalogue only feeds the frontend's MCP scope picker, which reads names and
    methods.
    """
    if not tools:
        return """// Auto-generated MCP tools from OpenAPI specification
// This file is generated by generate_mcp_tools.py - DO NOT EDIT MANUALLY

export interface EndpointTool {
    name: string;
    description: string;
    instructions: string;
    path: string;

View on GitHub (pinned to e474e8803c)

Solutions

  1. Identify the duplicated names from the error message and grep the spec for that x-mcp-tool-name
  2. Rename one endpoint's x-mcp-tool-name so every tool name is unique
  3. If the name is derived rather than explicit, adjust the path/operation so generated names differ
  4. Regenerate the MCP tools (run the generator's main) and confirm no ValueError

Example fix

# before (spec)
x-mcp-tool-name: get-job
# on two different endpoints

# after
x-mcp-tool-name: get-job          # first endpoint
x-mcp-tool-name: get-job-result   # second endpoint
Defensive patterns

Strategy: validation

Validate before calling

# validate tool-name uniqueness in the spec before running the generator
import yaml, collections
spec = yaml.safe_load(open('spec.yaml'))
names = [op.get('x-mcp-tool-name') for op in spec['paths'].values() if op.get('x-mcp-tool-name')]
dups = [n for n, c in collections.Counter(names).items() if c > 1]
assert not dups, f"duplicate x-mcp-tool-name: {dups}"

Type guard

def has_unique_tool_names(tools: list[dict]) -> bool:
    names = [t['name'] for t in tools]
    return len(names) == len(set(names))

Try / catch

try:
    tools = find_mcp_tools(spec)
except ValueError as e:
    if str(e).startswith('duplicate MCP tool name'):
        sys.exit(f"Fix the spec: {e}")
    raise

Prevention

When it happens

Trigger: Two or more endpoints in the OpenAPI spec declare the same x-mcp-tool-name (or derive the same name), so the tools list contains repeated 'name' entries and the sorted-duplicates check trips.

Common situations: Copy-pasting an endpoint block in the spec and forgetting to change x-mcp-tool-name; adding a v2 endpoint alongside v1 with the same tool name; renaming an operation but leaving a stale x-mcp-tool-name collision with another route.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/4fb012c2255c71e6. Report an issue: GitHub.