hanzo-mcp 0.6.13__py3-none-any.whl → 0.7.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 (62) hide show
  1. hanzo_mcp/analytics/__init__.py +5 -0
  2. hanzo_mcp/analytics/posthog_analytics.py +364 -0
  3. hanzo_mcp/cli.py +3 -3
  4. hanzo_mcp/cli_enhanced.py +3 -3
  5. hanzo_mcp/config/settings.py +1 -1
  6. hanzo_mcp/config/tool_config.py +18 -4
  7. hanzo_mcp/server.py +34 -1
  8. hanzo_mcp/tools/__init__.py +65 -2
  9. hanzo_mcp/tools/agent/__init__.py +84 -3
  10. hanzo_mcp/tools/agent/agent_tool.py +102 -4
  11. hanzo_mcp/tools/agent/agent_tool_v2.py +492 -0
  12. hanzo_mcp/tools/agent/clarification_protocol.py +220 -0
  13. hanzo_mcp/tools/agent/clarification_tool.py +68 -0
  14. hanzo_mcp/tools/agent/claude_cli_tool.py +125 -0
  15. hanzo_mcp/tools/agent/claude_desktop_auth.py +508 -0
  16. hanzo_mcp/tools/agent/cli_agent_base.py +191 -0
  17. hanzo_mcp/tools/agent/code_auth.py +436 -0
  18. hanzo_mcp/tools/agent/code_auth_tool.py +194 -0
  19. hanzo_mcp/tools/agent/codex_cli_tool.py +123 -0
  20. hanzo_mcp/tools/agent/critic_tool.py +376 -0
  21. hanzo_mcp/tools/agent/gemini_cli_tool.py +128 -0
  22. hanzo_mcp/tools/agent/grok_cli_tool.py +128 -0
  23. hanzo_mcp/tools/agent/iching_tool.py +380 -0
  24. hanzo_mcp/tools/agent/network_tool.py +273 -0
  25. hanzo_mcp/tools/agent/prompt.py +62 -20
  26. hanzo_mcp/tools/agent/review_tool.py +433 -0
  27. hanzo_mcp/tools/agent/swarm_tool.py +535 -0
  28. hanzo_mcp/tools/agent/swarm_tool_v2.py +654 -0
  29. hanzo_mcp/tools/common/base.py +1 -0
  30. hanzo_mcp/tools/common/batch_tool.py +102 -10
  31. hanzo_mcp/tools/common/fastmcp_pagination.py +369 -0
  32. hanzo_mcp/tools/common/forgiving_edit.py +243 -0
  33. hanzo_mcp/tools/common/paginated_base.py +230 -0
  34. hanzo_mcp/tools/common/paginated_response.py +307 -0
  35. hanzo_mcp/tools/common/pagination.py +226 -0
  36. hanzo_mcp/tools/common/tool_list.py +3 -0
  37. hanzo_mcp/tools/common/truncate.py +101 -0
  38. hanzo_mcp/tools/filesystem/__init__.py +29 -0
  39. hanzo_mcp/tools/filesystem/ast_multi_edit.py +562 -0
  40. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +338 -0
  41. hanzo_mcp/tools/lsp/__init__.py +5 -0
  42. hanzo_mcp/tools/lsp/lsp_tool.py +512 -0
  43. hanzo_mcp/tools/memory/__init__.py +76 -0
  44. hanzo_mcp/tools/memory/knowledge_tools.py +518 -0
  45. hanzo_mcp/tools/memory/memory_tools.py +456 -0
  46. hanzo_mcp/tools/search/__init__.py +6 -0
  47. hanzo_mcp/tools/search/find_tool.py +581 -0
  48. hanzo_mcp/tools/search/unified_search.py +953 -0
  49. hanzo_mcp/tools/shell/__init__.py +5 -0
  50. hanzo_mcp/tools/shell/auto_background.py +203 -0
  51. hanzo_mcp/tools/shell/base_process.py +53 -27
  52. hanzo_mcp/tools/shell/bash_tool.py +17 -33
  53. hanzo_mcp/tools/shell/npx_tool.py +15 -32
  54. hanzo_mcp/tools/shell/streaming_command.py +594 -0
  55. hanzo_mcp/tools/shell/uvx_tool.py +15 -32
  56. hanzo_mcp/types.py +23 -0
  57. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/METADATA +229 -71
  58. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/RECORD +61 -24
  59. hanzo_mcp-0.6.13.dist-info/licenses/LICENSE +0 -21
  60. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/WHEEL +0 -0
  61. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/entry_points.txt +0 -0
  62. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,433 @@
