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 bff668fbf6
commit 95a9f9879a
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}")
+96
View File
@@ -0,0 +1,96 @@
#!/bin/bash
# Script to build and publish updated Hanzo packages to PyPI
set -e
echo "Building and publishing Hanzo Python SDK packages..."
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Function to build and check package
build_package() {
local pkg_dir=$1
local pkg_name=$2
echo -e "${YELLOW}Building $pkg_name...${NC}"
cd "$pkg_dir"
# Clean previous builds
rm -rf dist/ build/ *.egg-info/
# Build the package
python -m build
# Check the built package
if [ -f dist/*.whl ]; then
echo -e "${GREEN}✓ Built $pkg_name successfully${NC}"
ls -la dist/
else
echo -e "${RED}✗ Failed to build $pkg_name${NC}"
return 1
fi
cd - > /dev/null
}
# Function to publish package
publish_package() {
local pkg_dir=$1
local pkg_name=$2
echo -e "${YELLOW}Publishing $pkg_name to PyPI...${NC}"
cd "$pkg_dir"
# Upload to PyPI (requires authentication)
# Uncomment when ready to publish:
# python -m twine upload dist/*
echo -e "${GREEN}Ready to publish: twine upload dist/*${NC}"
cd - > /dev/null
}
# Main packages to publish
PACKAGES=(
"pkg/hanzo-memory:hanzo-memory:1.0.1"
"pkg/hanzo-mcp:hanzo-mcp:0.8.9"
"pkg/hanzo-agents:hanzo-agents:0.1.0"
"pkg/hanzo-network:hanzo-network:0.1.3"
"pkg/hanzo-aci:hanzo-aci:0.2.8"
"pkg/hanzo-repl:hanzo-repl:0.1.0"
"pkg/hanzoai:hanzoai:2.1.0"
)
echo "Packages to build and publish:"
for pkg_info in "${PACKAGES[@]}"; do
IFS=':' read -r dir name version <<< "$pkg_info"
echo " - $name v$version ($dir)"
done
echo
# Build all packages first
echo -e "${YELLOW}Building all packages...${NC}"
for pkg_info in "${PACKAGES[@]}"; do
IFS=':' read -r dir name version <<< "$pkg_info"
build_package "$dir" "$name"
done
echo
echo -e "${GREEN}All packages built successfully!${NC}"
echo
echo "To publish to PyPI, run:"
echo " 1. Make sure you have PyPI credentials configured:"
echo " python -m pip install --upgrade twine"
echo " python -m twine login"
echo
echo " 2. For each package, run:"
for pkg_info in "${PACKAGES[@]}"; do
IFS=':' read -r dir name version <<< "$pkg_info"
echo " cd $dir && python -m twine upload dist/*"
done
echo
echo "Or uncomment the twine upload line in this script and run again."