hanzo-mcp 0.2.0__tar.gz → 0.3.3__tar.gz
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.
Potentially problematic release.
This version of hanzo-mcp might be problematic. Click here for more details.
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/PKG-INFO +20 -3
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/README.md +15 -1
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/__init__.py +1 -1
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/cli.py +138 -25
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/server.py +26 -4
- hanzo_mcp-0.3.3/hanzo_mcp/tools/agent/base_provider.py +73 -0
- hanzo_mcp-0.3.3/hanzo_mcp/tools/agent/litellm_provider.py +45 -0
- hanzo_mcp-0.3.3/hanzo_mcp/tools/agent/lmstudio_agent.py +385 -0
- hanzo_mcp-0.3.3/hanzo_mcp/tools/agent/lmstudio_provider.py +219 -0
- hanzo_mcp-0.3.3/hanzo_mcp/tools/agent/provider_registry.py +120 -0
- hanzo_mcp-0.3.3/hanzo_mcp/tools/common/error_handling.py +86 -0
- hanzo_mcp-0.3.3/hanzo_mcp/tools/common/logging_config.py +115 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/shell/command_executor.py +3 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp.egg-info/PKG-INFO +20 -3
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp.egg-info/SOURCES.txt +7 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp.egg-info/requires.txt +3 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/pyproject.toml +3 -3
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/LICENSE +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/__init__.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/agent/__init__.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/agent/agent_tool.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/agent/prompt.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/agent/tool_adapter.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/common/__init__.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/common/base.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/common/context.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/common/permissions.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/common/session.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/common/think_tool.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/common/validation.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/common/version_tool.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/filesystem/__init__.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/filesystem/base.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/filesystem/content_replace.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/filesystem/directory_tree.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/filesystem/edit_file.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/filesystem/get_file_info.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/filesystem/read_files.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/filesystem/search_content.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/filesystem/write_file.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/jupyter/__init__.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/jupyter/base.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/jupyter/edit_notebook.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/jupyter/notebook_operations.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/jupyter/read_notebook.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/project/__init__.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/project/analysis.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/project/base.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/project/project_analyze.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/shell/__init__.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/shell/base.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/shell/run_command.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/shell/run_script.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp/tools/shell/script_tool.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp.egg-info/dependency_links.txt +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp.egg-info/entry_points.txt +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/hanzo_mcp.egg-info/top_level.txt +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/setup.cfg +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/setup.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/tests/test_cli.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/tests/test_server.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/tests/test_tools_registration.py +0 -0
- {hanzo_mcp-0.2.0 → hanzo_mcp-0.3.3}/tests/test_validation.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hanzo-mcp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: MCP implementation of Hanzo capabilities
|
|
5
5
|
Author-email: Hanzo Industries Inc <dev@hanzo.ai>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/hanzoai/mcp
|
|
8
8
|
Project-URL: Bug Tracker, https://github.com/hanzoai/mcp/issues
|
|
9
|
-
Project-URL: Documentation, https://github.com/hanzoai/mcp/
|
|
9
|
+
Project-URL: Documentation, https://github.com/hanzoai/mcp/tree/main/docs
|
|
10
10
|
Keywords: mcp,claude,hanzo,code,agent
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -25,6 +25,9 @@ Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
|
25
25
|
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
26
26
|
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
27
27
|
Requires-Dist: black>=23.3.0; extra == "dev"
|
|
28
|
+
Requires-Dist: sphinx>=8.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: sphinx-rtd-theme>=1.3.0; extra == "dev"
|
|
30
|
+
Requires-Dist: myst-parser>=2.0.0; extra == "dev"
|
|
28
31
|
Provides-Extra: test
|
|
29
32
|
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
30
33
|
Requires-Dist: pytest-cov>=4.1.0; extra == "test"
|
|
@@ -93,7 +96,7 @@ uv pip install hanzo-mcp
|
|
|
93
96
|
pip install hanzo-mcp
|
|
94
97
|
```
|
|
95
98
|
|
|
96
|
-
For detailed installation and configuration instructions, please refer to [
|
|
99
|
+
For detailed installation and configuration instructions, please refer to the [documentation](./docs/).
|
|
97
100
|
|
|
98
101
|
Of course, you can also read [USEFUL_PROMPTS](./doc/USEFUL_PROMPTS.md) for some inspiration on how to use hanzo-mcp.
|
|
99
102
|
|
|
@@ -106,6 +109,20 @@ This implementation follows best practices for securing access to your filesyste
|
|
|
106
109
|
- Input validation and sanitization
|
|
107
110
|
- Proper error handling and reporting
|
|
108
111
|
|
|
112
|
+
## Documentation
|
|
113
|
+
|
|
114
|
+
Comprehensive documentation is available in the [docs](./docs/) directory. You can build and view the documentation locally:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# Build the documentation
|
|
118
|
+
make docs
|
|
119
|
+
|
|
120
|
+
# Start a local server to view the documentation
|
|
121
|
+
make docs-serve
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Then open http://localhost:8000/ in your browser to view the documentation.
|
|
125
|
+
|
|
109
126
|
## Development
|
|
110
127
|
|
|
111
128
|
### Setup Development Environment
|
|
@@ -52,7 +52,7 @@ uv pip install hanzo-mcp
|
|
|
52
52
|
pip install hanzo-mcp
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
For detailed installation and configuration instructions, please refer to [
|
|
55
|
+
For detailed installation and configuration instructions, please refer to the [documentation](./docs/).
|
|
56
56
|
|
|
57
57
|
Of course, you can also read [USEFUL_PROMPTS](./doc/USEFUL_PROMPTS.md) for some inspiration on how to use hanzo-mcp.
|
|
58
58
|
|
|
@@ -65,6 +65,20 @@ This implementation follows best practices for securing access to your filesyste
|
|
|
65
65
|
- Input validation and sanitization
|
|
66
66
|
- Proper error handling and reporting
|
|
67
67
|
|
|
68
|
+
## Documentation
|
|
69
|
+
|
|
70
|
+
Comprehensive documentation is available in the [docs](./docs/) directory. You can build and view the documentation locally:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Build the documentation
|
|
74
|
+
make docs
|
|
75
|
+
|
|
76
|
+
# Start a local server to view the documentation
|
|
77
|
+
make docs-serve
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then open http://localhost:8000/ in your browser to view the documentation.
|
|
81
|
+
|
|
68
82
|
## Development
|
|
69
83
|
|
|
70
84
|
### Setup Development Environment
|
|
@@ -1,20 +1,42 @@
|
|
|
1
|
-
"""Command-line interface for the Hanzo MCP server.
|
|
1
|
+
"""Command-line interface for the Hanzo MCP server.
|
|
2
|
+
|
|
3
|
+
Includes logging configuration and enhanced error handling.
|
|
4
|
+
"""
|
|
2
5
|
|
|
3
6
|
import argparse
|
|
4
7
|
import json
|
|
8
|
+
import logging
|
|
5
9
|
import os
|
|
6
10
|
import sys
|
|
7
11
|
from pathlib import Path
|
|
8
12
|
from typing import Any, cast
|
|
9
13
|
|
|
14
|
+
from hanzo_mcp import __version__
|
|
15
|
+
from hanzo_mcp.tools.common.logging_config import setup_logging
|
|
16
|
+
|
|
10
17
|
from hanzo_mcp.server import HanzoServer
|
|
11
18
|
|
|
12
19
|
|
|
13
20
|
def main() -> None:
|
|
14
21
|
"""Run the CLI for the Hanzo MCP server."""
|
|
22
|
+
# Initialize logger
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
# Check if 'version' is the first argument
|
|
26
|
+
if len(sys.argv) > 1 and sys.argv[1] == 'version':
|
|
27
|
+
print(f"hanzo-mcp {__version__}")
|
|
28
|
+
return
|
|
29
|
+
|
|
15
30
|
parser = argparse.ArgumentParser(
|
|
16
31
|
description="MCP server implementing Hanzo capabilities"
|
|
17
32
|
)
|
|
33
|
+
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--version",
|
|
36
|
+
action="version",
|
|
37
|
+
version=f"%(prog)s {__version__}",
|
|
38
|
+
help="Show the current version and exit"
|
|
39
|
+
)
|
|
18
40
|
|
|
19
41
|
_ = parser.add_argument(
|
|
20
42
|
"--transport",
|
|
@@ -23,6 +45,19 @@ def main() -> None:
|
|
|
23
45
|
help="Transport protocol to use (default: stdio)",
|
|
24
46
|
)
|
|
25
47
|
|
|
48
|
+
_ = parser.add_argument(
|
|
49
|
+
"--port",
|
|
50
|
+
type=int,
|
|
51
|
+
default=3001,
|
|
52
|
+
help="Port to use for SSE transport (default: 3001)",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
_ = parser.add_argument(
|
|
56
|
+
"--host",
|
|
57
|
+
default="0.0.0.0",
|
|
58
|
+
help="Host to bind to for SSE transport (default: 0.0.0.0)",
|
|
59
|
+
)
|
|
60
|
+
|
|
26
61
|
_ = parser.add_argument(
|
|
27
62
|
"--name",
|
|
28
63
|
default="claude-code",
|
|
@@ -39,26 +74,26 @@ def main() -> None:
|
|
|
39
74
|
_ = parser.add_argument(
|
|
40
75
|
"--project-dir", dest="project_dir", help="Set the project directory to analyze"
|
|
41
76
|
)
|
|
42
|
-
|
|
77
|
+
|
|
43
78
|
_ = parser.add_argument(
|
|
44
79
|
"--agent-model",
|
|
45
80
|
dest="agent_model",
|
|
46
81
|
help="Specify the model name in LiteLLM format (e.g., 'openai/gpt-4o', 'anthropic/claude-3-sonnet')"
|
|
47
82
|
)
|
|
48
|
-
|
|
83
|
+
|
|
49
84
|
_ = parser.add_argument(
|
|
50
85
|
"--agent-max-tokens",
|
|
51
86
|
dest="agent_max_tokens",
|
|
52
87
|
type=int,
|
|
53
88
|
help="Specify the maximum tokens for agent responses"
|
|
54
89
|
)
|
|
55
|
-
|
|
90
|
+
|
|
56
91
|
_ = parser.add_argument(
|
|
57
92
|
"--agent-api-key",
|
|
58
93
|
dest="agent_api_key",
|
|
59
94
|
help="Specify the API key for the LLM provider (for development/testing only)"
|
|
60
95
|
)
|
|
61
|
-
|
|
96
|
+
|
|
62
97
|
_ = parser.add_argument(
|
|
63
98
|
"--agent-max-iterations",
|
|
64
99
|
dest="agent_max_iterations",
|
|
@@ -66,7 +101,7 @@ def main() -> None:
|
|
|
66
101
|
default=10,
|
|
67
102
|
help="Maximum number of iterations for agent (default: 10)"
|
|
68
103
|
)
|
|
69
|
-
|
|
104
|
+
|
|
70
105
|
_ = parser.add_argument(
|
|
71
106
|
"--agent-max-tool-uses",
|
|
72
107
|
dest="agent_max_tool_uses",
|
|
@@ -74,7 +109,7 @@ def main() -> None:
|
|
|
74
109
|
default=30,
|
|
75
110
|
help="Maximum number of total tool uses for agent (default: 30)"
|
|
76
111
|
)
|
|
77
|
-
|
|
112
|
+
|
|
78
113
|
_ = parser.add_argument(
|
|
79
114
|
"--enable-agent-tool",
|
|
80
115
|
dest="enable_agent_tool",
|
|
@@ -82,7 +117,31 @@ def main() -> None:
|
|
|
82
117
|
default=False,
|
|
83
118
|
help="Enable the agent tool (disabled by default)"
|
|
84
119
|
)
|
|
120
|
+
|
|
121
|
+
_ = parser.add_argument(
|
|
122
|
+
"--log-level",
|
|
123
|
+
dest="log_level",
|
|
124
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
125
|
+
default="INFO",
|
|
126
|
+
help="Set the logging level (default: INFO)"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
_ = parser.add_argument(
|
|
130
|
+
"--disable-file-logging",
|
|
131
|
+
dest="disable_file_logging",
|
|
132
|
+
action="store_true",
|
|
133
|
+
default=False,
|
|
134
|
+
help="Disable logging to file (logs to console only)"
|
|
135
|
+
)
|
|
85
136
|
|
|
137
|
+
_ = parser.add_argument(
|
|
138
|
+
"--enable-console-logging",
|
|
139
|
+
dest="enable_console_logging",
|
|
140
|
+
action="store_true",
|
|
141
|
+
default=False,
|
|
142
|
+
help="Enable logging to console (stdout/stderr). By default, logs to file only."
|
|
143
|
+
)
|
|
144
|
+
|
|
86
145
|
_ = parser.add_argument(
|
|
87
146
|
"--disable-write-tools",
|
|
88
147
|
dest="disable_write_tools",
|
|
@@ -103,6 +162,8 @@ def main() -> None:
|
|
|
103
162
|
name: str = cast(str, args.name)
|
|
104
163
|
install: bool = cast(bool, args.install)
|
|
105
164
|
transport: str = cast(str, args.transport)
|
|
165
|
+
port: int = cast(int, args.port)
|
|
166
|
+
host: str = cast(str, args.host)
|
|
106
167
|
project_dir: str | None = cast(str | None, args.project_dir)
|
|
107
168
|
agent_model: str | None = cast(str | None, args.agent_model)
|
|
108
169
|
agent_max_tokens: int | None = cast(int | None, args.agent_max_tokens)
|
|
@@ -111,17 +172,37 @@ def main() -> None:
|
|
|
111
172
|
agent_max_tool_uses: int = cast(int, args.agent_max_tool_uses)
|
|
112
173
|
enable_agent_tool: bool = cast(bool, args.enable_agent_tool)
|
|
113
174
|
disable_write_tools: bool = cast(bool, args.disable_write_tools)
|
|
175
|
+
log_level: str = cast(str, args.log_level)
|
|
176
|
+
disable_file_logging: bool = cast(bool, args.disable_file_logging)
|
|
177
|
+
enable_console_logging: bool = cast(bool, args.enable_console_logging)
|
|
114
178
|
allowed_paths: list[str] = (
|
|
115
179
|
cast(list[str], args.allowed_paths) if args.allowed_paths else []
|
|
116
180
|
)
|
|
117
181
|
|
|
182
|
+
# Setup logging
|
|
183
|
+
# Ensure absolutely NO logging when using stdio transport to avoid protocol corruption
|
|
184
|
+
# For sse transport, use file logging by default and console logging only if explicitly requested
|
|
185
|
+
# Only set up logging if not using stdio transport or explicitly requested
|
|
186
|
+
if transport != "stdio" or (enable_console_logging or not disable_file_logging):
|
|
187
|
+
setup_logging(
|
|
188
|
+
log_level=log_level,
|
|
189
|
+
log_to_file=not disable_file_logging and transport != "stdio", # Disable file logging for stdio transport
|
|
190
|
+
log_to_console=enable_console_logging and transport != "stdio", # Only enable console logging if requested AND not using stdio
|
|
191
|
+
transport=transport, # Pass the transport to ensure it's properly handled
|
|
192
|
+
testing="pytest" in sys.modules
|
|
193
|
+
)
|
|
194
|
+
logger.debug(f"Hanzo MCP CLI started with arguments: {args}")
|
|
195
|
+
# No logging setup at all for stdio transport unless explicitly requested
|
|
196
|
+
|
|
197
|
+
|
|
118
198
|
if install:
|
|
119
|
-
install_claude_desktop_config(name, allowed_paths,
|
|
199
|
+
install_claude_desktop_config(name, allowed_paths, host, port)
|
|
120
200
|
return
|
|
121
201
|
|
|
122
202
|
# If no allowed paths are specified, use the user's home directory
|
|
123
203
|
if not allowed_paths:
|
|
124
204
|
allowed_paths = [str(Path.home())]
|
|
205
|
+
logger.info(f"No allowed paths specified, using home directory: {allowed_paths[0]}")
|
|
125
206
|
|
|
126
207
|
# If project directory is specified, add it to allowed paths
|
|
127
208
|
if project_dir and project_dir not in allowed_paths:
|
|
@@ -139,26 +220,48 @@ def main() -> None:
|
|
|
139
220
|
elif allowed_paths:
|
|
140
221
|
project_dir = allowed_paths[0]
|
|
141
222
|
|
|
142
|
-
# Run the server
|
|
143
|
-
|
|
144
|
-
name
|
|
145
|
-
allowed_paths
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
223
|
+
# Run the server - only log if not using stdio transport or logging is explicitly enabled
|
|
224
|
+
if transport != "stdio" or (enable_console_logging or not disable_file_logging):
|
|
225
|
+
logger.info(f"Starting Hanzo MCP server with name: {name}")
|
|
226
|
+
logger.debug(f"Allowed paths: {allowed_paths}")
|
|
227
|
+
logger.debug(f"Project directory: {project_dir}")
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
server = HanzoServer(
|
|
231
|
+
name=name,
|
|
232
|
+
allowed_paths=allowed_paths,
|
|
233
|
+
project_dir=project_dir, # Pass project_dir for initial working directory
|
|
234
|
+
agent_model=agent_model,
|
|
235
|
+
agent_max_tokens=agent_max_tokens,
|
|
236
|
+
agent_api_key=agent_api_key,
|
|
237
|
+
agent_max_iterations=agent_max_iterations,
|
|
238
|
+
agent_max_tool_uses=agent_max_tool_uses,
|
|
239
|
+
enable_agent_tool=enable_agent_tool,
|
|
240
|
+
disable_write_tools=disable_write_tools,
|
|
241
|
+
host=host,
|
|
242
|
+
port=port
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Only log if not using stdio transport or logging is explicitly enabled
|
|
246
|
+
if transport != "stdio" or (enable_console_logging or not disable_file_logging):
|
|
247
|
+
logger.info(f"Server initialized successfully, running with transport: {transport}")
|
|
248
|
+
|
|
249
|
+
# Transport will be automatically cast to Literal['stdio', 'sse'] by the server
|
|
250
|
+
server.run(transport=transport)
|
|
251
|
+
except Exception as e:
|
|
252
|
+
# Only log if not using stdio transport or logging is explicitly enabled
|
|
253
|
+
if transport != "stdio" or (enable_console_logging or not disable_file_logging):
|
|
254
|
+
logger.error(f"Error starting server: {str(e)}")
|
|
255
|
+
logger.exception("Server startup failed with exception:")
|
|
256
|
+
# For stdio transport, we want a clean exception without any logging
|
|
257
|
+
# Re-raise the exception for proper error handling
|
|
258
|
+
raise
|
|
157
259
|
|
|
158
260
|
|
|
159
261
|
def install_claude_desktop_config(
|
|
160
262
|
name: str = "claude-code", allowed_paths: list[str] | None = None,
|
|
161
|
-
disable_write_tools: bool = False
|
|
263
|
+
disable_write_tools: bool = False,
|
|
264
|
+
host: str = "0.0.0.0", port: int = 3001
|
|
162
265
|
) -> None:
|
|
163
266
|
"""Install the server configuration in Claude Desktop.
|
|
164
267
|
|
|
@@ -168,6 +271,8 @@ def install_claude_desktop_config(
|
|
|
168
271
|
disable_write_tools: Whether to disable write/edit tools (file writing, editing, notebook editing)
|
|
169
272
|
to use IDE tools instead. Note: Shell commands can still modify files.
|
|
170
273
|
(default: False)
|
|
274
|
+
host: Host to bind to for SSE transport (default: '0.0.0.0')
|
|
275
|
+
port: Port to use for SSE transport (default: 3001)
|
|
171
276
|
"""
|
|
172
277
|
# Find the Claude Desktop config directory
|
|
173
278
|
home: Path = Path.home()
|
|
@@ -197,11 +302,19 @@ def install_claude_desktop_config(
|
|
|
197
302
|
else:
|
|
198
303
|
# Allow home directory by default
|
|
199
304
|
args.extend(["--allow-path", str(home)])
|
|
200
|
-
|
|
305
|
+
|
|
306
|
+
# Add host and port
|
|
307
|
+
args.extend(["--host", host])
|
|
308
|
+
args.extend(["--port", str(port)])
|
|
309
|
+
|
|
201
310
|
# Add disable_write_tools flag if specified
|
|
202
311
|
if disable_write_tools:
|
|
203
312
|
args.append("--disable-write-tools")
|
|
204
313
|
|
|
314
|
+
# Add host and port
|
|
315
|
+
args.extend(["--host", host])
|
|
316
|
+
args.extend(["--port", str(port)])
|
|
317
|
+
|
|
205
318
|
# Create config object
|
|
206
319
|
config: dict[str, Any] = {
|
|
207
320
|
"mcpServers": {name: {"command": str(script_path), "args": args}}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
"""MCP server implementing Hanzo capabilities.
|
|
1
|
+
"""MCP server implementing Hanzo capabilities.
|
|
2
|
+
|
|
3
|
+
Includes improved error handling and debugging for tool execution.
|
|
4
|
+
"""
|
|
2
5
|
|
|
3
6
|
from typing import Literal, cast, final
|
|
4
7
|
|
|
@@ -13,7 +16,10 @@ from hanzo_mcp.tools.shell.command_executor import CommandExecutor
|
|
|
13
16
|
|
|
14
17
|
@final
|
|
15
18
|
class HanzoServer:
|
|
16
|
-
"""MCP server implementing Hanzo capabilities.
|
|
19
|
+
"""MCP server implementing Hanzo capabilities.
|
|
20
|
+
|
|
21
|
+
Includes improved error handling and debugging for tool execution.
|
|
22
|
+
"""
|
|
17
23
|
|
|
18
24
|
def __init__(
|
|
19
25
|
self,
|
|
@@ -28,6 +34,8 @@ class HanzoServer:
|
|
|
28
34
|
agent_max_tool_uses: int = 30,
|
|
29
35
|
enable_agent_tool: bool = False,
|
|
30
36
|
disable_write_tools: bool = False,
|
|
37
|
+
host: str = "0.0.0.0",
|
|
38
|
+
port: int = 3001,
|
|
31
39
|
):
|
|
32
40
|
"""Initialize the Hanzo server.
|
|
33
41
|
|
|
@@ -43,6 +51,8 @@ class HanzoServer:
|
|
|
43
51
|
agent_max_tool_uses: Maximum number of total tool uses for agent (default: 30)
|
|
44
52
|
enable_agent_tool: Whether to enable the agent tool (default: False)
|
|
45
53
|
disable_write_tools: Whether to disable write/edit tools (default: False)
|
|
54
|
+
host: Host to bind to for SSE transport (default: '0.0.0.0')
|
|
55
|
+
port: Port to use for SSE transport (default: 3001)
|
|
46
56
|
"""
|
|
47
57
|
self.mcp = mcp_instance if mcp_instance is not None else FastMCP(name)
|
|
48
58
|
|
|
@@ -55,7 +65,7 @@ class HanzoServer:
|
|
|
55
65
|
permission_manager=self.permission_manager,
|
|
56
66
|
verbose=False, # Set to True for debugging
|
|
57
67
|
)
|
|
58
|
-
|
|
68
|
+
|
|
59
69
|
# If project_dir is specified, set it as initial working directory for all sessions
|
|
60
70
|
if project_dir:
|
|
61
71
|
initial_session_id = name # Use server name as default session ID
|
|
@@ -83,7 +93,11 @@ class HanzoServer:
|
|
|
83
93
|
self.agent_max_tool_uses = agent_max_tool_uses
|
|
84
94
|
self.enable_agent_tool = enable_agent_tool
|
|
85
95
|
self.disable_write_tools = disable_write_tools
|
|
86
|
-
|
|
96
|
+
|
|
97
|
+
# Store network options
|
|
98
|
+
self.host = host
|
|
99
|
+
self.port = port
|
|
100
|
+
|
|
87
101
|
# Register all tools
|
|
88
102
|
register_all_tools(
|
|
89
103
|
mcp_server=self.mcp,
|
|
@@ -111,6 +125,14 @@ class HanzoServer:
|
|
|
111
125
|
self.permission_manager.add_allowed_path(path)
|
|
112
126
|
self.document_context.add_allowed_path(path)
|
|
113
127
|
|
|
128
|
+
# If using SSE, set the port and host in the environment variables
|
|
129
|
+
if transport == "sse":
|
|
130
|
+
import os
|
|
131
|
+
# Set environment variables for FastMCP settings
|
|
132
|
+
os.environ["FASTMCP_PORT"] = str(self.port)
|
|
133
|
+
os.environ["FASTMCP_HOST"] = self.host
|
|
134
|
+
print(f"Starting SSE server on {self.host}:{self.port}")
|
|
135
|
+
|
|
114
136
|
# Run the server
|
|
115
137
|
transport_type = cast(Literal["stdio", "sse"], transport)
|
|
116
138
|
self.mcp.run(transport=transport_type)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Base model provider for agent delegation.
|
|
2
|
+
|
|
3
|
+
Defines the interface for model providers.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BaseModelProvider(ABC):
|
|
14
|
+
"""Base class for model providers."""
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
async def initialize(self) -> None:
|
|
18
|
+
"""Initialize the provider."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
async def load_model(self, model_name: str, identifier: Optional[str] = None) -> str:
|
|
23
|
+
"""Load a model.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
model_name: The name of the model to load
|
|
27
|
+
identifier: Optional identifier for the model instance
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
The identifier for the loaded model
|
|
31
|
+
"""
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
@abstractmethod
|
|
35
|
+
async def generate(
|
|
36
|
+
self,
|
|
37
|
+
model_id: str,
|
|
38
|
+
prompt: str,
|
|
39
|
+
system_prompt: Optional[str] = None,
|
|
40
|
+
max_tokens: int = 4096,
|
|
41
|
+
temperature: float = 0.7,
|
|
42
|
+
top_p: float = 0.95,
|
|
43
|
+
stop_sequences: Optional[List[str]] = None,
|
|
44
|
+
) -> Tuple[str, Dict[str, Any]]:
|
|
45
|
+
"""Generate a response from the model.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
model_id: The identifier of the model to use
|
|
49
|
+
prompt: The prompt to send to the model
|
|
50
|
+
system_prompt: Optional system prompt to send to the model
|
|
51
|
+
max_tokens: Maximum number of tokens to generate
|
|
52
|
+
temperature: Sampling temperature
|
|
53
|
+
top_p: Top-p sampling parameter
|
|
54
|
+
stop_sequences: Optional list of strings that will stop generation
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
A tuple of (generated text, metadata)
|
|
58
|
+
"""
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
async def unload_model(self, model_id: str) -> None:
|
|
63
|
+
"""Unload a model.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
model_id: The identifier of the model to unload
|
|
67
|
+
"""
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
@abstractmethod
|
|
71
|
+
async def shutdown(self) -> None:
|
|
72
|
+
"""Shutdown the provider."""
|
|
73
|
+
pass
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""LiteLLM provider for agent delegation.
|
|
2
|
+
|
|
3
|
+
Enables the use of various cloud LLM providers via LiteLLM.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import logging
|
|
8
|
+
import json
|
|
9
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
from hanzo_mcp.tools.agent.base_provider import BaseModelProvider
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
# Define model capabilities
|
|
16
|
+
DEFAULT_MAX_TOKENS = 4096
|
|
17
|
+
DEFAULT_CONTEXT_WINDOW = 8192
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LiteLLMProvider(BaseModelProvider):
|
|
21
|
+
"""Provider for cloud models via LiteLLM."""
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
"""Initialize the LiteLLM provider."""
|
|
25
|
+
self.models = {}
|
|
26
|
+
self.initialized = False
|
|
27
|
+
|
|
28
|
+
async def initialize(self) -> None:
|
|
29
|
+
"""Initialize the LiteLLM provider."""
|
|
30
|
+
if self.initialized:
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
# Import LiteLLM
|
|
35
|
+
import litellm
|
|
36
|
+
self.litellm = litellm
|
|
37
|
+
self.initialized = True
|
|
38
|
+
logger.info("LiteLLM provider initialized successfully")
|
|
39
|
+
except ImportError:
|
|
40
|
+
logger.error("Failed to import LiteLLM")
|
|
41
|
+
logger.error("Install LiteLLM with 'pip install litellm'")
|
|
42
|
+
except Exception as e:
|
|
43
|
+
logger.error(f"Failed to initialize LiteLLM provider: {str(e)}")
|
|
44
|
+
|
|
45
|
+
async def load_model(self, model_name: str, identifier: Optional[str] = None
|