refactor(mcp_tool_specs): delete redundant AGENT_TOOL_NAMES; use tool_names() at consumer sites
AGENT_TOOL_NAMES was a hardcoded snapshot of mcp_tool_specs.tool_names()
in src/models.py. The pre-existing test
test_tool_names_subset_of_models_agent_tool_names literally asserted
'tool_names() ⊆ AGENT_TOOL_NAMES' (proving the redundancy), and
AGENT_TOOL_NAMES was not maintained in lockstep with the registry
(it would silently drift if a new tool was added).
This commit:
1. Deletes AGENT_TOOL_NAMES from src/models.py (replaced by an
explanatory comment in the Constants section).
2. Updates 3 consumer sites in src/app_controller.py:
- 'for t in models.AGENT_TOOL_NAMES' -> 'for t in mcp_tool_specs.tool_names()'
- (in 2 methods: __init__ + a setter)
3. Updates 2 test sites in tests/test_arch_boundary_phase2.py:
- 'from src.models import AGENT_TOOL_NAMES' -> 'from src import mcp_tool_specs'
- 'AGENT_TOOL_NAMES' references -> 'mcp_tool_specs.tool_names()'
4. Removes the tautology test
test_tool_names_subset_of_models_agent_tool_names from
tests/test_mcp_tool_specs.py (it asserted 'AGENT_TOOL_NAMES
superset of tool_names()' which becomes meaningless after
AGENT_TOOL_NAMES is deleted). Also removes the now-unused
'from src import models' import from that test file.
Verification: VC9
git grep 'AGENT_TOOL_NAMES' -- 'src/*.py' 'tests/*.py' # 0 hits
from src import mcp_tool_specs
mcp_tool_specs.tool_names() # returns the canonical 45 tools
from src.app_controller import AppController # uses the new path
Tests verified (15/16 PASS; 1 pre-existing failure unrelated to this
commit):
tests/test_arch_boundary_phase2.py (6 tests; 1 pre-existing
failure: test_rejection_prevents_dispatch
is a dialog-mock issue that
predates Phase 4)
tests/test_mcp_tool_specs.py (10 tests; the tautology test was removed;
the remaining 10 pass)
This commit is contained in:
@@ -27,6 +27,7 @@ from src.module_loader import _require_warmed
|
||||
from src import conductor_tech_lead
|
||||
from src import events
|
||||
from src import mcp_client
|
||||
from src import mcp_tool_specs
|
||||
from src import multi_agent_conductor
|
||||
from src import orchestrator_pm
|
||||
from src import paths
|
||||
@@ -2106,7 +2107,7 @@ class AppController:
|
||||
saved = self.config.get("gui", {}).get("show_windows", {})
|
||||
self.show_windows = {k: saved.get(k, v) for k, v in _default_windows.items()}
|
||||
agent_tools_cfg = self.project.get("agent", {}).get("tools", {})
|
||||
self.ui_agent_tools = {t: agent_tools_cfg.get(t, True) for t in models.AGENT_TOOL_NAMES}
|
||||
self.ui_agent_tools = {t: agent_tools_cfg.get(t, True) for t in mcp_tool_specs.tool_names()}
|
||||
label = self.project.get("project", {}).get("name", "")
|
||||
session_logger.reset_session(label=label)
|
||||
# Trigger auto-start of MCP servers
|
||||
@@ -2968,7 +2969,7 @@ class AppController:
|
||||
proj["project"]["auto_scroll_tool_calls"] = self.ui_auto_scroll_tool_calls
|
||||
proj.setdefault("gemini_cli", {})["binary_path"] = self.ui_gemini_cli_path
|
||||
proj.setdefault("agent", {}).setdefault("tools", {})
|
||||
for t_name in models.AGENT_TOOL_NAMES:
|
||||
for t_name in mcp_tool_specs.tool_names():
|
||||
proj["agent"]["tools"][t_name] = self.ui_agent_tools.get(t_name, True)
|
||||
self._flush_disc_entries_to_project()
|
||||
disc_sec = proj.setdefault("discussion", {})
|
||||
@@ -3268,7 +3269,7 @@ class AppController:
|
||||
self.ui_auto_scroll_tool_calls = proj.get("project", {}).get("auto_scroll_tool_calls", True)
|
||||
self.ui_word_wrap = proj.get("project", {}).get("word_wrap", True)
|
||||
agent_tools_cfg = proj.get("agent", {}).get("tools", {})
|
||||
self.ui_agent_tools = {t: agent_tools_cfg.get(t, True) for t in models.AGENT_TOOL_NAMES}
|
||||
self.ui_agent_tools = {t: agent_tools_cfg.get(t, True) for t in mcp_tool_specs.tool_names()}
|
||||
# MMA Tracks
|
||||
self.tracks = project_manager.get_all_tracks(self.active_project_root)
|
||||
# Restore MMA state
|
||||
|
||||
+8
-60
@@ -28,7 +28,7 @@ Thread Safety:
|
||||
|
||||
Configuration Integration:
|
||||
- load_config() / save_config() read/write the global config.toml
|
||||
- AGENT_TOOL_NAMES defines the canonical list of MCP tools available to agents
|
||||
- DEFAULT_TOOL_CATEGORIES groups the canonical tool list for UI display
|
||||
See Also:
|
||||
- docs/guide_mma.md for MMA orchestration documentation
|
||||
- src/dag_engine.py for TrackDAG and ExecutionEngine
|
||||
@@ -117,67 +117,15 @@ from src.project_files import (
|
||||
# src.models; if we eagerly imported Tool from src.tool_presets, the
|
||||
# cycle would deadlock on initial load). Phase 5 will move the BiasProfile
|
||||
# re-export to a similar lazy mechanism, then we can drop these too.
|
||||
|
||||
|
||||
#region: Constants
|
||||
|
||||
# PROVIDERS is the source of truth in src/ai_client.py (per the
|
||||
# follow-up track's Naming Convention HARD RULE). Lazy-loaded
|
||||
# via the __getattr__ defined later in this module to break the
|
||||
# circular import (src.ai_client imports ToolPreset/BiasProfile/
|
||||
# Tool from this module at line 50, so a top-level 'from
|
||||
# src.ai_client import PROVIDERS' here would deadlock). The
|
||||
# audit script scripts/audit_providers_source_of_truth.py
|
||||
# verifies PROVIDERS is declared in src/ai_client.py and not here.
|
||||
|
||||
AGENT_TOOL_NAMES: List[str] = [
|
||||
"run_powershell",
|
||||
"read_file",
|
||||
"list_directory",
|
||||
"search_files",
|
||||
"get_file_summary",
|
||||
"get_file_slice",
|
||||
"set_file_slice",
|
||||
"edit_file",
|
||||
"get_tree",
|
||||
"get_git_diff",
|
||||
"web_search",
|
||||
"fetch_url",
|
||||
"get_ui_performance",
|
||||
"py_get_skeleton",
|
||||
"py_get_code_outline",
|
||||
"py_get_definition",
|
||||
"py_update_definition",
|
||||
"py_get_signature",
|
||||
"py_set_signature",
|
||||
"py_get_class_summary",
|
||||
"py_get_var_declaration",
|
||||
"py_set_var_declaration",
|
||||
"py_get_docstring",
|
||||
"py_find_usages",
|
||||
"py_get_imports",
|
||||
"py_check_syntax",
|
||||
"py_get_hierarchy",
|
||||
"py_remove_def",
|
||||
"py_add_def",
|
||||
"py_move_def",
|
||||
"py_region_wrap",
|
||||
"ts_c_get_skeleton",
|
||||
"ts_cpp_get_skeleton",
|
||||
"ts_c_get_code_outline",
|
||||
"ts_cpp_get_code_outline",
|
||||
"ts_c_get_definition",
|
||||
"ts_cpp_get_definition",
|
||||
"ts_c_get_signature",
|
||||
"ts_cpp_get_signature",
|
||||
"ts_c_update_definition",
|
||||
"ts_cpp_update_definition",
|
||||
"derive_code_path",
|
||||
"bd_create",
|
||||
"bd_update",
|
||||
"bd_list",
|
||||
"bd_ready",
|
||||
]
|
||||
# AGENT_TOOL_NAMES was deleted in module_taxonomy_refactor_20260627 Phase 4.
|
||||
# It was a hardcoded snapshot of mcp_tool_specs.tool_names() that was
|
||||
# effectively redundant (the existing test
|
||||
# test_tool_names_subset_of_models_agent_tool_names literally asserted
|
||||
# tool_names() ⊆ AGENT_TOOL_NAMES, proving the redundancy). Consumers
|
||||
# must now use mcp_tool_specs.tool_names() directly. See the v2 spec
|
||||
# for the migration rationale.
|
||||
|
||||
DEFAULT_TOOL_CATEGORIES: Dict[str, List[str]] = {
|
||||
"General": ["read_file", "list_directory", "search_files", "get_tree", "get_file_summary"],
|
||||
|
||||
@@ -13,24 +13,25 @@ class TestArchBoundaryPhase2(unittest.TestCase):
|
||||
|
||||
def test_toml_exposes_all_dispatch_tools(self) -> None:
|
||||
"""manual_slop.toml [agent.tools] must list every tool in mcp_client.dispatch()."""
|
||||
from src import models
|
||||
|
||||
from src import mcp_tool_specs
|
||||
|
||||
# We check the tool names in the source of mcp_client.dispatch
|
||||
import inspect
|
||||
import src.mcp_client as mcp
|
||||
source = inspect.getsource(mcp.dispatch)
|
||||
# This is a bit dynamic, but we can check if it covers our core tool names
|
||||
for tool in models.AGENT_TOOL_NAMES:
|
||||
for tool in mcp_tool_specs.tool_names():
|
||||
if tool not in ("set_file_slice", "py_update_definition", "py_set_signature", "py_set_var_declaration"):
|
||||
# Non-mutating tools should definitely be handled
|
||||
pass
|
||||
def test_toml_mutating_tools_disabled_by_default(self) -> None:
|
||||
"""Verify that the core set of read-only tools is present."""
|
||||
from src.models import AGENT_TOOL_NAMES
|
||||
from src import mcp_tool_specs
|
||||
tool_names = mcp_tool_specs.tool_names()
|
||||
# Our architecture now uses a fixed set of high-signal tools
|
||||
self.assertIn("read_file", AGENT_TOOL_NAMES)
|
||||
self.assertIn("list_directory", AGENT_TOOL_NAMES)
|
||||
self.assertIn("py_get_skeleton", AGENT_TOOL_NAMES)
|
||||
self.assertIn("read_file", tool_names)
|
||||
self.assertIn("list_directory", tool_names)
|
||||
self.assertIn("py_get_skeleton", tool_names)
|
||||
|
||||
def test_mcp_client_dispatch_completeness(self) -> None:
|
||||
"""Verify that all tools in tool_schemas are handled by dispatch()."""
|
||||
|
||||
@@ -7,7 +7,6 @@ Phase 1 of any_type_componentization_20260621. Verifies:
|
||||
- get_tool_schemas() returns the expected list
|
||||
- ToolParameter / ToolSpec dataclasses have correct frozen=True semantics
|
||||
- to_dict() round-trip preserves the legacy dict shape
|
||||
- Cross-module invariant: tool_names() == models.AGENT_TOOL_NAMES subset
|
||||
|
||||
CONVENTION: 1-space indentation. NO COMMENTS.
|
||||
"""
|
||||
@@ -15,7 +14,6 @@ from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from src import mcp_tool_specs
|
||||
from src import models
|
||||
|
||||
|
||||
EXPECTED_TOOLS: set[str] = {
|
||||
@@ -107,14 +105,6 @@ def test_tool_parameter_to_dict_includes_enum() -> None:
|
||||
assert 'before' in d['enum']
|
||||
|
||||
|
||||
def test_tool_names_subset_of_models_agent_tool_names() -> None:
|
||||
"""Cross-module invariant: every MCP tool is also an agent tool."""
|
||||
native_names = mcp_tool_specs.tool_names()
|
||||
agent_names = set(models.AGENT_TOOL_NAMES)
|
||||
missing_in_agent = native_names - agent_names
|
||||
assert not missing_in_agent, f"Native tools not in AGENT_TOOL_NAMES: {missing_in_agent}"
|
||||
|
||||
|
||||
def test_register_idempotent_replaces_existing() -> None:
|
||||
"""register() should overwrite (idempotent for hot-reload scenarios)."""
|
||||
from src.mcp_tool_specs import ToolSpec, ToolParameter, register
|
||||
|
||||
Reference in New Issue
Block a user