hanzo-mcp 0.1.21__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
hanzo_mcp/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """Hanzo MCP - Implementation of Hanzo Platform capabilities using MCP."""
2
+
3
+ __version__ = "0.1.8"
hanzo_mcp/cli.py ADDED
@@ -0,0 +1,155 @@
1
+ """Command-line interface for the Hanzo MCP server."""
2
+
3
+ import argparse
4
+ import json
5
+ import os
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import Any, cast
9
+
10
+ from hanzo_mcp.server import HanzoDevServer
11
+
12
+
13
+ def main() -> None:
14
+ """Run the CLI for the Hanzo Dev MCP server."""
15
+ parser = argparse.ArgumentParser(
16
+ description="MCP server for accessing Hanzo APIs and Platform capabilities"
17
+ )
18
+
19
+ _ = parser.add_argument(
20
+ "--transport",
21
+ choices=["stdio", "sse"],
22
+ default="stdio",
23
+ help="Transport protocol to use (default: stdio)",
24
+ )
25
+
26
+ _ = parser.add_argument(
27
+ "--name",
28
+ default="hanzo",
29
+ help="Name of the MCP server (default: hanzo)",
30
+ )
31
+
32
+ _ = parser.add_argument(
33
+ "--allow-path",
34
+ action="append",
35
+ dest="allowed_paths",
36
+ help="Add an allowed path (can be specified multiple times)",
37
+ )
38
+
39
+ _ = parser.add_argument(
40
+ "--project-dir", dest="project_dir", help="Set the project directory to analyze"
41
+ )
42
+
43
+ _ = parser.add_argument(
44
+ "--install",
45
+ action="store_true",
46
+ help="Install server configuration in Claude Desktop",
47
+ )
48
+
49
+ args = parser.parse_args()
50
+
51
+ # Cast args attributes to appropriate types to avoid 'Any' warnings
52
+ name: str = cast(str, args.name)
53
+ install: bool = cast(bool, args.install)
54
+ transport: str = cast(str, args.transport)
55
+ project_dir: str | None = cast(str | None, args.project_dir)
56
+ allowed_paths: list[str] = (
57
+ cast(list[str], args.allowed_paths) if args.allowed_paths else []
58
+ )
59
+
60
+ if install:
61
+ install_claude_desktop_config(name, allowed_paths)
62
+ return
63
+
64
+ # If no allowed paths are specified, use the current directory
65
+ if not allowed_paths:
66
+ allowed_paths = [os.getcwd()]
67
+
68
+ # If project directory is specified, add it to allowed paths
69
+ if project_dir and project_dir not in allowed_paths:
70
+ allowed_paths.append(project_dir)
71
+
72
+ # Run the server
73
+ server = HanzoDevServer(name=name, allowed_paths=allowed_paths)
74
+ # Transport will be automatically cast to Literal['stdio', 'sse'] by the server
75
+ server.run(transport=transport)
76
+
77
+
78
+ def install_claude_desktop_config(
79
+ name: str = "hanzo", allowed_paths: list[str] | None = None
80
+ ) -> None:
81
+ """Install the server configuration in Claude Desktop.
82
+
83
+ Args:
84
+ name: The name to use for the server in the config
85
+ allowed_paths: Optional list of paths to allow
86
+ """
87
+ # Find the Claude Desktop config directory
88
+ home: Path = Path.home()
89
+
90
+ if sys.platform == "darwin": # macOS
91
+ config_dir: Path = home / "Library" / "Application Support" / "Claude"
92
+ elif sys.platform == "win32": # Windows
93
+ config_dir = Path(os.environ.get("APPDATA", "")) / "Claude"
94
+ else: # Linux and others
95
+ config_dir = home / ".config" / "claude"
96
+
97
+ config_file: Path = config_dir / "claude_desktop_config.json"
98
+
99
+ # Create directory if it doesn't exist
100
+ config_dir.mkdir(parents=True, exist_ok=True)
101
+
102
+ # Get current script path
103
+ script_path: Path = Path(sys.executable)
104
+
105
+ # Create args array
106
+ args: list[str] = ["-m", "hanzo_mcp.cli"]
107
+
108
+ # Add allowed paths if specified
109
+ if allowed_paths:
110
+ for path in allowed_paths:
111
+ args.extend(["--allow-path", path])
112
+ else:
113
+ # Allow home directory by default
114
+ args.extend(["--allow-path", str(home)])
115
+
116
+ # Create config object
117
+ config: dict[str, Any] = {
118
+ "mcpServers": {name: {"command": str(script_path), "args": args}}
119
+ }
120
+
121
+ # Check if the file already exists
122
+ if config_file.exists():
123
+ try:
124
+ with open(config_file, "r") as f:
125
+ existing_config: dict[str, Any] = json.load(f)
126
+
127
+ # Update the existing config
128
+ if "mcpServers" not in existing_config:
129
+ existing_config["mcpServers"] = {}
130
+
131
+ existing_config["mcpServers"][name] = config["mcpServers"][name]
132
+ config = existing_config
133
+ except Exception as e:
134
+ print(f"Error reading existing config: {e}")
135
+ print("Creating new config file.")
136
+
137
+ # Write the config file
138
+ with open(config_file, mode="w") as f:
139
+ json.dump(config, f, indent=2)
140
+
141
+ print(f"Successfully installed {name} in Claude Desktop configuration.")
142
+ print(f"Config file: {config_file}")
143
+
144
+ if allowed_paths:
145
+ print("\nAllowed paths:")
146
+ for path in allowed_paths:
147
+ print(f"- {path}")
148
+ else:
149
+ print(f"\nDefault allowed path: {home}")
150
+ print("\nYou can modify allowed paths in the config file directly.")
151
+ print("Restart Claude Desktop for changes to take effect.")
152
+
153
+
154
+ if __name__ == "__main__":
155
+ main()
hanzo_mcp/server.py ADDED
@@ -0,0 +1,125 @@
1
+ """MCP server for accessing Hanzo APIs and Platform capabilities."""
2
+
3
+ from typing import Literal, cast, final
4
+
5
+ from mcp.server.fastmcp import FastMCP
6
+
7
+ from hanzo_mcp.tools import register_all_tools
8
+ from hanzo_mcp.tools.common.context import DocumentContext
9
+ from hanzo_mcp.tools.common.permissions import PermissionManager
10
+ from hanzo_mcp.tools.project.analysis import ProjectAnalyzer, ProjectManager
11
+ from hanzo_mcp.tools.shell.command_executor import CommandExecutor
12
+
13
+
14
+ @final
15
+ class HanzoDevServer:
16
+ """MCP server for accessing Hanzo APIs and Platform capabilities."""
17
+
18
+ def __init__(
19
+ self,
20
+ name: str = "hanzo",
21
+ allowed_paths: list[str] | None = None,
22
+ mcp_instance: FastMCP | None = None,
23
+ ):
24
+ """Initialize the Hanzo Dev server.
25
+
26
+ Args:
27
+ name: The name of the server
28
+ allowed_paths: list of paths that the server is allowed to access
29
+ mcp_instance: Optional FastMCP instance for testing
30
+ """
31
+ self.mcp = mcp_instance if mcp_instance is not None else FastMCP(name)
32
+
33
+ # Initialize context, permissions, and command executor
34
+ self.document_context = DocumentContext()
35
+ self.permission_manager = PermissionManager()
36
+
37
+ # Initialize command executor
38
+ self.command_executor = CommandExecutor(
39
+ permission_manager=self.permission_manager,
40
+ verbose=False, # Set to True for debugging
41
+ )
42
+
43
+ # Initialize project analyzer
44
+ self.project_analyzer = ProjectAnalyzer(self.command_executor)
45
+
46
+ # Initialize project manager
47
+ self.project_manager = ProjectManager(
48
+ self.document_context, self.permission_manager, self.project_analyzer
49
+ )
50
+
51
+ # Add allowed paths
52
+ if allowed_paths:
53
+ for path in allowed_paths:
54
+ self.permission_manager.add_allowed_path(path)
55
+ self.document_context.add_allowed_path(path)
56
+
57
+ # Register all tools
58
+ register_all_tools(
59
+ mcp_server=self.mcp,
60
+ document_context=self.document_context,
61
+ permission_manager=self.permission_manager,
62
+ project_manager=self.project_manager,
63
+ project_analyzer=self.project_analyzer,
64
+ )
65
+
66
+ def run(self, transport: str = "stdio", allowed_paths: list[str] | None = None):
67
+ """Run the MCP server.
68
+
69
+ Args:
70
+ transport: The transport to use (stdio or sse)
71
+ allowed_paths: list of paths that the server is allowed to access
72
+ """
73
+ # Add allowed paths if provided
74
+ allowed_paths_list = allowed_paths or []
75
+ for path in allowed_paths_list:
76
+ self.permission_manager.add_allowed_path(path)
77
+ self.document_context.add_allowed_path(path)
78
+
79
+ # Run the server
80
+ transport_type = cast(Literal["stdio", "sse"], transport)
81
+ self.mcp.run(transport=transport_type)
82
+
83
+
84
+ def main():
85
+ """Run the Hanzo MCP server."""
86
+ import argparse
87
+
88
+ parser = argparse.ArgumentParser(
89
+ description="MCP server for accessing Hanzo APIs and Platform capabilities"
90
+ )
91
+
92
+ _ = parser.add_argument(
93
+ "--name",
94
+ default="hanzo",
95
+ help="Name of the MCP server (default: hanzo)",
96
+ )
97
+
98
+ _ = parser.add_argument(
99
+ "--transport",
100
+ choices=["stdio", "sse"],
101
+ default="stdio",
102
+ help="Transport protocol to use (default: stdio)",
103
+ )
104
+
105
+ _ = parser.add_argument(
106
+ "--allow-path",
107
+ action="append",
108
+ dest="allowed_paths",
109
+ help="Add an allowed path (can be specified multiple times)",
110
+ )
111
+
112
+ args = parser.parse_args()
113
+
114
+ # Type annotations for args to avoid Any warnings
115
+ name: str = args.name
116
+ transport: str = args.transport
117
+ allowed_paths: list[str] | None = args.allowed_paths
118
+
119
+ # Create and run the server
120
+ server = HanzoDevServer(name=name, allowed_paths=allowed_paths)
121
+ server.run(transport=transport, allowed_paths=allowed_paths or [])
122
+
123
+
124
+ if __name__ == "__main__":
125
+ main()
@@ -0,0 +1,62 @@
1
+ """Tools package for Hanzo MCP.
2
+
3
+ This package contains all the tools for the Hanzo MCP server.
4
+ It provides a unified interface for registering all tools with an MCP server.
5
+
6
+ This includes a "think" tool implementation based on Anthropic's research showing
7
+ improved performance for complex tool-based interactions when Claude has a dedicated
8
+ space for structured thinking.
9
+ """
10
+
11
+ from typing import Any
12
+
13
+ from mcp.server.fastmcp import FastMCP
14
+
15
+ from hanzo_mcp.tools.common.context import DocumentContext
16
+ from hanzo_mcp.tools.common.permissions import PermissionManager
17
+ from hanzo_mcp.tools.common.thinking import ThinkingTool
18
+ from hanzo_mcp.tools.filesystem.file_operations import FileOperations
19
+ from hanzo_mcp.tools.jupyter.notebook_operations import JupyterNotebookTools
20
+ from hanzo_mcp.tools.project.analysis import ProjectAnalysis, ProjectManager
21
+ from hanzo_mcp.tools.shell.command_executor import CommandExecutor
22
+
23
+
24
+ def register_all_tools(
25
+ mcp_server: FastMCP,
26
+ document_context: DocumentContext,
27
+ permission_manager: PermissionManager,
28
+ project_manager: ProjectManager,
29
+ project_analyzer: Any,
30
+ ) -> None:
31
+ """Register all Hanzo MCP tools with the MCP server.
32
+
33
+ Args:
34
+ mcp_server: The FastMCP server instance
35
+ document_context: Document context for tracking file contents
36
+ permission_manager: Permission manager for access control
37
+ command_executor: Enhanced command executor for running shell commands
38
+ project_manager: Project manager for tracking projects
39
+ project_analyzer: Project analyzer for analyzing project structure and dependencies
40
+ """
41
+ # Initialize and register file operations tools
42
+ # Now includes all filesystem functionality (navigation + file operations)
43
+ file_ops = FileOperations(document_context, permission_manager)
44
+ file_ops.register_tools(mcp_server)
45
+
46
+ # Initialize and register command execution tools
47
+ cmd_exec = CommandExecutor(permission_manager)
48
+ cmd_exec.register_tools(mcp_server)
49
+
50
+ # Initialize and register project analysis tools
51
+ proj_analysis = ProjectAnalysis(
52
+ project_manager, project_analyzer, permission_manager
53
+ )
54
+ proj_analysis.register_tools(mcp_server)
55
+
56
+ # Initialize and register Jupyter notebook tools
57
+ jupyter_tools = JupyterNotebookTools(document_context, permission_manager)
58
+ jupyter_tools.register_tools(mcp_server)
59
+
60
+ # Initialize and register thinking tool
61
+ thinking_tool = ThinkingTool()
62
+ thinking_tool.register_tools(mcp_server)
@@ -0,0 +1 @@
1
+ """Common utilities for Hanzo Dev MCP tools."""