PrefectHQ/fastmcp · error

ToolTransform has duplicate target name {target!r}: both {se

Error message

ToolTransform has duplicate target name {target!r}: both {seen_targets[target]!r} and {original_name!r} map to it

What it means

ToolTransform validates that every transformed tool maps to a unique public target name. Each entry's target name is its explicit `name` if set, otherwise the original tool name. If two entries resolve to the same target, the transform map would be ambiguous at lookup time, so the constructor raises ValueError immediately.

Source

Thrown at fastmcp_slim/fastmcp/server/transforms/tool_transform.py:52

        """Initialize ToolTransform.

        Args:
            transforms: Map of original tool name → transform config.
        """
        self._transforms = transforms

        # Build reverse mapping: final_name → original_name
        self._name_reverse: dict[str, str] = {}
        for original_name, config in transforms.items():
            final_name = config.name if config.name else original_name
            self._name_reverse[final_name] = original_name

        # Validate no duplicate target names
        seen_targets: dict[str, str] = {}
        for original_name, config in transforms.items():
            target = config.name if config.name else original_name
            if target in seen_targets:
                raise ValueError(
                    f"ToolTransform has duplicate target name {target!r}: "
                    f"both {seen_targets[target]!r} and {original_name!r} map to it"
                )
            seen_targets[target] = original_name

    def __repr__(self) -> str:
        names = list(self._transforms.keys())
        if len(names) <= 3:
            return f"ToolTransform({names!r})"
        return f"ToolTransform({names[:3]!r}... +{len(names) - 3} more)"

    async def list_tools(self, tools: Sequence[Tool]) -> Sequence[Tool]:
        """Apply transforms to matching tools."""
        result: list[Tool] = []
        for tool in tools:
            if tool.name in self._transforms:
                transformed = self._transforms[tool.name].apply(tool)
                result.append(transformed)

View on GitHub (pinned to 1f02114297)

Solutions

  1. Pick a distinct `name` for one of the colliding transform entries so targets are unique.
  2. Remove one of the duplicate entries if it is redundant (e.g. left over from an old config).
  3. Prefix or namespace generated target names (e.g. `api_<tool>`) in scripted configs to avoid accidental collisions.

Example fix

# before
transforms = ToolTransform({
    "list_items": ToolTransformConfig(name="items"),
    "get_items": ToolTransformConfig(name="items"),  # duplicate target 'items'
})
# after
transforms = ToolTransform({
    "list_items": ToolTransformConfig(name="list_items_view"),
    "get_items": ToolTransformConfig(name="get_items_view"),
})
Defensive patterns

Strategy: validation

Validate before calling

targets = [(k, c.name or k) for k, c in transforms.items()]
dups = [t for t in set(x[1] for x in targets) if [x[1] for x in targets].count(t) > 1]
if dups:
    raise ValueError(f"Duplicate transform target names: {dups}")

Try / catch

try:
    t = ToolTransform(transforms)
except ValueError as e:
    if "duplicate target name" in str(e):
        log.error(f"Transform config conflict: {e}")
        raise SystemExit(2)

Prevention

When it happens

Trigger: Calling `ToolTransform({...})` (or a subclass like the renaming transform) with a dict where two entries produce the same target: e.g. two entries renaming different originals to the same new name, or an explicit `name` on one entry colliding with another entry's original tool name.

Common situations: Bulk-renaming configs generated by hand or script where a rename accidentally matches an existing tool name; copying a transform config between environments where one tool was renamed upstream; adding a new renamed entry without noticing the target name already exists.

Related errors


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