feat(mcp_client): add MUTATING_TOOLS frozenset sentinel for HITL enforcement

This commit is contained in:
2026-03-02 16:47:51 -05:00
parent 2d8f9f4d7a
commit 1f92629a55
2 changed files with 37 additions and 0 deletions

View File

@@ -40,6 +40,17 @@ import urllib.parse
from html.parser import HTMLParser from html.parser import HTMLParser
import re as _re 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 # ------------------------------------------------------------------ state
# Set by configure() before the AI send loop starts. # Set by configure() before the AI send loop starts.

View File

@@ -67,3 +67,29 @@ def test_gui_agent_tool_names_exposes_all_dispatch_tools():
gui_tools = set(AGENT_TOOL_NAMES) gui_tools = set(AGENT_TOOL_NAMES)
missing = ALL_DISPATCH_TOOLS - gui_tools missing = ALL_DISPATCH_TOOLS - gui_tools
assert not missing, f"Tools missing from gui_2.AGENT_TOOL_NAMES: {missing}" 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"