File size: 2,582 Bytes
7aca0bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# SPDX-License-Identifier: BSD-3-Clause

"""Tool name conflict resolution for harness tool injection (RFC 005)."""

from __future__ import annotations

from typing import Collection

from ..env_server.mcp_types import RESERVED_TOOL_NAMES, Tool


def resolve_tool_conflicts(
    env_tools: list[Tool],
    harness_builtin_tools: Collection[str],
    prefix: str = "env_",
) -> list[Tool]:
    """
    Detect and resolve name conflicts between environment and harness tools.

    Environment tools whose names collide with the harness's built-in tools
    are renamed with `prefix`. Ambiguous situations fail loudly instead of
    silently overriding.

    Args:
        env_tools (`list[Tool]`):
            Environment MCP tool definitions to inject into the harness.
        harness_builtin_tools (`Collection[str]`):
            Names of the harness's built-in tools.
        prefix (`str`, *optional*, defaults to `"env_"`):
            Prefix applied to conflicting environment tool names.

    Returns:
        `list[Tool]` of conflict-free tool definitions. The input list is not
        mutated.

    Raises:
        `ValueError`: If an environment tool uses a reserved orchestration
            name, if environment tool names are duplicated, or if a prefixed
            name still collides with a built-in or another environment tool.
    """
    reserved = sorted({tool.name for tool in env_tools} & RESERVED_TOOL_NAMES)
    if reserved:
        raise ValueError(
            "Tool names are reserved for orchestration controls: " + ", ".join(reserved)
        )

    seen: dict[str, Tool] = {}
    for tool in env_tools:
        if tool.name in seen:
            raise ValueError(f"Duplicate environment tool name: {tool.name}")
        seen[tool.name] = tool

    builtin_names = set(harness_builtin_tools)
    env_names = set(seen)
    resolved: list[Tool] = []
    taken: set[str] = set()
    for tool in env_tools:
        name = tool.name
        if name in builtin_names:
            renamed = f"{prefix}{name}"
            if renamed in builtin_names or renamed in env_names:
                raise ValueError(
                    f"Cannot resolve tool name conflict: '{name}' collides with a "
                    f"harness built-in tool and '{renamed}' is also taken"
                )
            name = renamed
        if name in taken:
            raise ValueError(f"Tool name conflict after prefixing: {name}")
        taken.add(name)
        resolved.append(tool.model_copy(update={"name": name}))
    return resolved


__all__ = ["resolve_tool_conflicts"]