PrefectHQ/fastmcp · error · ValueError

Discovery tools must have unique names.

Error message

Discovery tools must have unique names.

What it means

CodeMode's discovery tools must each have a unique name; when the set of built discovery tools is smaller than the list, duplicates exist and a ValueError is raised before the transform is usable.

Source

Thrown at fastmcp_slim/fastmcp/experimental/transforms/code_mode.py:545

            if discovery_tools is not None
            else _default_discovery_tools()
        )
        self._built_discovery_tools: list[Tool] | None = None
        self._cached_execute_tool: Tool | None = None

    def _build_discovery_tools(self) -> list[Tool]:
        if self._built_discovery_tools is None:
            tools = [
                factory(self.get_tool_catalog) for factory in self._discovery_factories
            ]
            names = {t.name for t in tools}
            if self.execute_tool_name in names:
                raise ValueError(
                    f"Discovery tool name '{self.execute_tool_name}' "
                    f"collides with execute_tool_name."
                )
            if len(names) != len(tools):
                raise ValueError("Discovery tools must have unique names.")
            self._built_discovery_tools = tools
        return self._built_discovery_tools

    async def transform_tools(self, tools: Sequence[Tool]) -> Sequence[Tool]:
        return [*self._build_discovery_tools(), self._get_execute_tool()]

    async def get_tool(
        self,
        name: str,
        call_next: GetToolNext,
        *,
        version: VersionSpec | None = None,
    ) -> Tool | None:
        for tool in self._build_discovery_tools():
            if tool.name == name:
                return tool
        if name == self.execute_tool_name:
            return self._get_execute_tool()

View on GitHub (pinned to 1f02114297)

Solutions

  1. Ensure each discovery factory emits a distinct tool name; add a distinguishing prefix/suffix per factory
  2. Remove duplicate factories from the discovery list
  3. Add an assertion or unit test on generated names before constructing the transform

Example fix

// before
factories = [make_catalog_tool, make_catalog_tool]  # same name twice

// after
factories = [make_catalog_tool, make_usage_tool]  # unique names
Defensive patterns

Strategy: validation

Validate before calling

names = [t.name for f in discovery_factories for t in [f(get_tool_catalog)]]
if len(set(names)) != len(names):
    raise ValueError(f"Duplicate discovery tool names: {sorted(n for n in names if names.count(n) > 1)}")

Type guard

def discovery_names_unique(names: list[str]) -> bool:
    return len(set(names)) == len(names)

Try / catch

try:
    tools = await transform.transform_tools(existing_tools)
except ValueError as e:
    if "unique names" in str(e):
        # dedupe/rename your discovery factories' outputs
        ...
    raise

Prevention

When it happens

Trigger: Providing multiple discovery tool factories whose generated tools produce the same name — _build_discovery_tools() (via transform_tools() or get_tool()) detects len(names) != len(tools) and raises.

Common situations: Reusing the same factory (or two factories that emit identical names) in the discovery_factories list; programmatic name generation that collides for different catalogs.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/1e772016602096d3. Report an issue: GitHub.