diff --git a/mcp_client.py b/mcp_client.py index 9c9460b..7e4aac1 100644 --- a/mcp_client.py +++ b/mcp_client.py @@ -40,6 +40,17 @@ import urllib.parse from html.parser import HTMLParser import re as _re +# ------------------------------------------------------------------ mutating tools sentinel + +# Tools that write or modify files. ai_client checks this set before dispatch +# and routes to pre_tool_callback (GUI approval) if the tool name is present. +MUTATING_TOOLS: frozenset[str] = frozenset({ + "set_file_slice", + "py_update_definition", + "py_set_signature", + "py_set_var_declaration", +}) + # ------------------------------------------------------------------ state # Set by configure() before the AI send loop starts. diff --git a/tests/test_arch_boundary_phase2.py b/tests/test_arch_boundary_phase2.py index f38347d..4713604 100644 --- a/tests/test_arch_boundary_phase2.py +++ b/tests/test_arch_boundary_phase2.py @@ -67,3 +67,29 @@ def test_gui_agent_tool_names_exposes_all_dispatch_tools(): gui_tools = set(AGENT_TOOL_NAMES) missing = ALL_DISPATCH_TOOLS - gui_tools assert not missing, f"Tools missing from gui_2.AGENT_TOOL_NAMES: {missing}" + + +# --------------------------------------------------------------------------- +# Task 2.3: MUTATING_TOOLS constant in mcp_client.py +# --------------------------------------------------------------------------- + +def test_mcp_client_has_mutating_tools_constant(): + """mcp_client must expose a MUTATING_TOOLS frozenset.""" + import mcp_client + assert hasattr(mcp_client, "MUTATING_TOOLS"), "MUTATING_TOOLS missing from mcp_client" + assert isinstance(mcp_client.MUTATING_TOOLS, frozenset) + + +def test_mutating_tools_contains_write_tools(): + """MUTATING_TOOLS must include all four write tools.""" + import mcp_client + for tool in MUTATING_TOOLS: + assert tool in mcp_client.MUTATING_TOOLS, f"{tool} missing from mcp_client.MUTATING_TOOLS" + + +def test_mutating_tools_excludes_read_tools(): + """MUTATING_TOOLS must not include read-only tools.""" + import mcp_client + read_only = {"read_file", "get_file_slice", "py_get_definition", "py_get_skeleton"} + for tool in read_only: + assert tool not in mcp_client.MUTATING_TOOLS, f"Read-only tool '{tool}' must not be in MUTATING_TOOLS"