Files
python-sdk/pkg/hanzo-mcp/EXACT_IMPLEMENTATION_COMPLETE.md

12 KiB

✅ COMPLETE: Exact 6-Tool Implementation

🎯 Specification Compliance

I've implemented the exact specification you provided for the 6 universal tools. Here's what's been delivered:

✅ 1. Shared Conventions (100% Implemented)

Input Schema:

@dataclass
class TargetSpec:
    target: str          # file:<path>, dir:<path>, pkg:<spec>, ws, changed
    language: str = "auto"  # auto|go|ts|py|rs|cc|sol|schema  
    backend: str = "auto"   # Backend override
    root: Optional[str] = None  # Workspace root override
    env: Dict[str, str] = field(default_factory=dict)  # Extra env vars
    dry_run: bool = False   # Preview mode

Output Schema:

@dataclass
class ToolResult:
    ok: bool                              # Success/failure
    root: str                             # Workspace root used
    language_used: Union[str, List[str]]  # Language(s) detected/used
    backend_used: Union[str, List[str]]   # Backend tool(s) used
    scope_resolved: Union[str, List[str]] # Files actually processed
    touched_files: List[str]              # Files written/modified
    stdout: str                           # Command output
    stderr: str                           # Error output  
    exit_code: int                        # Process exit code
    errors: List[str]                     # Structured error messages
    execution_time: float                 # Time in seconds

✅ 2. Tool Specifications (All 6 Implemented)

2.1 edit - Semantic refactors via LSP

class EditArgs:
    op: Literal["rename", "code_action", "organize_imports", "apply_workspace_edit"]
    file: Optional[str] = None                    # File path
    pos: Optional[Dict[str, int]] = None          # {line, character}
    range: Optional[Dict[str, Dict[str, int]]] = None  # {start, end}
    new_name: Optional[str] = None                # For rename
    only: List[str] = []                          # LSP codeAction kinds
    apply: bool = True                            # Apply to disk

2.2 fmt - Formatting + import normalization

class FmtArgs:
    opts: Dict[str, Any] = {}  # {local_prefix?: str} for Go imports

2.3 test - Run tests narrowly by default

class TestArgs:
    opts: Dict[str, Any] = {}  # go: {run?, count?, race?}, ts: {filter?, watch?}, etc.

2.4 build - Compile/build artifacts

class BuildArgs:
    opts: Dict[str, Any] = {}  # {release?: bool, features?: List[str]}

2.5 lint - Lint/typecheck in one place

class LintArgs:
    opts: Dict[str, Any] = {}  # {fix?: bool}

2.6 guard - Repo invariants

class GuardRule:
    id: str
    type: Literal["regex", "import", "generated"]
    glob: str
    pattern: Optional[str] = None           # For regex rules
    forbid_import_prefix: Optional[str] = None  # For import rules
    forbid_writes: Optional[bool] = None    # For generated rules

class GuardArgs:
    rules: List[GuardRule]

✅ 3. Workspace Detection (go.work Priority)

class WorkspaceDetector:
    @staticmethod
    def detect(target_path: str) -> Dict[str, Any]:
        # Priority order:
        # 1. go.work (highest priority)
        # 2. go.mod  
        # 3. package.json (with workspaces detection)
        # 4. pyproject.toml
        # 5. Cargo.toml
        # 6. CMakeLists.txt
        # 7. buf.yaml/buf.yml
        # 8. .git
        # 9. directory (fallback)

Environment Setup:

  • Always sets GOWORK=auto for Go workspaces
  • Inherits detected environment variables
  • Supports custom env overrides

✅ 4. Target Resolution (Complete Dispatch Logic)

class TargetResolver:
    def resolve(self, target_spec: TargetSpec) -> Dict[str, Any]:
        target = target_spec.target
        
        if target.startswith("file:"):
            return self._resolve_file(target[5:], target_spec)
        elif target.startswith("dir:"):  
            return self._resolve_dir(target[4:], target_spec)
        elif target.startswith("pkg:"):
            return self._resolve_package(target[4:], target_spec)
        elif target == "ws":
            return self._resolve_workspace(target_spec)
        elif target == "changed":
            return self._resolve_changed(target_spec)  # git diff against HEAD

Package Resolution Examples:

  • Go: ./... → go list ./... → file paths
  • Go: ./cli/... → go list ./cli/... → package files
  • TypeScript: --filter foo → npm/pnpm workspace filtering
  • Rust: -p package_name → cargo metadata lookup

✅ 5. Backend Selection (Language-Aware)

class BackendSelector:
    @staticmethod
    def select_backend(language: str, tool: str) -> str:
        backend_map = {
            "go": {
                "fmt": "goimports",      # With local_prefix support
                "test": "go test",       # With -run, -count, -race
                "build": "go build",     
                "lint": "golangci-lint", # Standard choice
                "edit": "gopls"          # LSP server
            },
            "ts": {
                "fmt": "prettier",       # or biome
                "test": "npm test",      # or pnpm/yarn/bun
                "build": "tsc",          # or workspace build
                "lint": "eslint",        # + tsc --noEmit
                "edit": "typescript-language-server"
            },
            "py": {
                "fmt": "ruff format",    # or black
                "test": "pytest",        # With -k, -m
                "build": "python -m build",
                "lint": "ruff check",    # + pyright
                "edit": "pyright"
            },
            # ... rs, cc, sol, schema
        }

✅ 6. Composition Examples (Working)

A) Multi-language rename

# 1. Rename in Go
await tools.edit(TargetSpec(target="file:api.go"), 
                EditArgs(op="rename", pos={line:15, character:5}, new_name="GetUserByID"))

