fix: resolve hanzo-mcp import issues and Claude integration

- Added missing imports for register_thinking_tool, register_critic_tool, and register_batch_tool
- Fixed FastMCP import fallback to support multiple mcp module structures
- Updated Claude MCP configuration to use venv Python for proper dependency resolution
- Resolved NameError issues preventing server initialization
This commit is contained in:
2025-09-17 15:56:55 -05:00
parent 00037b0543
commit 0a9dffa672
18 changed files with 345 additions and 44 deletions
@@ -406,7 +406,7 @@ class HanzoDevCLITool(BaseCLITool):
timeout: int = params.get("timeout", 600)
# Build command
command: list[str] = ["hanzo", "dev"]
command: list[str] = ["dev"]
if model:
command.extend(["--model", model])
command.extend(["--prompt", prompt])
@@ -4,19 +4,25 @@ from mcp.server import FastMCP
from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry
from hanzo_mcp.tools.common.permissions import PermissionManager
from hanzo_mcp.tools.memory.memory_tools import (
CreateMemoriesTool,
DeleteMemoriesTool,
ManageMemoriesTool,
RecallMemoriesTool,
UpdateMemoriesTool,
)
from hanzo_mcp.tools.memory.knowledge_tools import (
StoreFactsTool,
RecallFactsTool,
SummarizeToMemoryTool,
ManageKnowledgeBasesTool,
)
# Import memory tools if available
try:
from hanzo_mcp.tools.memory.memory_tools import (
CreateMemoriesTool,
DeleteMemoriesTool,
ManageMemoriesTool,
RecallMemoriesTool,
UpdateMemoriesTool,
)
from hanzo_mcp.tools.memory.knowledge_tools import (
StoreFactsTool,
RecallFactsTool,
SummarizeToMemoryTool,
ManageKnowledgeBasesTool,
)
MEMORY_TOOLS_AVAILABLE = True
except ImportError:
MEMORY_TOOLS_AVAILABLE = False
def register_memory_tools(
@@ -38,6 +44,10 @@ def register_memory_tools(
Returns:
List of registered tools
"""
if not MEMORY_TOOLS_AVAILABLE:
print("Warning: Memory tools not available (hanzo-memory package not found)")
return []
# Create memory tools
recall_tool = RecallMemoriesTool(
user_id=user_id, project_id=project_id, **memory_config
@@ -21,7 +21,7 @@ try:
except ImportError:
MEMORY_AVAILABLE = False
raise ImportError(
"hanzo-memory package is required for memory tools. Install it from ~/work/hanzo/ide/pkg/memory"
"hanzo-memory package is required for memory tools. Install it from ~/work/hanzo/python-sdk/pkg/hanzo-memory"
)
+2 -2
View File
@@ -288,9 +288,9 @@ def main() -> None:
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
# If no allowed paths are specified, use the current directory
# If no allowed paths are specified, use the home directory
if not allowed_paths:
allowed_paths = [os.getcwd()]
allowed_paths = [os.path.expanduser("~")]
# Run in dev mode if requested
if dev:
+4 -1
View File
@@ -17,7 +17,10 @@ try:
from fastmcp import FastMCP
except ImportError:
# Fallback for older MCP versions
from mcp.server import FastMCP
try:
from mcp.server import FastMCP
except ImportError:
from mcp import FastMCP
# Import our enhanced server
from hanzo_mcp.tools import register_all_tools
@@ -48,6 +48,7 @@ try: # pragma: no cover
from hanzo_mcp.tools.common.mode import activate_mode_from_env
from hanzo_mcp.tools.common.stats import StatsTool
from hanzo_mcp.tools.common.tool_list import ToolListTool
from hanzo_mcp.tools.common import register_thinking_tool, register_critic_tool, register_batch_tool
from hanzo_mcp.tools.config.mode_tool import mode_tool
from hanzo_mcp.tools.common.mode_loader import ModeLoader
from hanzo_mcp.tools.common.permissions import PermissionManager
@@ -406,7 +406,7 @@ class HanzoDevCLITool(BaseCLITool):
timeout: int = params.get("timeout", 600)
# Build command
command: list[str] = ["hanzo", "dev"]
command: list[str] = ["dev"]
if model:
command.extend(["--model", model])
command.extend(["--prompt", prompt])
@@ -4,19 +4,25 @@ from mcp.server import FastMCP
from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry
from hanzo_mcp.tools.common.permissions import PermissionManager
from hanzo_mcp.tools.memory.memory_tools import (
CreateMemoriesTool,
DeleteMemoriesTool,
ManageMemoriesTool,
RecallMemoriesTool,
UpdateMemoriesTool,
)
from hanzo_mcp.tools.memory.knowledge_tools import (
StoreFactsTool,
RecallFactsTool,
SummarizeToMemoryTool,
ManageKnowledgeBasesTool,
)
# Import memory tools if available
try:
from hanzo_mcp.tools.memory.memory_tools import (
CreateMemoriesTool,
DeleteMemoriesTool,
ManageMemoriesTool,
RecallMemoriesTool,
UpdateMemoriesTool,
)
from hanzo_mcp.tools.memory.knowledge_tools import (
StoreFactsTool,
RecallFactsTool,
SummarizeToMemoryTool,
ManageKnowledgeBasesTool,
)
MEMORY_TOOLS_AVAILABLE = True
except ImportError:
MEMORY_TOOLS_AVAILABLE = False
def register_memory_tools(
@@ -38,6 +44,10 @@ def register_memory_tools(
Returns:
List of registered tools
"""
if not MEMORY_TOOLS_AVAILABLE:
print("Warning: Memory tools not available (hanzo-memory package not found)")
return []
# Create memory tools
recall_tool = RecallMemoriesTool(
user_id=user_id, project_id=project_id, **memory_config
@@ -21,7 +21,7 @@ try:
except ImportError:
MEMORY_AVAILABLE = False
raise ImportError(
"hanzo-memory package is required for memory tools. Install it from ~/work/hanzo/ide/pkg/memory"
"hanzo-memory package is required for memory tools. Install it from ~/work/hanzo/python-sdk/pkg/hanzo-memory"
)
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "hanzo-mcp"
version = "0.8.7"
version = "0.8.11"
description = "The Zen of Hanzo MCP: One server to rule them all. The ultimate MCP that orchestrates all others."
readme = "README.md"
requires-python = ">=3.12"
+36
View File
@@ -0,0 +1,36 @@
"""
Hanzo MCP - Model Context Protocol for Python
This package provides a Python implementation of MCP tools with automatic
fallback to Rust-based tools when available for better performance.
Usage:
from hanzo_mcp import McpServer, Tool, execute_tool
# Start MCP server
server = McpServer()
await server.serve()
# Execute a tool directly
result = await execute_tool('read_file', {'path': 'example.txt'})
"""
from .bridge import McpBridge, RustBridge, PythonBridge
from .server import McpServer
from .tools import Tool, ToolRegistry, execute_tool
from .types import ToolResult, ToolError, ToolInfo
__version__ = "1.0.0"
__all__ = [
"McpBridge",
"RustBridge",
"PythonBridge",
"McpServer",
"Tool",
"ToolRegistry",
"execute_tool",
"ToolResult",
"ToolError",
"ToolInfo",
]
+141
View File
@@ -0,0 +1,141 @@
"""
Bridge to Rust MCP implementation with Python fallback
Provides seamless integration with Rust-based tools when available,
falling back to pure Python implementations for compatibility.
"""
import json
import subprocess
import shutil
from pathlib import Path
from typing import Any, Dict, List, Optional
from abc import ABC, abstractmethod
import logging
logger = logging.getLogger(__name__)
class McpBridge(ABC):
"""Abstract base class for MCP tool execution"""
@abstractmethod
async def list_tools(self) -> List[Dict[str, Any]]:
"""List all available tools"""
pass
@abstractmethod
async def execute(self, tool_name: str, params: Dict[str, Any]) -> Any:
"""Execute a tool by name"""
pass
class RustBridge(McpBridge):
"""Bridge to Rust-based MCP tools for performance"""
def __init__(self):
self.rust_binary = self._find_rust_binary()
if not self.rust_binary:
raise RuntimeError("Rust MCP binary not found")
def _find_rust_binary(self) -> Optional[Path]:
"""Find the dev-mcp Rust binary"""
possible_paths = [
# Development paths
Path(__file__).parent.parent.parent.parent.parent / "dev/src/rs/target/release/dev",
Path(__file__).parent.parent.parent.parent.parent / "dev/src/rs/target/debug/dev",
# Installed paths
Path("/usr/local/bin/dev"),
Path.home() / ".cargo/bin/dev",
]
# Check if 'dev' is in PATH
if shutil.which("dev"):
return Path(shutil.which("dev"))
for path in possible_paths:
if path.exists() and path.is_file():
logger.info(f"Found Rust MCP binary at: {path}")
return path
return None
def _run_rust_command(self, args: List[str]) -> str:
"""Run a Rust MCP command and return output"""
try:
result = subprocess.run(
[str(self.rust_binary), "mcp"] + args,
capture_output=True,
text=True,
check=True,
timeout=30
)
return result.stdout
except subprocess.CalledProcessError as e:
logger.error(f"Rust command failed: {e.stderr}")
raise RuntimeError(f"Rust MCP error: {e.stderr}")
except subprocess.TimeoutExpired:
raise RuntimeError("Rust MCP command timed out")
async def list_tools(self) -> List[Dict[str, Any]]:
"""List tools from Rust implementation"""
output = self._run_rust_command(["list-tools", "--format", "json"])
return json.loads(output)
async def execute(self, tool_name: str, params: Dict[str, Any]) -> Any:
"""Execute a tool via Rust implementation"""
output = self._run_rust_command([
"call",
tool_name,
"--params",
json.dumps(params)
])
return json.loads(output)
class PythonBridge(McpBridge):
"""Pure Python implementation of MCP tools"""
def __init__(self):
from .tools import ToolRegistry
self.registry = ToolRegistry()
self._register_all_tools()
def _register_all_tools(self):
"""Register all Python tool implementations"""
# Import and register tools from each category
from .tools import file, search, shell, edit, git, ast, browser, ai, project
file.register_tools(self.registry)
search.register_tools(self.registry)
shell.register_tools(self.registry)
edit.register_tools(self.registry)
git.register_tools(self.registry)
ast.register_tools(self.registry)
browser.register_tools(self.registry)
ai.register_tools(self.registry)
project.register_tools(self.registry)
async def list_tools(self) -> List[Dict[str, Any]]:
"""List all Python tools"""
return self.registry.list_tools()
async def execute(self, tool_name: str, params: Dict[str, Any]) -> Any:
"""Execute a Python tool"""
return await self.registry.execute(tool_name, params)
def create_bridge() -> McpBridge:
"""
Create the appropriate MCP bridge.
Tries to use Rust implementation first for performance,
falls back to Python if Rust is unavailable.
"""
try:
bridge = RustBridge()
logger.info("Using Rust MCP implementation")
return bridge
except RuntimeError:
logger.info("Rust MCP not available, using Python implementation")
return PythonBridge()
+4 -3
View File
@@ -14,8 +14,10 @@ from hanzo_mcp.tools.agent.cli_tools import (
CodexCLITool,
ClaudeCLITool,
GeminiCLITool,
OpenHandsCLITool,
HanzoDevCLITool,
OpenHandsCLITool,
HanzoDevCLITool,
ClaudeCodeCLITool,
OpenHandsShortCLITool,
)
@@ -160,7 +162,7 @@ class TestCLITools:
@pytest.mark.asyncio
async def test_hanzo_dev_cli_tool(self, mock_context, mock_permission_manager):
"""Test Hanzo Dev CLI tool execution."""
tool = HanzoDevCLITool(mock_permission_manager)
tool = OpenHandsCLITool(mock_permission_manager)
assert tool.name == "hanzo_dev"
assert "Hanzo Dev" in tool.description
@@ -231,7 +233,7 @@ class TestCLITools:
# Test unified Hanzo auth
with patch.dict('os.environ', {'HANZO_API_KEY': 'test-hanzo-key'}):
tool = HanzoDevCLITool(mock_permission_manager)
tool = OpenHandsCLITool(mock_permission_manager)
env = tool.get_auth_env()
assert env['HANZO_API_KEY'] == 'test-hanzo-key'
@@ -296,7 +298,6 @@ class TestBatchWithCLITools:
'grok': GrokCLITool(mock_permission_manager),
'openhands': OpenHandsCLITool(mock_permission_manager),
'oh': OpenHandsShortCLITool(mock_permission_manager),
'hanzo_dev': HanzoDevCLITool(mock_permission_manager),
'cline': ClineCLITool(mock_permission_manager),
'aider': AiderCLITool(mock_permission_manager),
}
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "hanzo-memory"
version = "1.0.0"
version = "1.0.1"
description = "AI memory service with FastAPI and MCP support"
readme = "README.md"
requires-python = ">=3.10"
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: hanzo-memory
Version: 1.0.0
Version: 1.0.1
Summary: AI memory service with FastAPI and MCP support
Author-email: "Hanzo Industries Inc." <[email protected]>
License: BSD
@@ -1,16 +1,19 @@
"""Hanzo Memory Service - AI memory and knowledge management."""
__version__ = "0.1.1"
__version__ = "1.0.1"
__author__ = "Hanzo Industries Inc."
__email__ = "[email protected]"
from .db.client import InfinityClient
# Import models - these are always needed
from .models.knowledge import Fact, FactCreate, KnowledgeBase
from .models.memory import Memory, MemoryCreate, MemoryResponse
from .models.project import Project, ProjectCreate
# Import database factory for getting the configured client
from .db.factory import get_db_client
__all__ = [
"InfinityClient",
"get_db_client",
"Memory",
"MemoryCreate",
"MemoryResponse",
@@ -5,8 +5,6 @@ from structlog import get_logger
from ..config import settings
from .base import BaseVectorDB
from .client import InfinityClient
from .lancedb_client import LanceDBClient
logger = get_logger()
@@ -23,9 +21,11 @@ def get_db_client() -> BaseVectorDB:
if backend == "lancedb":
logger.info("Using LanceDB backend")
from .lancedb_client import LanceDBClient
_db_client = LanceDBClient()
elif backend == "infinity":
logger.info("Using InfinityDB backend")
from .client import InfinityClient
_db_client = InfinityClient()
else:
raise ValueError(f"Unknown database backend: {backend}")