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
- Ensure each discovery factory emits a distinct tool name; add a distinguishing prefix/suffix per factory
- Remove duplicate factories from the discovery list
- 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
- Give each discovery factory a unique naming scheme/prefix
- Deduplicate the factory list before constructing CodeModeTransform
- Test-transform a sample tool list in CI to surface collisions early
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
- Discovery tool name '{self.execute_tool_name}' collides with
- CodeMode requires pydantic-monty for the Monty sandbox provi
- Tool call limit exceeded: at most {max_tool_calls} call_tool
- Unknown tool: {tool_name}
- Tool name collision: {public_name!r}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/1e772016602096d3.
Report an issue: GitHub.