mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
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:
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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()
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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}")
|
||||
|
||||
Executable
+96
@@ -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."
|
||||
Reference in New Issue
Block a user