hanzo-mcp 0.5.2__py3-none-any.whl → 0.6.1__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.

Files changed (114) hide show
  1. hanzo_mcp/__init__.py +1 -1
  2. hanzo_mcp/cli.py +32 -0
  3. hanzo_mcp/dev_server.py +246 -0
  4. hanzo_mcp/prompts/__init__.py +1 -1
  5. hanzo_mcp/prompts/project_system.py +43 -7
  6. hanzo_mcp/server.py +5 -1
  7. hanzo_mcp/tools/__init__.py +66 -35
  8. hanzo_mcp/tools/agent/__init__.py +1 -1
  9. hanzo_mcp/tools/agent/agent.py +401 -0
  10. hanzo_mcp/tools/agent/agent_tool.py +3 -4
  11. hanzo_mcp/tools/common/__init__.py +1 -1
  12. hanzo_mcp/tools/common/base.py +2 -2
  13. hanzo_mcp/tools/common/batch_tool.py +3 -5
  14. hanzo_mcp/tools/common/config_tool.py +1 -1
  15. hanzo_mcp/tools/common/context.py +1 -1
  16. hanzo_mcp/tools/common/palette.py +344 -0
  17. hanzo_mcp/tools/common/palette_loader.py +108 -0
  18. hanzo_mcp/tools/common/stats.py +1 -1
  19. hanzo_mcp/tools/common/thinking_tool.py +3 -5
  20. hanzo_mcp/tools/common/tool_disable.py +1 -1
  21. hanzo_mcp/tools/common/tool_enable.py +1 -1
  22. hanzo_mcp/tools/common/tool_list.py +49 -52
  23. hanzo_mcp/tools/config/__init__.py +10 -0
  24. hanzo_mcp/tools/config/config_tool.py +212 -0
  25. hanzo_mcp/tools/config/index_config.py +176 -0
  26. hanzo_mcp/tools/config/palette_tool.py +166 -0
  27. hanzo_mcp/tools/database/__init__.py +1 -1
  28. hanzo_mcp/tools/database/graph.py +482 -0
  29. hanzo_mcp/tools/database/graph_add.py +1 -1
  30. hanzo_mcp/tools/database/graph_query.py +1 -1
  31. hanzo_mcp/tools/database/graph_remove.py +1 -1
  32. hanzo_mcp/tools/database/graph_search.py +1 -1
  33. hanzo_mcp/tools/database/graph_stats.py +1 -1
  34. hanzo_mcp/tools/database/sql.py +411 -0
  35. hanzo_mcp/tools/database/sql_query.py +1 -1
  36. hanzo_mcp/tools/database/sql_search.py +1 -1
  37. hanzo_mcp/tools/database/sql_stats.py +1 -1
  38. hanzo_mcp/tools/editor/neovim_command.py +1 -1
  39. hanzo_mcp/tools/editor/neovim_edit.py +1 -1
  40. hanzo_mcp/tools/editor/neovim_session.py +1 -1
  41. hanzo_mcp/tools/filesystem/__init__.py +42 -13
  42. hanzo_mcp/tools/filesystem/base.py +1 -1
  43. hanzo_mcp/tools/filesystem/batch_search.py +4 -4
  44. hanzo_mcp/tools/filesystem/content_replace.py +3 -5
  45. hanzo_mcp/tools/filesystem/diff.py +193 -0
  46. hanzo_mcp/tools/filesystem/directory_tree.py +3 -5
  47. hanzo_mcp/tools/filesystem/edit.py +3 -5
  48. hanzo_mcp/tools/filesystem/find.py +443 -0
  49. hanzo_mcp/tools/filesystem/find_files.py +1 -1
  50. hanzo_mcp/tools/filesystem/git_search.py +1 -1
  51. hanzo_mcp/tools/filesystem/grep.py +2 -2
  52. hanzo_mcp/tools/filesystem/multi_edit.py +3 -5
  53. hanzo_mcp/tools/filesystem/read.py +17 -5
  54. hanzo_mcp/tools/filesystem/{grep_ast_tool.py → symbols.py} +17 -27
  55. hanzo_mcp/tools/filesystem/symbols_unified.py +376 -0
  56. hanzo_mcp/tools/filesystem/tree.py +268 -0
  57. hanzo_mcp/tools/filesystem/unified_search.py +711 -0
  58. hanzo_mcp/tools/filesystem/unix_aliases.py +99 -0
  59. hanzo_mcp/tools/filesystem/watch.py +174 -0
  60. hanzo_mcp/tools/filesystem/write.py +3 -5
  61. hanzo_mcp/tools/jupyter/__init__.py +9 -12
  62. hanzo_mcp/tools/jupyter/base.py +1 -1
  63. hanzo_mcp/tools/jupyter/jupyter.py +326 -0
  64. hanzo_mcp/tools/jupyter/notebook_edit.py +3 -4
  65. hanzo_mcp/tools/jupyter/notebook_read.py +3 -5
  66. hanzo_mcp/tools/llm/__init__.py +4 -0
  67. hanzo_mcp/tools/llm/consensus_tool.py +1 -1
  68. hanzo_mcp/tools/llm/llm_manage.py +1 -1
  69. hanzo_mcp/tools/llm/llm_tool.py +1 -1
  70. hanzo_mcp/tools/llm/llm_unified.py +851 -0
  71. hanzo_mcp/tools/llm/provider_tools.py +1 -1
  72. hanzo_mcp/tools/mcp/__init__.py +4 -0
  73. hanzo_mcp/tools/mcp/mcp_add.py +1 -1
  74. hanzo_mcp/tools/mcp/mcp_remove.py +1 -1
  75. hanzo_mcp/tools/mcp/mcp_stats.py +1 -1
  76. hanzo_mcp/tools/mcp/mcp_unified.py +503 -0
  77. hanzo_mcp/tools/shell/__init__.py +20 -42
  78. hanzo_mcp/tools/shell/base.py +1 -1
  79. hanzo_mcp/tools/shell/base_process.py +303 -0
  80. hanzo_mcp/tools/shell/bash_unified.py +134 -0
  81. hanzo_mcp/tools/shell/logs.py +1 -1
  82. hanzo_mcp/tools/shell/npx.py +1 -1
  83. hanzo_mcp/tools/shell/npx_background.py +1 -1
  84. hanzo_mcp/tools/shell/npx_unified.py +101 -0
  85. hanzo_mcp/tools/shell/open.py +107 -0
  86. hanzo_mcp/tools/shell/pkill.py +1 -1
  87. hanzo_mcp/tools/shell/process_unified.py +131 -0
  88. hanzo_mcp/tools/shell/processes.py +1 -1
  89. hanzo_mcp/tools/shell/run_background.py +1 -1
  90. hanzo_mcp/tools/shell/run_command.py +3 -4
  91. hanzo_mcp/tools/shell/run_command_windows.py +3 -4
  92. hanzo_mcp/tools/shell/uvx.py +1 -1
  93. hanzo_mcp/tools/shell/uvx_background.py +1 -1
  94. hanzo_mcp/tools/shell/uvx_unified.py +101 -0
  95. hanzo_mcp/tools/todo/__init__.py +1 -1
  96. hanzo_mcp/tools/todo/base.py +1 -1
  97. hanzo_mcp/tools/todo/todo.py +265 -0
  98. hanzo_mcp/tools/todo/todo_read.py +3 -5
  99. hanzo_mcp/tools/todo/todo_write.py +3 -5
  100. hanzo_mcp/tools/vector/__init__.py +1 -1
  101. hanzo_mcp/tools/vector/index_tool.py +1 -1
  102. hanzo_mcp/tools/vector/project_manager.py +27 -5
  103. hanzo_mcp/tools/vector/vector.py +311 -0
  104. hanzo_mcp/tools/vector/vector_index.py +1 -1
  105. hanzo_mcp/tools/vector/vector_search.py +1 -1
  106. hanzo_mcp-0.6.1.dist-info/METADATA +336 -0
  107. hanzo_mcp-0.6.1.dist-info/RECORD +134 -0
  108. hanzo_mcp-0.6.1.dist-info/entry_points.txt +3 -0
  109. hanzo_mcp-0.5.2.dist-info/METADATA +0 -276
  110. hanzo_mcp-0.5.2.dist-info/RECORD +0 -106
  111. hanzo_mcp-0.5.2.dist-info/entry_points.txt +0 -2
  112. {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.1.dist-info}/WHEEL +0 -0
  113. {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.1.dist-info}/licenses/LICENSE +0 -0
  114. {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.1.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 fastmcp import FastMCP
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 fastmcp import FastMCP
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 fastmcp import FastMCP
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,