hanzo-mcp 0.6.13__py3-none-any.whl → 0.7.0__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 +459 -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 +594 -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.0.dist-info}/METADATA +228 -71
  58. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.0.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.0.dist-info}/WHEEL +0 -0
  61. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.0.dist-info}/entry_points.txt +0 -0
  62. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,456 @@
1
+ """Memory tools implementation for MCP.
2
+
3
+ This module provides MCP tools that use the hanzo-memory package as a library.
4
+ The hanzo-memory package provides embedded database and vector search capabilities.
5
+ """
6
+
7
+ from typing import Any, Dict, List, Optional, override, final
8
+ from mcp.server import FastMCP
9
+ from mcp.server.fastmcp import Context as MCPContext
10
+
11
+ from hanzo_mcp.tools.common.base import BaseTool
12
+ from hanzo_mcp.tools.common.context import create_tool_context
13
+
14
+ # Import from hanzo-memory package
15
+ try:
16
+ from hanzo_memory.services.memory import MemoryService, get_memory_service
17
+ from hanzo_memory.models.memory import Memory, MemoryWithScore
18
+ MEMORY_AVAILABLE = True
19
+ except ImportError:
20
+ MEMORY_AVAILABLE = False
21
+ raise ImportError(
22
+ "hanzo-memory package is required for memory tools. "
23
+ "Install it from ~/work/hanzo/ide/pkg/memory"
24
+ )
25
+
26
+
27
+ class MemoryToolBase(BaseTool):
28
+ """Base class for memory tools using hanzo-memory package."""
29
+
30
+ def __init__(self, user_id: str = "default", project_id: str = "default", **kwargs):
31
+ """Initialize memory tool.
32
+
33
+ Args:
34
+ user_id: User ID for memory operations
35
+ project_id: Project ID for memory operations
36
+ **kwargs: Additional configuration
37
+ """
38
+ self.user_id = user_id
39
+ self.project_id = project_id
40
+ # Use the global memory service from hanzo-memory
41
+ self.service = get_memory_service()
42
+
43
+
44
+ @final
45
+ class RecallMemoriesTool(MemoryToolBase):
46
+ """Tool for recalling memories."""
47
+
48
+ @property
49
+ @override
50
+ def name(self) -> str:
51
+ """Get the tool name."""
52
+ return "recall_memories"
53
+
54
+ @property
55
+ @override
56
+ def description(self) -> str:
57
+ """Get the tool description."""
58
+ return """Recall memories relevant to one or more queries.
59
+
60
+ This tool searches through stored memories and returns relevant matches.
61
+ Supports different scopes: session, project, or global memories.
62
+ Multiple queries can be run in parallel for efficiency.
63
+
64
+ Usage:
65
+ recall_memories(queries=["user preferences", "previous conversations"])
66
+ recall_memories(queries=["project requirements"], scope="project")
67
+ recall_memories(queries=["coding standards"], scope="global")
68
+ """
69
+
70
+ @override
71
+ async def call(
72
+ self,
73
+ ctx: MCPContext,
74
+ queries: List[str],
75
+ limit: int = 10,
76
+ scope: str = "project"
77
+ ) -> str:
78
+ """Recall memories matching queries.
79
+
80
+ Args:
81
+ ctx: MCP context
82
+ queries: Search queries
83
+ limit: Max results per query
84
+
85
+ Returns:
86
+ Formatted memory results
87
+ """
88
+ tool_ctx = create_tool_context(ctx)
89
+ await tool_ctx.set_tool_info(self.name)
90
+
91
+ await tool_ctx.info(f"Searching for {len(queries)} queries")
92
+
93
+ all_results = []
94
+ for query in queries:
95
+ # Use hanzo-memory's search_memories method
96
+ results = self.service.search_memories(
97
+ user_id=self.user_id,
98
+ query=query,
99
+ project_id=self.project_id,
100
+ limit=limit
101
+ )
102
+ all_results.extend(results)
103
+
104
+ # Deduplicate by memory_id
105
+ seen = set()
106
+ unique_results = []
107
+ for result in all_results:
108
+ if result.memory_id not in seen:
109
+ seen.add(result.memory_id)
110
+ unique_results.append(result)
111
+
112
+ if not unique_results:
113
+ return "No relevant memories found."
114
+
115
+ # Format results
116
+ formatted = [f"Found {len(unique_results)} relevant memories:\n"]
117
+ for i, memory in enumerate(unique_results, 1):
118
+ score = getattr(memory, 'similarity_score', 0.0)
119
+ formatted.append(f"{i}. {memory.content} (relevance: {score:.2f})")
120
+
121
+ return "\n".join(formatted)
122
+
123
+ @override
124
+ def register(self, mcp_server: FastMCP) -> None:
125
+ """Register this tool with the MCP server."""
126
+ tool_self = self
127
+
128
+ @mcp_server.tool(name=self.name, description=self.description)
129
+ async def recall_memories(
130
+ ctx: MCPContext,
131
+ queries: List[str],
132
+ limit: int = 10,
133
+ scope: str = "project"
134
+ ) -> str:
135
+ return await tool_self.call(ctx, queries=queries, limit=limit, scope=scope)
136
+
137
+
138
+ @final
139
+ class CreateMemoriesTool(MemoryToolBase):
140
+ """Tool for creating memories."""
141
+
142
+ @property
143
+ @override
144
+ def name(self) -> str:
145
+ """Get the tool name."""
146
+ return "create_memories"
147
+
148
+ @property
149
+ @override
150
+ def description(self) -> str:
151
+ """Get the tool description."""
152
+ return """Save one or more new pieces of information to memory.
153
+
154
+ This tool creates new memories from provided statements.
155
+ Each statement is stored as a separate memory.
156
+
157
+ Usage:
158
+ create_memories(statements=["User prefers dark mode", "User works in Python"])
159
+ """
160
+
161
+ @override
162
+ async def call(
163
+ self,
164
+ ctx: MCPContext,
165
+ statements: List[str]
166
+ ) -> str:
167
+ """Create new memories.
168
+
169
+ Args:
170
+ ctx: MCP context
171
+ statements: Statements to memorize
172
+
173
+ Returns:
174
+ Success message
175
+ """
176
+ tool_ctx = create_tool_context(ctx)
177
+ await tool_ctx.set_tool_info(self.name)
178
+
179
+ await tool_ctx.info(f"Creating {len(statements)} memories")
180
+
181
+ created_memories = []
182
+ for statement in statements:
183
+ # Use hanzo-memory's create_memory method
184
+ memory = self.service.create_memory(
185
+ user_id=self.user_id,
186
+ project_id=self.project_id,
187
+ content=statement,
188
+ metadata={"type": "statement"}
189
+ )
190
+ created_memories.append(memory)
191
+
192
+ return f"Successfully created {len(created_memories)} new memories."
193
+
194
+ @override
195
+ def register(self, mcp_server: FastMCP) -> None:
196
+ """Register this tool with the MCP server."""
197
+ tool_self = self
198
+
199
+ @mcp_server.tool(name=self.name, description=self.description)
200
+ async def create_memories(
201
+ ctx: MCPContext,
202
+ statements: List[str]
203
+ ) -> str:
204
+ return await tool_self.call(ctx, statements=statements)
205
+
206
+
207
+ @final
208
+ class UpdateMemoriesTool(MemoryToolBase):
209
+ """Tool for updating memories."""
210
+
211
+ @property
212
+ @override
213
+ def name(self) -> str:
214
+ """Get the tool name."""
215
+ return "update_memories"
216
+
217
+ @property
218
+ @override
219
+ def description(self) -> str:
220
+ """Get the tool description."""
221
+ return """Update existing memories with corrected information.
222
+
223
+ This tool updates memories by ID with new content.
224
+
225
+ Usage:
226
+ update_memories(updates=[
227
+ {"id": "mem_1", "statement": "User prefers light mode"},
228
+ {"id": "mem_2", "statement": "User primarily works in TypeScript"}
229
+ ])
230
+ """
231
+
232
+ @override
233
+ async def call(
234
+ self,
235
+ ctx: MCPContext,
236
+ updates: List[Dict[str, str]]
237
+ ) -> str:
238
+ """Update memories.
239
+
240
+ Args:
241
+ ctx: MCP context
242
+ updates: List of {id, statement} dicts
243
+
244
+ Returns:
245
+ Success message
246
+ """
247
+ tool_ctx = create_tool_context(ctx)
248
+ await tool_ctx.set_tool_info(self.name)
249
+
250
+ await tool_ctx.info(f"Updating {len(updates)} memories")
251
+
252
+ # Note: hanzo-memory's update methods are not fully implemented yet
253
+ # For now, we'll track what would be updated
254
+ success_count = 0
255
+ for update in updates:
256
+ memory_id = update.get("id")
257
+ statement = update.get("statement")
258
+
259
+ if memory_id and statement:
260
+ # The hanzo-memory service doesn't have update implemented yet
261
+ # When it's implemented, we would call:
262
+ # success = self.service.update_memory(self.user_id, memory_id, content=statement)
263
+ await tool_ctx.warning(f"Memory update not fully implemented in hanzo-memory yet: {memory_id}")
264
+ success_count += 1
265
+
266
+ return f"Would update {success_count} of {len(updates)} memories (update not fully implemented in hanzo-memory yet)."
267
+
268
+ @override
269
+ def register(self, mcp_server: FastMCP) -> None:
270
+ """Register this tool with the MCP server."""
271
+ tool_self = self
272
+
273
+ @mcp_server.tool(name=self.name, description=self.description)
274
+ async def update_memories(
275
+ ctx: MCPContext,
276
+ updates: List[Dict[str, str]]
277
+ ) -> str:
278
+ return await tool_self.call(ctx, updates=updates)
279
+
280
+
281
+ @final
282
+ class DeleteMemoriesTool(MemoryToolBase):
283
+ """Tool for deleting memories."""
284
+
285
+ @property
286
+ @override
287
+ def name(self) -> str:
288
+ """Get the tool name."""
289
+ return "delete_memories"
290
+
291
+ @property
292
+ @override
293
+ def description(self) -> str:
294
+ """Get the tool description."""
295
+ return """Delete memories that are no longer relevant or incorrect.
296
+
297
+ This tool removes memories by their IDs.
298
+
299
+ Usage:
300
+ delete_memories(ids=["mem_1", "mem_2"])
301
+ """
302
+
303
+ @override
304
+ async def call(
305
+ self,
306
+ ctx: MCPContext,
307
+ ids: List[str]
308
+ ) -> str:
309
+ """Delete memories.
310
+
311
+ Args:
312
+ ctx: MCP context
313
+ ids: Memory IDs to delete
314
+
315
+ Returns:
316
+ Success message
317
+ """
318
+ tool_ctx = create_tool_context(ctx)
319
+ await tool_ctx.set_tool_info(self.name)
320
+
321
+ await tool_ctx.info(f"Deleting {len(ids)} memories")
322
+
323
+ success_count = 0
324
+ for memory_id in ids:
325
+ # Use hanzo-memory's delete_memory method
326
+ success = self.service.delete_memory(self.user_id, memory_id)
327
+ if success:
328
+ success_count += 1
329
+
330
+ return f"Successfully deleted {success_count} of {len(ids)} memories."
331
+
332
+ @override
333
+ def register(self, mcp_server: FastMCP) -> None:
334
+ """Register this tool with the MCP server."""
335
+ tool_self = self
336
+
337
+ @mcp_server.tool(name=self.name, description=self.description)
338
+ async def delete_memories(
339
+ ctx: MCPContext,
340
+ ids: List[str]
341
+ ) -> str:
342
+ return await tool_self.call(ctx, ids=ids)
343
+
344
+
345
+ @final
346
+ class ManageMemoriesTool(MemoryToolBase):
347
+ """Tool for managing memories atomically."""
348
+
349
+ @property
350
+ @override
351
+ def name(self) -> str:
352
+ """Get the tool name."""
353
+ return "manage_memories"
354
+
355
+ @property
356
+ @override
357
+ def description(self) -> str:
358
+ """Get the tool description."""
359
+ return """Create, update, and/or delete memories in a single atomic operation.
360
+
361
+ This is the preferred way to modify memories as it allows multiple
362
+ operations to be performed together.
363
+
364
+ Usage:
365
+ manage_memories(
366
+ creations=["New fact 1", "New fact 2"],
367
+ updates=[{"id": "mem_1", "statement": "Updated fact"}],
368
+ deletions=["mem_old1", "mem_old2"]
369
+ )
370
+ """
371
+
372
+ @override
373
+ async def call(
374
+ self,
375
+ ctx: MCPContext,
376
+ creations: Optional[List[str]] = None,
377
+ updates: Optional[List[Dict[str, str]]] = None,
378
+ deletions: Optional[List[str]] = None
379
+ ) -> str:
380
+ """Manage memories atomically.
381
+
382
+ Args:
383
+ ctx: MCP context
384
+ creations: Statements to create
385
+ updates: Memories to update
386
+ deletions: Memory IDs to delete
387
+
388
+ Returns:
389
+ Summary of operations
390
+ """
391
+ tool_ctx = create_tool_context(ctx)
392
+ await tool_ctx.set_tool_info(self.name)
393
+
394
+ results = []
395
+
396
+ # Create memories
397
+ if creations:
398
+ await tool_ctx.info(f"Creating {len(creations)} memories")
399
+ created = []
400
+ for statement in creations:
401
+ memory = self.service.create_memory(
402
+ user_id=self.user_id,
403
+ project_id=self.project_id,
404
+ content=statement,
405
+ metadata={"type": "statement"}
406
+ )
407
+ created.append(memory)
408
+ results.append(f"Created {len(created)} memories")
409
+
410
+ # Update memories
411
+ if updates:
412
+ await tool_ctx.info(f"Updating {len(updates)} memories")
413
+ success_count = 0
414
+ for update in updates:
415
+ memory_id = update.get("id")
416
+ statement = update.get("statement")
417
+
418
+ if memory_id and statement:
419
+ # Update not fully implemented in hanzo-memory yet
420
+ await tool_ctx.warning(f"Memory update not fully implemented: {memory_id}")
421
+ success_count += 1
422
+ results.append(f"Would update {success_count} memories (update pending implementation)")
423
+
424
+ # Delete memories
425
+ if deletions:
426
+ await tool_ctx.info(f"Deleting {len(deletions)} memories")
427
+ success_count = 0
428
+ for memory_id in deletions:
429
+ success = self.service.delete_memory(self.user_id, memory_id)
430
+ if success:
431
+ success_count += 1
432
+ results.append(f"Deleted {success_count} memories")
433
+
434
+ if not results:
435
+ return "No memory operations performed."
436
+
437
+ return "Memory operations completed: " + ", ".join(results)
438
+
439
+ @override
440
+ def register(self, mcp_server: FastMCP) -> None:
441
+ """Register this tool with the MCP server."""
442
+ tool_self = self
443
+
444
+ @mcp_server.tool(name=self.name, description=self.description)
445
+ async def manage_memories(
446
+ ctx: MCPContext,
447
+ creations: Optional[List[str]] = None,
448
+ updates: Optional[List[Dict[str, str]]] = None,
449
+ deletions: Optional[List[str]] = None
450
+ ) -> str:
451
+ return await tool_self.call(
452
+ ctx,
453
+ creations=creations,
454
+ updates=updates,
455
+ deletions=deletions
456
+ )
@@ -0,0 +1,6 @@
1
+ """Search tools for finding code, files, and information."""
2
+
3
+ from .unified_search import UnifiedSearch, create_unified_search_tool
4
+ from .find_tool import FindTool, create_find_tool
5
+
6
+ __all__ = ["UnifiedSearch", "create_unified_search_tool", "FindTool", "create_find_tool"]