diff --git a/conductor/tracks/python_structural_mcp_tools_20260513/plan.md b/conductor/tracks/python_structural_mcp_tools_20260513/plan.md index 97aba5d..3f49d20 100644 --- a/conductor/tracks/python_structural_mcp_tools_20260513/plan.md +++ b/conductor/tracks/python_structural_mcp_tools_20260513/plan.md @@ -7,10 +7,10 @@ - [x] Task: Implement `py_add_def` core logic. [d044ccb] - [x] Task: Implement `py_move_def` core logic. [d044ccb] - [x] Task: Implement `py_region_wrap` core logic. [d044ccb] -- [~] Task: Conductor - User Manual Verification 'Phase 1: Core Logic Implementation' (Protocol in workflow.md) +- [x] Task: Conductor - User Manual Verification 'Phase 1: Core Logic Implementation' (Protocol in workflow.md) [578d9a2] ## Phase 2: MCP Client Integration -- [ ] Task: Register JSON schemas for `py_remove_def`, `py_add_def`, `py_move_def`, and `py_region_wrap` in `src/mcp_client.py`. +- [~] Task: Register JSON schemas for `py_remove_def`, `py_add_def`, `py_move_def`, and `py_region_wrap` in `src/mcp_client.py`. - [ ] Task: Implement dispatch handlers in `src/mcp_client.py` to bridge the native tool calls to the `./scripts/py_struct_tools.py` logic. - [ ] Task: Update the `TOOL_NAMES` and `MUTATING_TOOLS` constants to include the new capabilities. - [ ] Task: Conductor - User Manual Verification 'Phase 2: MCP Client Integration' (Protocol in workflow.md) diff --git a/src/mcp_client.py b/src/mcp_client.py index 4bede8d..e0150cb 100644 --- a/src/mcp_client.py +++ b/src/mcp_client.py @@ -67,6 +67,7 @@ from src import beads_client from src import models from src import outline_tool from src import summarize +from scripts import py_struct_tools # ------------------------------------------------------------------ mutating tools sentinel @@ -80,6 +81,10 @@ MUTATING_TOOLS: frozenset[str] = frozenset({ "edit_file", "ts_c_update_definition", "ts_cpp_update_definition", + "py_remove_def", + "py_add_def", + "py_move_def", + "py_region_wrap", }) # ------------------------------------------------------------------ state @@ -1351,6 +1356,32 @@ def dispatch(tool_name: str, tool_input: dict[str, Any]) -> str: return ts_c_update_definition(path, str(tool_input.get("name", "")), str(tool_input.get("new_content", ""))) if tool_name == "ts_cpp_update_definition": return ts_cpp_update_definition(path, str(tool_input.get("name", "")), str(tool_input.get("new_content", ""))) + if tool_name == "py_remove_def": + return py_struct_tools.py_remove_def(path, str(tool_input.get("name", ""))) + if tool_name == "py_add_def": + return py_struct_tools.py_add_def( + path, + str(tool_input.get("name", "")), + str(tool_input.get("new_content", "")), + str(tool_input.get("anchor_type", "")), + tool_input.get("anchor_symbol") + ) + if tool_name == "py_move_def": + return py_struct_tools.py_move_def( + str(tool_input.get("src_path", "")), + str(tool_input.get("dest_path", "")), + str(tool_input.get("name", "")), + str(tool_input.get("dest_name", "")), + str(tool_input.get("anchor_type", "")), + tool_input.get("anchor_symbol") + ) + if tool_name == "py_region_wrap": + return py_struct_tools.py_region_wrap( + path, + int(tool_input.get("start_line", 1)), + int(tool_input.get("end_line", 1)), + str(tool_input.get("region_name", "")) + ) if tool_name == "py_get_definition": return py_get_definition(path, str(tool_input.get("name", ""))) if tool_name == "py_update_definition": @@ -1468,6 +1499,63 @@ def get_tool_schemas() -> list[dict[str, Any]]: # These are imported by ai_client.py to build provider-specific declarations. MCP_TOOL_SPECS: list[dict[str, Any]] = [ + { + "name": "py_remove_def", + "description": "Excises a specific class or function definition from a Python file using AST-derived line ranges, preserving surrounding formatting and comments.", + "parameters": { + "type": "object", + "properties": { + "path": { "type": "string", "description": "Path to the .py file." }, + "name": { "type": "string", "description": "The name of the class or function to remove. Use 'ClassName.method_name' for methods." } + }, + "required": ["path", "name"] + } + }, + { + "name": "py_add_def", + "description": "Inserts a new definition into a specific context (module level or within a specific class).", + "parameters": { + "type": "object", + "properties": { + "path": { "type": "string", "description": "Path to the .py file." }, + "name": { "type": "string", "description": "Context path (e.g. 'ClassName' or empty for module level)." }, + "new_content": { "type": "string", "description": "The code to insert." }, + "anchor_type": { "type": "string", "enum": ["before", "after", "top", "bottom"], "description": "Where to insert relative to the anchor." }, + "anchor_symbol": { "type": "string", "description": "Symbol name to anchor to if anchor_type is 'before' or 'after'." } + }, + "required": ["path", "name", "new_content", "anchor_type"] + } + }, + { + "name": "py_move_def", + "description": "Relocates a definition within a file or across different Python files.", + "parameters": { + "type": "object", + "properties": { + "src_path": { "type": "string", "description": "Path to the source .py file." }, + "dest_path": { "type": "string", "description": "Path to the destination .py file." }, + "name": { "type": "string", "description": "The name of the class or function to move." }, + "dest_name": { "type": "string", "description": "Context path in destination file (e.g. 'ClassName' or empty)." }, + "anchor_type": { "type": "string", "enum": ["before", "after", "top", "bottom"], "description": "Where to insert in destination." }, + "anchor_symbol": { "type": "string", "description": "Anchor symbol in destination." } + }, + "required": ["src_path", "dest_path", "name", "dest_name", "anchor_type"] + } + }, + { + "name": "py_region_wrap", + "description": "Wraps a specified block of code (e.g., a set of methods) in #region: Name and #endregion: Name tags.", + "parameters": { + "type": "object", + "properties": { + "path": { "type": "string", "description": "Path to the .py file." }, + "start_line": { "type": "integer", "description": "1-based start line number." }, + "end_line": { "type": "integer", "description": "1-based end line number (inclusive)." }, + "region_name": { "type": "string", "description": "The name of the region." } + }, + "required": ["path", "start_line", "end_line", "region_name"] + } + }, { "name": "read_file", "description": (