1
+ """Review tool for agents to request balanced code review from main loop."""
2
+
3
+ import json
4
+ from typing import Any, Dict, List, Optional, override
5
+ from enum import Enum
6
+
7
+ from hanzo_mcp.tools.common.base import BaseTool
8
+ from mcp.server.fastmcp import Context as MCPContext
9
+ from mcp.server import FastMCP
10
+
11
+
12
+ class ReviewFocus(Enum):
13
+ """Types of review focus areas."""
14
+ GENERAL = "general"
15
+ FUNCTIONALITY = "functionality"
16
+ READABILITY = "readability"
17
+ MAINTAINABILITY = "maintainability"
18
+ TESTING = "testing"
19
+ DOCUMENTATION = "documentation"
20
+ ARCHITECTURE = "architecture"
21
+
22
+
23
+ class ReviewTool(BaseTool):
24
+ """Tool for agents to request balanced code review from the main loop."""
25
+
26
+ name = "review"
27
+
28
+ @property
29
+ @override
30
+ def description(self) -> str:
31
+ """Get the tool description."""
32
+ return """Request a balanced, constructive code review from the main loop.
33
+
34
+ Unlike the critic tool (which plays devil's advocate), this provides:
35
+ - Objective assessment of code quality
36
+ - Recognition of what's done well
37
+ - Constructive suggestions for improvement
38
+ - Focus on practical concerns
39
+ - No predetermined bias or harsh judgment
40
+
41
+ Parameters:
42
+ - focus: Review focus area (GENERAL, FUNCTIONALITY, READABILITY, MAINTAINABILITY, TESTING, DOCUMENTATION, ARCHITECTURE)
43
+ - work_description: Clear description of what you've implemented
44
+ - code_snippets: Optional code snippets to review (as a list of strings)
45
+ - file_paths: Optional list of file paths you've modified
46
+ - context: Optional additional context about the implementation
47
+
48
+ The review will be balanced, highlighting both strengths and areas for improvement.
49
+
50
+ Example:
51
+ review(
52
+ focus="FUNCTIONALITY",
53
+ work_description="Implemented auto-import feature for Go files",
54
+ code_snippets=["func AddImport(file string, importPath string) error { ... }"],
55
+ file_paths=["/path/to/import_handler.go"],
56
+ context="This will be used to automatically fix missing imports in Go files"
57
+ )"""
58
+
59
+ async def call(
60
+ self,
61
+ ctx: MCPContext,
62
+ focus: str,
63
+ work_description: str,
64
+ code_snippets: Optional[List[str]] = None,
65
+ file_paths: Optional[List[str]] = None,
66
+ context: Optional[str] = None
67
+ ) -> str:
68
+ """This is a placeholder - actual implementation happens in AgentTool."""
69
+ # This tool is handled specially in the agent execution
70
+ return f"Review requested for: {work_description}"
71
+
72
+ def register(self, server: FastMCP) -> None:
73
+ """Register the tool with the MCP server."""
74
+ tool_self = self
75
+
76
+ @server.tool(name=self.name, description=self.description)
77
+ async def review(
78
+ ctx: MCPContext,
79
+ focus: str,
80
+ work_description: str,
81
+ code_snippets: Optional[List[str]] = None,
82
+ file_paths: Optional[List[str]] = None,
83
+ context: Optional[str] = None
84
+ ) -> str:
85
+ return await tool_self.call(
86
+ ctx,
87
+ focus,
88
+ work_description,
89
+ code_snippets,
90
+ file_paths,
91
+ context
92
+ )
93
+
94
+
95
+ class BalancedReviewer:
96
+ """Provides balanced, constructive code reviews."""
97
+
98
+ def __init__(self):
99
+ self.review_handlers = {
100
+ ReviewFocus.GENERAL: self._review_general,
101
+ ReviewFocus.FUNCTIONALITY: self._review_functionality,
102
+ ReviewFocus.READABILITY: self._review_readability,
103
+ ReviewFocus.MAINTAINABILITY: self._review_maintainability,
104
+ ReviewFocus.TESTING: self._review_testing,
105
+ ReviewFocus.DOCUMENTATION: self._review_documentation,
106
+ ReviewFocus.ARCHITECTURE: self._review_architecture,
107
+ }
108
+
109
+ def review(
110
+ self,
111
+ focus: ReviewFocus,
112
+ work_description: str,
113
+ code_snippets: Optional[List[str]] = None,
114
+ file_paths: Optional[List[str]] = None,
115
+ context: Optional[str] = None
116
+ ) -> str:
117
+ """Perform a balanced code review."""
118
+ review_func = self.review_handlers.get(focus, self._review_general)
119
+ return review_func(work_description, code_snippets, file_paths, context)
120
+
121
+ def _review_general(
122
+ self,
123
+ work_description: str,
124
+ code_snippets: Optional[List[str]],
125
+ file_paths: Optional[List[str]],
126
+ context: Optional[str]
127
+ ) -> str:
128
+ """Provide a general balanced review."""
129
+ response = "📋 GENERAL CODE REVIEW:\n\n"
130
+ response += f"**Work Reviewed:** {work_description}\n\n"
131
+
132
+ # Positive observations
133
+ response += "**Positive Aspects:**\n"
134
+ if "fix" in work_description.lower():
135
+ response += "✓ Addressing identified issues proactively\n"
136
+ if "implement" in work_description.lower():
137
+ response += "✓ Adding new functionality to enhance the system\n"
138
+ if code_snippets:
139
+ response += "✓ Code structure appears organized\n"
140
+ if file_paths and len(file_paths) == 1:
141
+ response += "✓ Focused changes in a single file (good for reviewability)\n"
142
+ elif file_paths and len(file_paths) > 1:
143
+ response += "✓ Comprehensive approach across multiple files\n"
144
+
145
+ # Constructive suggestions
146
+ response += "\n**Suggestions for Consideration:**\n"
147
+ response += "• Ensure all edge cases are handled appropriately\n"
148
+ response += "• Consider adding unit tests if not already present\n"
149
+ response += "• Verify the changes integrate well with existing code\n"
150
+
151
+ if context:
152
+ response += f"\n**Context Consideration:**\n{context}\n"
153
+ response += "→ This context helps understand the implementation choices.\n"
154
+
155
+ # Summary
156
+ response += "\n**Summary:**\n"
157
+ response += "The implementation appears sound. Consider the suggestions above to further strengthen the code."
158
+
159
+ return response
160
+
161
+ def _review_functionality(
162
+ self,
163
+ work_description: str,
164
+ code_snippets: Optional[List[str]],
165
+ file_paths: Optional[List[str]],
166
+ context: Optional[str]
167
+ ) -> str:
168
+ """Review functionality aspects."""
169
+ response = "📋 FUNCTIONALITY REVIEW:\n\n"
170
+ response += f"**Implementation:** {work_description}\n\n"
171
+
172
+ response += "**Functional Assessment:**\n"
173
+
174
+ # Analyze code snippets if provided
175
+ if code_snippets:
176
+ for i, snippet in enumerate(code_snippets, 1):
177
+ response += f"\nCode Snippet {i}:\n"
178
+
179
+ # Check for function definitions
180
+ if "func " in snippet or "def " in snippet or "function " in snippet:
181
+ response += "✓ Function definition looks properly structured\n"
182
+
183
+ # Check for error handling
184
+ if "error" in snippet or "err" in snippet or "try" in snippet:
185
+ response += "✓ Error handling is present\n"
186
+ elif "return" in snippet:
187
+ response += "• Consider adding error handling if applicable\n"
188
+
189
+ # Check for input validation
190
+ if "if " in snippet or "check" in snippet.lower():
191
+ response += "✓ Input validation appears to be present\n"
192
+
193
+ response += "\n**Functional Considerations:**\n"
194
+ response += "• Does the implementation handle all expected inputs?\n"
195
+ response += "• Are return values meaningful and consistent?\n"
196
+ response += "• Is the functionality easily testable?\n"
197
+ response += "• Does it integrate well with existing features?\n"
198
+
199
+ response += "\n**Overall:** The functionality appears to meet the described requirements."
200
+
201
+ return response
202
+
203
+ def _review_readability(
204
+ self,
205
+ work_description: str,
206
+ code_snippets: Optional[List[str]],
207
+ file_paths: Optional[List[str]],
208
+ context: Optional[str]
209
+ ) -> str:
210
+ """Review code readability."""
211
+ response = "📋 READABILITY REVIEW:\n\n"
212
+
213
+ response += "**Readability Factors:**\n"
214
+
215
+ if code_snippets:
216
+ total_lines = sum(snippet.count('\n') + 1 for snippet in code_snippets)
217
+ avg_line_length = sum(len(line) for snippet in code_snippets for line in snippet.split('\n')) / max(total_lines, 1)
218
+
219
+ if avg_line_length < 80:
220
+ response += "✓ Line lengths are reasonable\n"
221
+ else:
222
+ response += "• Some lines might be too long, consider breaking them up\n"
223
+
224
+ # Check naming
225
+ has_good_names = any(
226
+ any(word in snippet for word in ['Add', 'Get', 'Set', 'Create', 'Update', 'Delete'])
227
+ for snippet in code_snippets
228
+ )
229
+ if has_good_names:
230
+ response += "✓ Function/method names appear descriptive\n"
231
+
232
+ response += "\n**Readability Suggestions:**\n"
233
+ response += "• Use meaningful variable and function names\n"
234
+ response += "• Keep functions focused on a single responsibility\n"
235
+ response += "• Add comments for complex logic sections\n"
236
+ response += "• Maintain consistent indentation and formatting\n"
237
+
238
+ response += "\n**Overall:** Code readability appears acceptable with room for minor improvements."
239
+
240
+ return response
241
+
242
+ def _review_maintainability(
243
+ self,
244
+ work_description: str,
245
+ code_snippets: Optional[List[str]],
246
+ file_paths: Optional[List[str]],
247
+ context: Optional[str]
248
+ ) -> str:
249
+ """Review maintainability aspects."""
250
+ response = "📋 MAINTAINABILITY REVIEW:\n\n"
251
+
252
+ response += "**Maintainability Factors:**\n"
253
+
254
+ # Check file organization
255
+ if file_paths:
256
+ if len(file_paths) == 1:
257
+ response += "✓ Changes are localized to a single file\n"
258
+ else:
259
+ response += "✓ Changes are logically distributed across files\n"
260
+
261
+ # Check for modularity in code
262
+ if code_snippets:
263
+ function_count = sum(
264
+ snippet.count('func ') + snippet.count('def ') + snippet.count('function ')
265
+ for snippet in code_snippets
266
+ )
267
+ if function_count > 0:
268
+ response += "✓ Code is broken into functions/methods\n"
269
+
270
+ response += "\n**Maintainability Considerations:**\n"
271
+ response += "• Is the code modular and reusable?\n"
272
+ response += "• Are dependencies clearly defined?\n"
273
+ response += "• Will future developers understand the intent?\n"
274
+ response += "• Is the code structured to allow easy updates?\n"
275
+
276
+ response += "\n**Recommendations:**\n"
277
+ response += "• Consider extracting common patterns into utilities\n"
278
+ response += "• Ensure consistent patterns across the codebase\n"
279
+ response += "• Document any non-obvious design decisions\n"
280
+
281
+ return response
282
+
283
+ def _review_testing(
284
+ self,
285
+ work_description: str,
286
+ code_snippets: Optional[List[str]],
287
+ file_paths: Optional[List[str]],
288
+ context: Optional[str]
289
+ ) -> str:
290
+ """Review testing aspects."""
291
+ response = "📋 TESTING REVIEW:\n\n"
292
+
293
+ has_test_files = any("test" in str(path).lower() for path in (file_paths or []))
294
+
295
+ if has_test_files:
296
+ response += "✓ Test files are included with the changes\n\n"
297
+ else:
298
+ response += "⚠️ No test files detected in the changes\n\n"
299
+
300
+ response += "**Testing Checklist:**\n"
301
+ response += "□ Unit tests for new functions\n"
302
+ response += "□ Integration tests for feature interactions\n"
303
+ response += "□ Edge case coverage\n"
304
+ response += "□ Error condition testing\n"
305
+ response += "□ Performance tests (if applicable)\n"
306
+
307
+ response += "\n**Testing Recommendations:**\n"
308
+ response += "• Write tests that document expected behavior\n"
309
+ response += "• Include both positive and negative test cases\n"
310
+ response += "• Ensure tests are maintainable and clear\n"
311
+ response += "• Aim for good coverage of critical paths\n"
312
+
313
+ if not has_test_files:
314
+ response += "\n💡 Consider adding tests to ensure reliability and prevent regressions."
315
+
316
+ return response
317
+
318
+ def _review_documentation(
319
+ self,
320
+ work_description: str,
321
+ code_snippets: Optional[List[str]],
322
+ file_paths: Optional[List[str]],
323
+ context: Optional[str]
324
+ ) -> str:
325
+ """Review documentation aspects."""
326
+ response = "📋 DOCUMENTATION REVIEW:\n\n"
327
+
328
+ # Check for documentation in code
329
+ has_comments = False
330
+ if code_snippets:
331
+ has_comments = any(
332
+ '//' in snippet or '/*' in snippet or '#' in snippet or '"""' in snippet
333
+ for snippet in code_snippets
334
+ )
335
+
336
+ if has_comments:
337
+ response += "✓ Code includes some documentation\n"
338
+ else:
339
+ response += "• Consider adding documentation comments\n"
340
+
341
+ response += "\n**Documentation Guidelines:**\n"
342
+ response += "• Document the 'why' not just the 'what'\n"
343
+ response += "• Include examples for complex functions\n"
344
+ response += "• Document any assumptions or limitations\n"
345
+ response += "• Keep documentation up-to-date with code changes\n"
346
+
347
+ response += "\n**Recommended Documentation:**\n"
348
+ response += "• Function/method purpose and parameters\n"
349
+ response += "• Complex algorithm explanations\n"
350
+ response += "• API usage examples\n"
351
+ response += "• Configuration requirements\n"
352
+
353
+ return response
354
+
355
+ def _review_architecture(
356
+ self,
357
+ work_description: str,
358
+ code_snippets: Optional[List[str]],
359
+ file_paths: Optional[List[str]],
360
+ context: Optional[str]
361
+ ) -> str:
362
+ """Review architectural aspects."""
363
+ response = "📋 ARCHITECTURE REVIEW:\n\n"
364
+
365
+ response += "**Architectural Considerations:**\n"
366
+
367
+ # Analyze file structure
368
+ if file_paths:
369
+ # Check for separation of concerns
370
+ has_separation = len(set(str(p).split('/')[-2] for p in file_paths if '/' in str(p))) > 1
371
+ if has_separation:
372
+ response += "✓ Changes span multiple modules (good separation)\n"
373
+ else:
374
+ response += "✓ Changes are cohesive within a module\n"
375
+
376
+ response += "\n**Architectural Principles:**\n"
377
+ response += "• Single Responsibility - Each component has one clear purpose\n"
378
+ response += "• Open/Closed - Open for extension, closed for modification\n"
379
+ response += "• Dependency Inversion - Depend on abstractions, not concretions\n"
380
+ response += "• Interface Segregation - Keep interfaces focused and minimal\n"
381
+
382
+ response += "\n**Questions to Consider:**\n"
383
+ response += "• Does this fit well with the existing architecture?\n"
384
+ response += "• Are the right abstractions in place?\n"
385
+ response += "• Is the coupling between components appropriate?\n"
386
+ response += "• Will this scale as requirements grow?\n"
387
+
388
+ if context:
389
+ response += f"\n**Context Impact:**\n{context}\n"
390
+ response += "→ Ensure the architectural choices align with this context.\n"
391
+
392
+ return response
393
+
394
+
395
+ class ReviewProtocol:
396
+ """Protocol for review interactions."""
397
+
398
+ def __init__(self):
399
+ self.reviewer = BalancedReviewer()
400
+ self.review_count = 0
401
+ self.max_reviews = 3 # Allow up to 3 reviews per task
402
+
403
+ def request_review(
404
+ self,
405
+ focus: str,
406
+ work_description: str,
407
+ code_snippets: Optional[List[str]] = None,
408
+ file_paths: Optional[List[str]] = None,
409
+ context: Optional[str] = None
410
+ ) -> str:
411
+ """Request a balanced review."""
412
+ if self.review_count >= self.max_reviews:
413
+ return "📋 Review limit reached. You've received comprehensive feedback - time to finalize your implementation."
414
+
415
+ self.review_count += 1
416
+
417
+ try:
418
+ focus_enum = ReviewFocus[focus.upper()]
419
+ except KeyError:
420
+ focus_enum = ReviewFocus.GENERAL
421
+
422
+ review = self.reviewer.review(
423
+ focus_enum,
424
+ work_description,
425
+ code_snippets,
426
+ file_paths,
427
+ context
428
+ )
429
+
430
+ header = f"Review {self.review_count}/{self.max_reviews} (Focus: {focus_enum.value}):\n\n"
431
+ footer = "\n\n💡 This is a balanced review - consider both strengths and suggestions."
432
+
433
+ return header + review + footer