hanzo-mcp 0.5.2__py3-none-any.whl → 0.6.2__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.
Potentially problematic release.
This version of hanzo-mcp might be problematic. Click here for more details.
- hanzo_mcp/__init__.py +1 -1
- hanzo_mcp/cli.py +32 -0
- hanzo_mcp/dev_server.py +246 -0
- hanzo_mcp/prompts/__init__.py +1 -1
- hanzo_mcp/prompts/project_system.py +43 -7
- hanzo_mcp/server.py +5 -1
- hanzo_mcp/tools/__init__.py +66 -35
- hanzo_mcp/tools/agent/__init__.py +1 -1
- hanzo_mcp/tools/agent/agent.py +401 -0
- hanzo_mcp/tools/agent/agent_tool.py +3 -4
- hanzo_mcp/tools/common/__init__.py +1 -1
- hanzo_mcp/tools/common/base.py +2 -2
- hanzo_mcp/tools/common/batch_tool.py +3 -5
- hanzo_mcp/tools/common/config_tool.py +1 -1
- hanzo_mcp/tools/common/context.py +1 -1
- hanzo_mcp/tools/common/palette.py +344 -0
- hanzo_mcp/tools/common/palette_loader.py +108 -0
- hanzo_mcp/tools/common/stats.py +1 -1
- hanzo_mcp/tools/common/thinking_tool.py +3 -5
- hanzo_mcp/tools/common/tool_disable.py +1 -1
- hanzo_mcp/tools/common/tool_enable.py +1 -1
- hanzo_mcp/tools/common/tool_list.py +49 -52
- hanzo_mcp/tools/config/__init__.py +10 -0
- hanzo_mcp/tools/config/config_tool.py +212 -0
- hanzo_mcp/tools/config/index_config.py +176 -0
- hanzo_mcp/tools/config/palette_tool.py +166 -0
- hanzo_mcp/tools/database/__init__.py +1 -1
- hanzo_mcp/tools/database/graph.py +482 -0
- hanzo_mcp/tools/database/graph_add.py +1 -1
- hanzo_mcp/tools/database/graph_query.py +1 -1
- hanzo_mcp/tools/database/graph_remove.py +1 -1
- hanzo_mcp/tools/database/graph_search.py +1 -1
- hanzo_mcp/tools/database/graph_stats.py +1 -1
- hanzo_mcp/tools/database/sql.py +411 -0
- hanzo_mcp/tools/database/sql_query.py +1 -1
- hanzo_mcp/tools/database/sql_search.py +1 -1
- hanzo_mcp/tools/database/sql_stats.py +1 -1
- hanzo_mcp/tools/editor/neovim_command.py +1 -1
- hanzo_mcp/tools/editor/neovim_edit.py +1 -1
- hanzo_mcp/tools/editor/neovim_session.py +1 -1
- hanzo_mcp/tools/filesystem/__init__.py +42 -13
- hanzo_mcp/tools/filesystem/base.py +1 -1
- hanzo_mcp/tools/filesystem/batch_search.py +4 -4
- hanzo_mcp/tools/filesystem/content_replace.py +3 -5
- hanzo_mcp/tools/filesystem/diff.py +193 -0
- hanzo_mcp/tools/filesystem/directory_tree.py +3 -5
- hanzo_mcp/tools/filesystem/edit.py +3 -5
- hanzo_mcp/tools/filesystem/find.py +443 -0
- hanzo_mcp/tools/filesystem/find_files.py +1 -1
- hanzo_mcp/tools/filesystem/git_search.py +1 -1
- hanzo_mcp/tools/filesystem/grep.py +2 -2
- hanzo_mcp/tools/filesystem/multi_edit.py +3 -5
- hanzo_mcp/tools/filesystem/read.py +17 -5
- hanzo_mcp/tools/filesystem/{grep_ast_tool.py → symbols.py} +17 -27
- hanzo_mcp/tools/filesystem/symbols_unified.py +376 -0
- hanzo_mcp/tools/filesystem/tree.py +268 -0
- hanzo_mcp/tools/filesystem/unified_search.py +711 -0
- hanzo_mcp/tools/filesystem/unix_aliases.py +99 -0
- hanzo_mcp/tools/filesystem/watch.py +174 -0
- hanzo_mcp/tools/filesystem/write.py +3 -5
- hanzo_mcp/tools/jupyter/__init__.py +9 -12
- hanzo_mcp/tools/jupyter/base.py +1 -1
- hanzo_mcp/tools/jupyter/jupyter.py +326 -0
- hanzo_mcp/tools/jupyter/notebook_edit.py +3 -4
- hanzo_mcp/tools/jupyter/notebook_read.py +3 -5
- hanzo_mcp/tools/llm/__init__.py +4 -0
- hanzo_mcp/tools/llm/consensus_tool.py +1 -1
- hanzo_mcp/tools/llm/llm_manage.py +1 -1
- hanzo_mcp/tools/llm/llm_tool.py +1 -1
- hanzo_mcp/tools/llm/llm_unified.py +851 -0
- hanzo_mcp/tools/llm/provider_tools.py +1 -1
- hanzo_mcp/tools/mcp/__init__.py +4 -0
- hanzo_mcp/tools/mcp/mcp_add.py +1 -1
- hanzo_mcp/tools/mcp/mcp_remove.py +1 -1
- hanzo_mcp/tools/mcp/mcp_stats.py +1 -1
- hanzo_mcp/tools/mcp/mcp_unified.py +503 -0
- hanzo_mcp/tools/shell/__init__.py +20 -42
- hanzo_mcp/tools/shell/base.py +1 -1
- hanzo_mcp/tools/shell/base_process.py +303 -0
- hanzo_mcp/tools/shell/bash_unified.py +134 -0
- hanzo_mcp/tools/shell/logs.py +1 -1
- hanzo_mcp/tools/shell/npx.py +1 -1
- hanzo_mcp/tools/shell/npx_background.py +1 -1
- hanzo_mcp/tools/shell/npx_unified.py +101 -0
- hanzo_mcp/tools/shell/open.py +107 -0
- hanzo_mcp/tools/shell/pkill.py +1 -1
- hanzo_mcp/tools/shell/process_unified.py +131 -0
- hanzo_mcp/tools/shell/processes.py +1 -1
- hanzo_mcp/tools/shell/run_background.py +1 -1
- hanzo_mcp/tools/shell/run_command.py +3 -4
- hanzo_mcp/tools/shell/run_command_windows.py +3 -4
- hanzo_mcp/tools/shell/uvx.py +1 -1
- hanzo_mcp/tools/shell/uvx_background.py +1 -1
- hanzo_mcp/tools/shell/uvx_unified.py +101 -0
- hanzo_mcp/tools/todo/__init__.py +1 -1
- hanzo_mcp/tools/todo/base.py +1 -1
- hanzo_mcp/tools/todo/todo.py +265 -0
- hanzo_mcp/tools/todo/todo_read.py +3 -5
- hanzo_mcp/tools/todo/todo_write.py +3 -5
- hanzo_mcp/tools/vector/__init__.py +1 -1
- hanzo_mcp/tools/vector/index_tool.py +1 -1
- hanzo_mcp/tools/vector/project_manager.py +27 -5
- hanzo_mcp/tools/vector/vector.py +311 -0
- hanzo_mcp/tools/vector/vector_index.py +1 -1
- hanzo_mcp/tools/vector/vector_search.py +1 -1
- hanzo_mcp-0.6.2.dist-info/METADATA +336 -0
- hanzo_mcp-0.6.2.dist-info/RECORD +134 -0
- hanzo_mcp-0.6.2.dist-info/entry_points.txt +3 -0
- hanzo_mcp-0.5.2.dist-info/METADATA +0 -276
- hanzo_mcp-0.5.2.dist-info/RECORD +0 -106
- hanzo_mcp-0.5.2.dist-info/entry_points.txt +0 -2
- {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.dist-info}/licenses/LICENSE +0 -0
- {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.dist-info}/top_level.txt +0 -0
|
@@ -7,9 +7,8 @@ import fnmatch
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import Annotated, TypedDict, Unpack, final, override
|
|
9
9
|
|
|
10
|
-
from fastmcp import Context as MCPContext
|
|
11
|
-
from
|
|
12
|
-
from fastmcp.server.dependencies import get_context
|
|
10
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
11
|
+
from mcp.server import FastMCP
|
|
13
12
|
from pydantic import Field
|
|
14
13
|
|
|
15
14
|
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
|
@@ -276,14 +275,13 @@ Only works within allowed directories."""
|
|
|
276
275
|
|
|
277
276
|
@mcp_server.tool(name=self.name, description=self.description)
|
|
278
277
|
async def content_replace(
|
|
279
|
-
ctx: MCPContext,
|
|
280
278
|
pattern: Pattern,
|
|
281
279
|
replacement: Replacement,
|
|
282
280
|
path: SearchPath,
|
|
283
281
|
file_pattern: FilePattern,
|
|
284
282
|
dry_run: DryRun,
|
|
283
|
+
ctx: MCPContext
|
|
285
284
|
) -> str:
|
|
286
|
-
ctx = get_context()
|
|
287
285
|
return await tool_self.call(
|
|
288
286
|
ctx,
|
|
289
287
|
pattern=pattern,
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Diff tool for comparing files."""
|
|
2
|
+
|
|
3
|
+
import difflib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional, override
|
|
6
|
+
|
|
7
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
8
|
+
|
|
9
|
+
from hanzo_mcp.tools.common.base import BaseTool
|
|
10
|
+
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
11
|
+
from mcp.server import FastMCP
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DiffTool(BaseTool):
|
|
15
|
+
"""Tool for comparing files and showing differences."""
|
|
16
|
+
|
|
17
|
+
name = "diff"
|
|
18
|
+
|
|
19
|
+
def __init__(self, permission_manager: PermissionManager):
|
|
20
|
+
"""Initialize the diff tool.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
permission_manager: Permission manager for access control
|
|
24
|
+
"""
|
|
25
|
+
super().__init__()
|
|
26
|
+
self.permission_manager = permission_manager
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
@override
|
|
30
|
+
def description(self) -> str:
|
|
31
|
+
"""Get the tool description."""
|
|
32
|
+
return """Compare files and show differences. Supports unified and context diff formats.
|
|
33
|
+
|
|
34
|
+
Usage:
|
|
35
|
+
diff file1.py file2.py
|
|
36
|
+
diff old_version.js new_version.js --context 5
|
|
37
|
+
diff before.txt after.txt --unified
|
|
38
|
+
diff a.json b.json --ignore-whitespace"""
|
|
39
|
+
|
|
40
|
+
@override
|
|
41
|
+
async def run(
|
|
42
|
+
self,
|
|
43
|
+
ctx: MCPContext,
|
|
44
|
+
file1: str,
|
|
45
|
+
file2: str,
|
|
46
|
+
unified: bool = True,
|
|
47
|
+
context: int = 3,
|
|
48
|
+
ignore_whitespace: bool = False,
|
|
49
|
+
show_line_numbers: bool = True,
|
|
50
|
+
) -> str:
|
|
51
|
+
"""Compare two files and show differences.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
ctx: MCP context
|
|
55
|
+
file1: Path to first file
|
|
56
|
+
file2: Path to second file
|
|
57
|
+
unified: Use unified diff format (default: True)
|
|
58
|
+
context: Number of context lines to show (default: 3)
|
|
59
|
+
ignore_whitespace: Ignore whitespace differences (default: False)
|
|
60
|
+
show_line_numbers: Show line numbers in output (default: True)
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Diff output showing differences between files
|
|
64
|
+
"""
|
|
65
|
+
# Resolve file paths
|
|
66
|
+
path1 = Path(file1).expanduser().resolve()
|
|
67
|
+
path2 = Path(file2).expanduser().resolve()
|
|
68
|
+
|
|
69
|
+
# Check permissions
|
|
70
|
+
if not self.permission_manager.is_path_allowed(str(path1)):
|
|
71
|
+
raise PermissionError(f"Access denied to path: {path1}")
|
|
72
|
+
if not self.permission_manager.is_path_allowed(str(path2)):
|
|
73
|
+
raise PermissionError(f"Access denied to path: {path2}")
|
|
74
|
+
|
|
75
|
+
# Check if files exist
|
|
76
|
+
if not path1.exists():
|
|
77
|
+
raise ValueError(f"File not found: {path1}")
|
|
78
|
+
if not path2.exists():
|
|
79
|
+
raise ValueError(f"File not found: {path2}")
|
|
80
|
+
|
|
81
|
+
# Read file contents
|
|
82
|
+
try:
|
|
83
|
+
with open(path1, 'r', encoding='utf-8') as f:
|
|
84
|
+
lines1 = f.readlines()
|
|
85
|
+
except Exception as e:
|
|
86
|
+
raise RuntimeError(f"Error reading {path1}: {e}")
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
with open(path2, 'r', encoding='utf-8') as f:
|
|
90
|
+
lines2 = f.readlines()
|
|
91
|
+
except Exception as e:
|
|
92
|
+
raise RuntimeError(f"Error reading {path2}: {e}")
|
|
93
|
+
|
|
94
|
+
# Optionally normalize whitespace
|
|
95
|
+
if ignore_whitespace:
|
|
96
|
+
lines1 = [line.strip() + '\n' for line in lines1]
|
|
97
|
+
lines2 = [line.strip() + '\n' for line in lines2]
|
|
98
|
+
|
|
99
|
+
# Generate diff
|
|
100
|
+
if unified:
|
|
101
|
+
# Unified diff format
|
|
102
|
+
diff_lines = list(difflib.unified_diff(
|
|
103
|
+
lines1,
|
|
104
|
+
lines2,
|
|
105
|
+
fromfile=str(path1),
|
|
106
|
+
tofile=str(path2),
|
|
107
|
+
n=context,
|
|
108
|
+
lineterm=''
|
|
109
|
+
))
|
|
110
|
+
else:
|
|
111
|
+
# Context diff format
|
|
112
|
+
diff_lines = list(difflib.context_diff(
|
|
113
|
+
lines1,
|
|
114
|
+
lines2,
|
|
115
|
+
fromfile=str(path1),
|
|
116
|
+
tofile=str(path2),
|
|
117
|
+
n=context,
|
|
118
|
+
lineterm=''
|
|
119
|
+
))
|
|
120
|
+
|
|
121
|
+
if not diff_lines:
|
|
122
|
+
return f"Files {path1.name} and {path2.name} are identical"
|
|
123
|
+
|
|
124
|
+
# Format output
|
|
125
|
+
output = []
|
|
126
|
+
|
|
127
|
+
# Add header
|
|
128
|
+
output.append(f"Comparing: {path1.name} vs {path2.name}")
|
|
129
|
+
output.append("=" * 60)
|
|
130
|
+
|
|
131
|
+
# Add diff with optional line numbers
|
|
132
|
+
if show_line_numbers and unified:
|
|
133
|
+
# Parse unified diff to add line numbers
|
|
134
|
+
current_line1 = 0
|
|
135
|
+
current_line2 = 0
|
|
136
|
+
|
|
137
|
+
for line in diff_lines:
|
|
138
|
+
if line.startswith('@@'):
|
|
139
|
+
# Parse hunk header
|
|
140
|
+
parts = line.split()
|
|
141
|
+
if len(parts) >= 3:
|
|
142
|
+
# Extract line numbers
|
|
143
|
+
old_info = parts[1].strip('-').split(',')
|
|
144
|
+
new_info = parts[2].strip('+').split(',')
|
|
145
|
+
current_line1 = int(old_info[0]) - 1
|
|
146
|
+
current_line2 = int(new_info[0]) - 1
|
|
147
|
+
output.append(line)
|
|
148
|
+
elif line.startswith('-'):
|
|
149
|
+
current_line1 += 1
|
|
150
|
+
output.append(f"{current_line1:4d}- {line[1:]}")
|
|
151
|
+
elif line.startswith('+'):
|
|
152
|
+
current_line2 += 1
|
|
153
|
+
output.append(f"{current_line2:4d}+ {line[1:]}")
|
|
154
|
+
elif line.startswith(' '):
|
|
155
|
+
current_line1 += 1
|
|
156
|
+
current_line2 += 1
|
|
157
|
+
output.append(f"{current_line1:4d} {line[1:]}")
|
|
158
|
+
else:
|
|
159
|
+
output.append(line)
|
|
160
|
+
else:
|
|
161
|
+
# Standard diff output
|
|
162
|
+
output.extend(diff_lines)
|
|
163
|
+
|
|
164
|
+
# Add summary
|
|
165
|
+
additions = sum(1 for line in diff_lines if line.startswith('+') and not line.startswith('+++'))
|
|
166
|
+
deletions = sum(1 for line in diff_lines if line.startswith('-') and not line.startswith('---'))
|
|
167
|
+
|
|
168
|
+
output.append("")
|
|
169
|
+
output.append("=" * 60)
|
|
170
|
+
output.append(f"Summary: {additions} additions, {deletions} deletions")
|
|
171
|
+
|
|
172
|
+
return '\n'.join(output)
|
|
173
|
+
|
|
174
|
+
def register(self, server: FastMCP) -> None:
|
|
175
|
+
"""Register the tool with the MCP server."""
|
|
176
|
+
server.tool(name=self.name, description=self.description)(self.call)
|
|
177
|
+
|
|
178
|
+
async def call(self, **kwargs) -> str:
|
|
179
|
+
"""Call the tool with arguments."""
|
|
180
|
+
return await self.run(None, **kwargs)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# Create tool instance (requires permission manager to be set)
|
|
184
|
+
def create_diff_tool(permission_manager: PermissionManager) -> DiffTool:
|
|
185
|
+
"""Create a diff tool instance.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
permission_manager: Permission manager for access control
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Configured diff tool instance
|
|
192
|
+
"""
|
|
193
|
+
return DiffTool(permission_manager)
|
|
@@ -6,9 +6,8 @@ This module provides the DirectoryTreeTool for viewing file and directory struct
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Annotated, Any, TypedDict, Unpack, final, override
|
|
8
8
|
|
|
9
|
-
from fastmcp import Context as MCPContext
|
|
10
|
-
from
|
|
11
|
-
from fastmcp.server.dependencies import get_context
|
|
9
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
10
|
+
from mcp.server import FastMCP
|
|
12
11
|
from pydantic import Field
|
|
13
12
|
|
|
14
13
|
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
|
@@ -299,12 +298,11 @@ requested. Only works within allowed directories."""
|
|
|
299
298
|
|
|
300
299
|
@mcp_server.tool(name=self.name, description=self.description)
|
|
301
300
|
async def directory_tree(
|
|
302
|
-
ctx: MCPContext,
|
|
303
301
|
path: DirectoryPath,
|
|
304
302
|
depth: Depth,
|
|
305
303
|
include_filtered: IncludeFiltered,
|
|
304
|
+
ctx: MCPContext
|
|
306
305
|
) -> str:
|
|
307
|
-
ctx = get_context()
|
|
308
306
|
return await tool_self.call(
|
|
309
307
|
ctx, path=path, depth=depth, include_filtered=include_filtered
|
|
310
308
|
)
|
|
@@ -7,9 +7,8 @@ from difflib import unified_diff
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import Annotated, TypedDict, Unpack, final, override
|
|
9
9
|
|
|
10
|
-
from fastmcp import Context as MCPContext
|
|
11
|
-
from
|
|
12
|
-
from fastmcp.server.dependencies import get_context
|
|
10
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
11
|
+
from mcp.server import FastMCP
|
|
13
12
|
from pydantic import Field
|
|
14
13
|
|
|
15
14
|
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
|
@@ -263,13 +262,12 @@ Usage:
|
|
|
263
262
|
|
|
264
263
|
@mcp_server.tool(name=self.name, description=self.description)
|
|
265
264
|
async def edit(
|
|
266
|
-
ctx: MCPContext,
|
|
267
265
|
file_path: FilePath,
|
|
268
266
|
old_string: OldString,
|
|
269
267
|
new_string: NewString,
|
|
270
268
|
expected_replacements: ExpectedReplacements,
|
|
269
|
+
ctx: MCPContext
|
|
271
270
|
) -> str:
|
|
272
|
-
ctx = get_context()
|
|
273
271
|
return await tool_self.call(
|
|
274
272
|
ctx,
|
|
275
273
|
file_path=file_path,
|