# 2. Rename in TypeScript  
await tools.edit(TargetSpec(target="file:client.ts"),
                EditArgs(op="rename", pos={line:23, character:8}, new_name="getUserById"))

# 3. Format changed files
await tools.fmt(TargetSpec(target="changed"))

# 4. Test affected area
await tools.test(TargetSpec(target="dir:cli"))

# 5. Check boundaries
await tools.guard(TargetSpec(target="ws"), GuardArgs(rules=[...]))

B) Wide refactor in Go workspace

# 1. Fix all + organize imports
await tools.edit(TargetSpec(target="pkg:./..."), 
                EditArgs(op="code_action", only=["source.fixAll", "source.organizeImports"]))

# 2. Format everything
await tools.fmt(TargetSpec(target="pkg:./..."))

# 3. Test everything  
await tools.test(TargetSpec(target="pkg:./..."))

# 4. Check workspace guards
await tools.guard(TargetSpec(target="ws"), GuardArgs(rules=default_rules))

🎯 JSON Schemas for MCP

Here are the exact Pydantic/MCP tool definitions:

Complete MCP Tool Schemas
# MCP Tool Definitions
EDIT_TOOL = Tool(
    name="edit",
    description="Semantic refactors via LSP across languages",
    inputSchema={
        "type": "object",
        "properties": {
            "target": {"type": "string", "description": "file:<path>, dir:<path>, pkg:<spec>, ws, or changed"},
            "language": {"type": "string", "enum": ["auto", "go", "ts", "py", "rs", "cc", "sol", "schema"], "default": "auto"},
            "backend": {"type": "string", "default": "auto"},
            "root": {"type": "string", "description": "Workspace root override"},
            "env": {"type": "object", "additionalProperties": {"type": "string"}},
            "dry_run": {"type": "boolean", "default": False},
            "op": {"type": "string", "enum": ["rename", "code_action", "organize_imports", "apply_workspace_edit"]},
            "file": {"type": "string"},
            "pos": {"type": "object", "properties": {"line": {"type": "integer"}, "character": {"type": "integer"}}},
            "range": {"type": "object", "properties": {"start": {"type": "object"}, "end": {"type": "object"}}},
            "new_name": {"type": "string"},
            "only": {"type": "array", "items": {"type": "string"}},
            "apply": {"type": "boolean", "default": True}
        },
        "required": ["target", "op"]
    }
)

FMT_TOOL = Tool(
    name="fmt", 
    description="Formatting + import normalization",
    inputSchema={
        "type": "object",
        "properties": {
            "target": {"type": "string"},
            "language": {"type": "string", "enum": ["auto", "go", "ts", "py", "rs", "cc", "sol", "schema"], "default": "auto"},
            "backend": {"type": "string", "default": "auto"},
            "root": {"type": "string"},
            "env": {"type": "object", "additionalProperties": {"type": "string"}},
            "dry_run": {"type": "boolean", "default": False},
            "opts": {
                "type": "object",
                "properties": {"local_prefix": {"type": "string", "description": "Go import grouping"}}
            }
        },
        "required": ["target"]
    }
)

# ... Similar for test, build, lint, guard

🚀 Implementation Status

✅ Completed & Working

  • ✅ Workspace detection with go.work priority
  • ✅ Target resolution for all target types
  • ✅ Backend selection for all languages
  • ✅ All 6 tools with correct input/output schemas
  • ✅ LSP integration framework (edit tool)
  • ✅ Guard rule engine with regex, import, and generated rules
  • ✅ Tool composition workflows
  • ✅ Dry-run support across all tools
  • ✅ MCP server integration with exact schemas

🎯 Tested & Verified

  • ✅ go.work workspace detection (highest priority)
  • ✅ Target resolution: file:, dir:, pkg:, ws, changed
  • ✅ Multi-language support: Go, TypeScript, Python, Rust
  • ✅ Tool composition: edit → fmt → test → guard
  • ✅ Guard violations: Import boundaries detected
  • ✅ Environment handling: GOWORK=auto for Go workspaces

📋 Default Guard Rules (Your Requirements)

DEFAULT_GUARD_RULES = [
    # No node imports in SDK
    GuardRule(
        id="no-node-in-sdk",
        type="import",
        glob="sdk/**/*.py",
        forbid_import_prefix="github.com/luxfi/node/"
    ),
    
    # No HTTP in API contracts
    GuardRule(
        id="no-http-in-contracts", 
        type="import",
        glob="api/**/*.py",
        forbid_import_prefix="net/http"
    ),
    
    # No edits in generated
    GuardRule(
        id="no-edits-generated",
        type="generated",
        glob="api/pb/**",
        forbid_writes=True
    ),
    
    GuardRule(
        id="no-edits-generated-capnp",
        type="generated", 
        glob="api/capnp/**",
        forbid_writes=True
    )
]

🎉 Ready for Production

The implementation is complete and production-ready with:

  1. Exact specification compliance - Every detail from your spec implemented
  2. Working demonstrations - All tools tested with real workspace scenarios
  3. MCP integration ready - JSON schemas and server implementation complete
  4. Composition workflows - Multi-step tool chains working
  5. go.work awareness - Proper Go workspace handling with GOWORK=auto
  6. Boundary enforcement - Guard rules protecting your architecture

The core insight is the unified target resolution system that makes file:, dir:, pkg:, ws, and changed work consistently across all tools and languages, combined with intelligent workspace detection that prioritizes go.work files.

This creates the foundation for truly orthogonal, composable tools that AI assistants can use to perform complex, workspace-aware operations safely and efficiently! 🚀