hanzo-mcp 0.7.6__py3-none-any.whl → 0.8.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 (178) hide show
  1. hanzo_mcp/__init__.py +7 -1
  2. hanzo_mcp/__main__.py +1 -1
  3. hanzo_mcp/analytics/__init__.py +2 -2
  4. hanzo_mcp/analytics/posthog_analytics.py +76 -82
  5. hanzo_mcp/cli.py +31 -36
  6. hanzo_mcp/cli_enhanced.py +94 -72
  7. hanzo_mcp/cli_plugin.py +27 -17
  8. hanzo_mcp/config/__init__.py +2 -2
  9. hanzo_mcp/config/settings.py +112 -88
  10. hanzo_mcp/config/tool_config.py +32 -34
  11. hanzo_mcp/dev_server.py +66 -67
  12. hanzo_mcp/prompts/__init__.py +94 -12
  13. hanzo_mcp/prompts/enhanced_prompts.py +809 -0
  14. hanzo_mcp/prompts/example_custom_prompt.py +6 -5
  15. hanzo_mcp/prompts/project_todo_reminder.py +0 -1
  16. hanzo_mcp/prompts/tool_explorer.py +10 -7
  17. hanzo_mcp/server.py +17 -21
  18. hanzo_mcp/server_enhanced.py +15 -22
  19. hanzo_mcp/tools/__init__.py +56 -28
  20. hanzo_mcp/tools/agent/__init__.py +16 -19
  21. hanzo_mcp/tools/agent/agent.py +82 -65
  22. hanzo_mcp/tools/agent/agent_tool.py +152 -122
  23. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +66 -62
  24. hanzo_mcp/tools/agent/clarification_protocol.py +55 -50
  25. hanzo_mcp/tools/agent/clarification_tool.py +11 -10
  26. hanzo_mcp/tools/agent/claude_cli_tool.py +21 -20
  27. hanzo_mcp/tools/agent/claude_desktop_auth.py +130 -144
  28. hanzo_mcp/tools/agent/cli_agent_base.py +59 -53
  29. hanzo_mcp/tools/agent/code_auth.py +102 -107
  30. hanzo_mcp/tools/agent/code_auth_tool.py +28 -27
  31. hanzo_mcp/tools/agent/codex_cli_tool.py +20 -19
  32. hanzo_mcp/tools/agent/critic_tool.py +86 -73
  33. hanzo_mcp/tools/agent/gemini_cli_tool.py +21 -20
  34. hanzo_mcp/tools/agent/grok_cli_tool.py +21 -20
  35. hanzo_mcp/tools/agent/iching_tool.py +404 -139
  36. hanzo_mcp/tools/agent/network_tool.py +89 -73
  37. hanzo_mcp/tools/agent/prompt.py +2 -1
  38. hanzo_mcp/tools/agent/review_tool.py +101 -98
  39. hanzo_mcp/tools/agent/swarm_alias.py +87 -0
  40. hanzo_mcp/tools/agent/swarm_tool.py +246 -161
  41. hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +134 -92
  42. hanzo_mcp/tools/agent/tool_adapter.py +21 -11
  43. hanzo_mcp/tools/common/__init__.py +1 -1
  44. hanzo_mcp/tools/common/base.py +3 -5
  45. hanzo_mcp/tools/common/batch_tool.py +46 -39
  46. hanzo_mcp/tools/common/config_tool.py +120 -84
  47. hanzo_mcp/tools/common/context.py +1 -5
  48. hanzo_mcp/tools/common/context_fix.py +5 -3
  49. hanzo_mcp/tools/common/critic_tool.py +4 -8
  50. hanzo_mcp/tools/common/decorators.py +58 -56
  51. hanzo_mcp/tools/common/enhanced_base.py +29 -32
  52. hanzo_mcp/tools/common/fastmcp_pagination.py +91 -94
  53. hanzo_mcp/tools/common/forgiving_edit.py +91 -87
  54. hanzo_mcp/tools/common/mode.py +15 -17
  55. hanzo_mcp/tools/common/mode_loader.py +27 -24
  56. hanzo_mcp/tools/common/paginated_base.py +61 -53
  57. hanzo_mcp/tools/common/paginated_response.py +72 -79
  58. hanzo_mcp/tools/common/pagination.py +50 -53
  59. hanzo_mcp/tools/common/permissions.py +4 -4
  60. hanzo_mcp/tools/common/personality.py +186 -138
  61. hanzo_mcp/tools/common/plugin_loader.py +54 -54
  62. hanzo_mcp/tools/common/stats.py +65 -47
  63. hanzo_mcp/tools/common/test_helpers.py +31 -0
  64. hanzo_mcp/tools/common/thinking_tool.py +4 -8
  65. hanzo_mcp/tools/common/tool_disable.py +17 -12
  66. hanzo_mcp/tools/common/tool_enable.py +13 -14
  67. hanzo_mcp/tools/common/tool_list.py +36 -28
  68. hanzo_mcp/tools/common/truncate.py +23 -23
  69. hanzo_mcp/tools/config/__init__.py +4 -4
  70. hanzo_mcp/tools/config/config_tool.py +42 -29
  71. hanzo_mcp/tools/config/index_config.py +37 -34
  72. hanzo_mcp/tools/config/mode_tool.py +175 -55
  73. hanzo_mcp/tools/database/__init__.py +15 -12
  74. hanzo_mcp/tools/database/database_manager.py +77 -75
  75. hanzo_mcp/tools/database/graph.py +137 -91
  76. hanzo_mcp/tools/database/graph_add.py +30 -18
  77. hanzo_mcp/tools/database/graph_query.py +178 -102
  78. hanzo_mcp/tools/database/graph_remove.py +33 -28
  79. hanzo_mcp/tools/database/graph_search.py +97 -75
  80. hanzo_mcp/tools/database/graph_stats.py +91 -59
  81. hanzo_mcp/tools/database/sql.py +107 -79
  82. hanzo_mcp/tools/database/sql_query.py +30 -24
  83. hanzo_mcp/tools/database/sql_search.py +29 -25
  84. hanzo_mcp/tools/database/sql_stats.py +47 -35
  85. hanzo_mcp/tools/editor/neovim_command.py +25 -28
  86. hanzo_mcp/tools/editor/neovim_edit.py +21 -23
  87. hanzo_mcp/tools/editor/neovim_session.py +60 -54
  88. hanzo_mcp/tools/filesystem/__init__.py +31 -30
  89. hanzo_mcp/tools/filesystem/ast_multi_edit.py +329 -249
  90. hanzo_mcp/tools/filesystem/ast_tool.py +4 -4
  91. hanzo_mcp/tools/filesystem/base.py +1 -1
  92. hanzo_mcp/tools/filesystem/batch_search.py +316 -224
  93. hanzo_mcp/tools/filesystem/content_replace.py +4 -4
  94. hanzo_mcp/tools/filesystem/diff.py +71 -59
  95. hanzo_mcp/tools/filesystem/directory_tree.py +7 -7
  96. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +49 -37
  97. hanzo_mcp/tools/filesystem/edit.py +4 -4
  98. hanzo_mcp/tools/filesystem/find.py +173 -80
  99. hanzo_mcp/tools/filesystem/find_files.py +73 -52
  100. hanzo_mcp/tools/filesystem/git_search.py +157 -104
  101. hanzo_mcp/tools/filesystem/grep.py +8 -8
  102. hanzo_mcp/tools/filesystem/multi_edit.py +4 -8
  103. hanzo_mcp/tools/filesystem/read.py +12 -10
  104. hanzo_mcp/tools/filesystem/rules_tool.py +59 -43
  105. hanzo_mcp/tools/filesystem/search_tool.py +263 -207
  106. hanzo_mcp/tools/filesystem/symbols_tool.py +94 -54
  107. hanzo_mcp/tools/filesystem/tree.py +35 -33
  108. hanzo_mcp/tools/filesystem/unix_aliases.py +13 -18
  109. hanzo_mcp/tools/filesystem/watch.py +37 -36
  110. hanzo_mcp/tools/filesystem/write.py +4 -8
  111. hanzo_mcp/tools/jupyter/__init__.py +4 -4
  112. hanzo_mcp/tools/jupyter/base.py +4 -5
  113. hanzo_mcp/tools/jupyter/jupyter.py +67 -47
  114. hanzo_mcp/tools/jupyter/notebook_edit.py +4 -4
  115. hanzo_mcp/tools/jupyter/notebook_read.py +4 -7
  116. hanzo_mcp/tools/llm/__init__.py +5 -7
  117. hanzo_mcp/tools/llm/consensus_tool.py +72 -52
  118. hanzo_mcp/tools/llm/llm_manage.py +101 -60
  119. hanzo_mcp/tools/llm/llm_tool.py +226 -166
  120. hanzo_mcp/tools/llm/provider_tools.py +25 -26
  121. hanzo_mcp/tools/lsp/__init__.py +1 -1
  122. hanzo_mcp/tools/lsp/lsp_tool.py +228 -143
  123. hanzo_mcp/tools/mcp/__init__.py +2 -3
  124. hanzo_mcp/tools/mcp/mcp_add.py +27 -25
  125. hanzo_mcp/tools/mcp/mcp_remove.py +7 -8
  126. hanzo_mcp/tools/mcp/mcp_stats.py +23 -22
  127. hanzo_mcp/tools/mcp/mcp_tool.py +129 -98
  128. hanzo_mcp/tools/memory/__init__.py +39 -21
  129. hanzo_mcp/tools/memory/knowledge_tools.py +124 -99
  130. hanzo_mcp/tools/memory/memory_tools.py +90 -108
  131. hanzo_mcp/tools/search/__init__.py +7 -2
  132. hanzo_mcp/tools/search/find_tool.py +297 -212
  133. hanzo_mcp/tools/search/unified_search.py +366 -314
  134. hanzo_mcp/tools/shell/__init__.py +8 -7
  135. hanzo_mcp/tools/shell/auto_background.py +56 -49
  136. hanzo_mcp/tools/shell/base.py +1 -1
  137. hanzo_mcp/tools/shell/base_process.py +75 -75
  138. hanzo_mcp/tools/shell/bash_session.py +2 -2
  139. hanzo_mcp/tools/shell/bash_session_executor.py +4 -4
  140. hanzo_mcp/tools/shell/bash_tool.py +24 -31
  141. hanzo_mcp/tools/shell/command_executor.py +12 -12
  142. hanzo_mcp/tools/shell/logs.py +43 -33
  143. hanzo_mcp/tools/shell/npx.py +13 -13
  144. hanzo_mcp/tools/shell/npx_background.py +24 -21
  145. hanzo_mcp/tools/shell/npx_tool.py +18 -22
  146. hanzo_mcp/tools/shell/open.py +19 -21
  147. hanzo_mcp/tools/shell/pkill.py +31 -26
  148. hanzo_mcp/tools/shell/process_tool.py +32 -32
  149. hanzo_mcp/tools/shell/processes.py +57 -58
  150. hanzo_mcp/tools/shell/run_background.py +24 -25
  151. hanzo_mcp/tools/shell/run_command.py +5 -5
  152. hanzo_mcp/tools/shell/run_command_windows.py +5 -5
  153. hanzo_mcp/tools/shell/session_storage.py +3 -3
  154. hanzo_mcp/tools/shell/streaming_command.py +141 -126
  155. hanzo_mcp/tools/shell/uvx.py +24 -25
  156. hanzo_mcp/tools/shell/uvx_background.py +35 -33
  157. hanzo_mcp/tools/shell/uvx_tool.py +18 -22
  158. hanzo_mcp/tools/todo/__init__.py +6 -2
  159. hanzo_mcp/tools/todo/todo.py +50 -37
  160. hanzo_mcp/tools/todo/todo_read.py +5 -8
  161. hanzo_mcp/tools/todo/todo_write.py +5 -7
  162. hanzo_mcp/tools/vector/__init__.py +40 -28
  163. hanzo_mcp/tools/vector/ast_analyzer.py +176 -143
  164. hanzo_mcp/tools/vector/git_ingester.py +170 -179
  165. hanzo_mcp/tools/vector/index_tool.py +96 -44
  166. hanzo_mcp/tools/vector/infinity_store.py +283 -228
  167. hanzo_mcp/tools/vector/mock_infinity.py +39 -40
  168. hanzo_mcp/tools/vector/project_manager.py +88 -78
  169. hanzo_mcp/tools/vector/vector.py +59 -42
  170. hanzo_mcp/tools/vector/vector_index.py +30 -27
  171. hanzo_mcp/tools/vector/vector_search.py +64 -45
  172. hanzo_mcp/types.py +6 -4
  173. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/METADATA +1 -1
  174. hanzo_mcp-0.8.0.dist-info/RECORD +185 -0
  175. hanzo_mcp-0.7.6.dist-info/RECORD +0 -182
  176. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/WHEEL +0 -0
  177. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/entry_points.txt +0 -0
  178. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,8 @@ These tools use the hanzo-memory package to manage knowledge bases and facts,
4
4
  supporting hierarchical organization (session, project, global).
5
5
  """
6
6
 
7
- from typing import Any, Dict, List, Optional, override, final
7
+ from typing import Any, Dict, List, Optional, final, override
8
+
8
9
  from mcp.server import FastMCP
9
10
  from mcp.server.fastmcp import Context as MCPContext
10
11
 
@@ -13,24 +14,24 @@ from hanzo_mcp.tools.common.context import create_tool_context
13
14
 
14
15
  # Import from hanzo-memory package
15
16
  try:
16
- from hanzo_memory.services.memory import MemoryService, get_memory_service
17
17
  from hanzo_memory.models.memory import Memory, MemoryWithScore
18
- from hanzo_memory.models.knowledge import KnowledgeBase, Fact, FactCreate
18
+ from hanzo_memory.services.memory import MemoryService, get_memory_service
19
+ from hanzo_memory.models.knowledge import Fact, FactCreate, KnowledgeBase
20
+
19
21
  KNOWLEDGE_AVAILABLE = True
20
22
  except ImportError:
21
23
  KNOWLEDGE_AVAILABLE = False
22
24
  raise ImportError(
23
- "hanzo-memory package is required for knowledge tools. "
24
- "Install it from ~/work/hanzo/ide/pkg/memory"
25
+ "hanzo-memory package is required for knowledge tools. Install it from ~/work/hanzo/ide/pkg/memory"
25
26
  )
26
27
 
27
28
 
28
29
  class KnowledgeToolBase(BaseTool):
29
30
  """Base class for knowledge tools using hanzo-memory package."""
30
-
31
+
31
32
  def __init__(self, user_id: str = "default", project_id: str = "default", **kwargs):
32
33
  """Initialize knowledge tool.
33
-
34
+
34
35
  Args:
35
36
  user_id: User ID for knowledge operations
36
37
  project_id: Project ID for knowledge operations
@@ -45,13 +46,13 @@ class KnowledgeToolBase(BaseTool):
45
46
  @final
46
47
  class RecallFactsTool(KnowledgeToolBase):
47
48
  """Tool for recalling facts from knowledge bases."""
48
-
49
+
49
50
  @property
50
51
  @override
51
52
  def name(self) -> str:
52
53
  """Get the tool name."""
53
54
  return "recall_facts"
54
-
55
+
55
56
  @property
56
57
  @override
57
58
  def description(self) -> str:
@@ -66,7 +67,7 @@ recall_facts(queries=["Python best practices"], kb_name="coding_standards")
66
67
  recall_facts(queries=["API endpoints"], scope="project")
67
68
  recall_facts(queries=["company policies"], scope="global", limit=5)
68
69
  """
69
-
70
+
70
71
  @override
71
72
  async def call(
72
73
  self,
@@ -74,25 +75,25 @@ recall_facts(queries=["company policies"], scope="global", limit=5)
74
75
  queries: List[str],
75
76
  kb_name: Optional[str] = None,
76
77
  scope: str = "project",
77
- limit: int = 10
78
+ limit: int = 10,
78
79
  ) -> str:
79
80
  """Recall facts matching queries.
80
-
81
+
81
82
  Args:
82
83
  ctx: MCP context
83
84
  queries: Search queries
84
85
  kb_name: Optional knowledge base name to search in
85
86
  scope: Scope level (session, project, global)
86
87
  limit: Max results per query
87
-
88
+
88
89
  Returns:
89
90
  Formatted fact results
90
91
  """
91
92
  tool_ctx = create_tool_context(ctx)
92
93
  await tool_ctx.set_tool_info(self.name)
93
-
94
+
94
95
  await tool_ctx.info(f"Searching for facts in scope: {scope}")
95
-
96
+
96
97
  # Determine the appropriate IDs based on scope
97
98
  if scope == "global":
98
99
  user_id = "global"
@@ -104,26 +105,23 @@ recall_facts(queries=["company policies"], scope="global", limit=5)
104
105
  else:
105
106
  user_id = self.user_id
106
107
  project_id = self.project_id
107
-
108
+
108
109
  all_facts = []
109
110
  for query in queries:
110
111
  # Search for facts using memory service with fact metadata
111
112
  search_query = f"fact: {query}"
112
113
  if kb_name:
113
114
  search_query = f"kb:{kb_name} {search_query}"
114
-
115
+
115
116
  memories = self.service.search_memories(
116
- user_id=user_id,
117
- query=search_query,
118
- project_id=project_id,
119
- limit=limit
117
+ user_id=user_id, query=search_query, project_id=project_id, limit=limit
120
118
  )
121
-
119
+
122
120
  # Filter for fact-type memories
123
121
  for memory in memories:
124
122
  if memory.metadata and memory.metadata.get("type") == "fact":
125
123
  all_facts.append(memory)
126
-
124
+
127
125
  # Deduplicate by memory ID
128
126
  seen = set()
129
127
  unique_facts = []
@@ -131,10 +129,10 @@ recall_facts(queries=["company policies"], scope="global", limit=5)
131
129
  if fact.memory_id not in seen:
132
130
  seen.add(fact.memory_id)
133
131
  unique_facts.append(fact)
134
-
132
+
135
133
  if not unique_facts:
136
134
  return "No relevant facts found."
137
-
135
+
138
136
  # Format results
139
137
  formatted = [f"Found {len(unique_facts)} relevant facts:\n"]
140
138
  for i, fact in enumerate(unique_facts, 1):
@@ -142,40 +140,48 @@ recall_facts(queries=["company policies"], scope="global", limit=5)
142
140
  if fact.metadata and fact.metadata.get("kb_name"):
143
141
  kb_info = f" (KB: {fact.metadata['kb_name']})"
144
142
  formatted.append(f"{i}. {fact.content}{kb_info}")
145
- if fact.metadata and len(fact.metadata) > 2: # More than just type and kb_name
143
+ if (
144
+ fact.metadata and len(fact.metadata) > 2
145
+ ): # More than just type and kb_name
146
146
  # Show other metadata
147
- other_meta = {k: v for k, v in fact.metadata.items() if k not in ["type", "kb_name"]}
147
+ other_meta = {
148
+ k: v
149
+ for k, v in fact.metadata.items()
150
+ if k not in ["type", "kb_name"]
151
+ }
148
152
  if other_meta:
149
153
  formatted.append(f" Metadata: {other_meta}")
150
-
154
+
151
155
  return "\n".join(formatted)
152
-
156
+
153
157
  @override
154
158
  def register(self, mcp_server: FastMCP) -> None:
155
159
  """Register this tool with the MCP server."""
156
160
  tool_self = self
157
-
161
+
158
162
  @mcp_server.tool(name=self.name, description=self.description)
159
163
  async def recall_facts(
160
164
  ctx: MCPContext,
161
165
  queries: List[str],
162
166
  kb_name: Optional[str] = None,
163
167
  scope: str = "project",
164
- limit: int = 10
168
+ limit: int = 10,
165
169
  ) -> str:
166
- return await tool_self.call(ctx, queries=queries, kb_name=kb_name, scope=scope, limit=limit)
170
+ return await tool_self.call(
171
+ ctx, queries=queries, kb_name=kb_name, scope=scope, limit=limit
172
+ )
167
173
 
168
174
 
169
175
  @final
170
176
  class StoreFactsTool(KnowledgeToolBase):
171
177
  """Tool for storing facts in knowledge bases."""
172
-
178
+
173
179
  @property
174
180
  @override
175
181
  def name(self) -> str:
176
182
  """Get the tool name."""
177
183
  return "store_facts"
178
-
184
+
179
185
  @property
180
186
  @override
181
187
  def description(self) -> str:
@@ -190,7 +196,7 @@ store_facts(facts=["Python uses indentation for blocks"], kb_name="python_basics
190
196
  store_facts(facts=["API rate limit: 100/hour"], scope="project", kb_name="api_docs")
191
197
  store_facts(facts=["Company founded in 2020"], scope="global", kb_name="company_info")
192
198
  """
193
-
199
+
194
200
  @override
195
201
  async def call(
196
202
  self,
@@ -198,25 +204,25 @@ store_facts(facts=["Company founded in 2020"], scope="global", kb_name="company_
198
204
  facts: List[str],
199
205
  kb_name: str = "general",
200
206
  scope: str = "project",
201
- metadata: Optional[Dict[str, Any]] = None
207
+ metadata: Optional[Dict[str, Any]] = None,
202
208
  ) -> str:
203
209
  """Store new facts.
204
-
210
+
205
211
  Args:
206
212
  ctx: MCP context
207
213
  facts: Facts to store
208
214
  kb_name: Knowledge base name
209
215
  scope: Scope level (session, project, global)
210
216
  metadata: Optional metadata for all facts
211
-
217
+
212
218
  Returns:
213
219
  Success message
214
220
  """
215
221
  tool_ctx = create_tool_context(ctx)
216
222
  await tool_ctx.set_tool_info(self.name)
217
-
223
+
218
224
  await tool_ctx.info(f"Storing {len(facts)} facts in {kb_name} (scope: {scope})")
219
-
225
+
220
226
  # Determine the appropriate IDs based on scope
221
227
  if scope == "global":
222
228
  user_id = "global"
@@ -227,51 +233,53 @@ store_facts(facts=["Company founded in 2020"], scope="global", kb_name="company_
227
233
  else:
228
234
  user_id = self.user_id
229
235
  project_id = self.project_id
230
-
236
+
231
237
  created_facts = []
232
238
  for fact_content in facts:
233
239
  # Create fact as a memory with special metadata
234
240
  fact_metadata = {"type": "fact", "kb_name": kb_name}
235
241
  if metadata:
236
242
  fact_metadata.update(metadata)
237
-
243
+
238
244
  memory = self.service.create_memory(
239
245
  user_id=user_id,
240
246
  project_id=project_id,
241
247
  content=f"fact: {fact_content}",
242
248
  metadata=fact_metadata,
243
- importance=1.5 # Facts have higher importance
249
+ importance=1.5, # Facts have higher importance
244
250
  )
245
251
  created_facts.append(memory)
246
-
252
+
247
253
  return f"Successfully stored {len(created_facts)} facts in {kb_name}."
248
-
254
+
249
255
  @override
250
256
  def register(self, mcp_server: FastMCP) -> None:
251
257
  """Register this tool with the MCP server."""
252
258
  tool_self = self
253
-
259
+
254
260
  @mcp_server.tool(name=self.name, description=self.description)
255
261
  async def store_facts(
256
262
  ctx: MCPContext,
257
263
  facts: List[str],
258
264
  kb_name: str = "general",
259
265
  scope: str = "project",
260
- metadata: Optional[Dict[str, Any]] = None
266
+ metadata: Optional[Dict[str, Any]] = None,
261
267
  ) -> str:
262
- return await tool_self.call(ctx, facts=facts, kb_name=kb_name, scope=scope, metadata=metadata)
268
+ return await tool_self.call(
269
+ ctx, facts=facts, kb_name=kb_name, scope=scope, metadata=metadata
270
+ )
263
271
 
264
272
 
265
273
  @final
266
274
  class SummarizeToMemoryTool(KnowledgeToolBase):
267
275
  """Tool for summarizing information and storing in memory."""
268
-
276
+
269
277
  @property
270
278
  @override
271
279
  def name(self) -> str:
272
280
  """Get the tool name."""
273
281
  return "summarize_to_memory"
274
-
282
+
275
283
  @property
276
284
  @override
277
285
  def description(self) -> str:
@@ -286,7 +294,7 @@ summarize_to_memory(content="Long discussion about API design...", topic="API De
286
294
  summarize_to_memory(content="User preferences...", topic="User Preferences", scope="session")
287
295
  summarize_to_memory(content="Company guidelines...", topic="Guidelines", scope="global")
288
296
  """
289
-
297
+
290
298
  @override
291
299
  async def call(
292
300
  self,
@@ -294,33 +302,38 @@ summarize_to_memory(content="Company guidelines...", topic="Guidelines", scope="
294
302
  content: str,
295
303
  topic: str,
296
304
  scope: str = "project",
297
- auto_facts: bool = True
305
+ auto_facts: bool = True,
298
306
  ) -> str:
299
307
  """Summarize content and store in memory.
300
-
308
+
301
309
  Args:
302
310
  ctx: MCP context
303
311
  content: Content to summarize
304
312
  topic: Topic or title for the summary
305
313
  scope: Scope level (session, project, global)
306
314
  auto_facts: Whether to extract facts automatically
307
-
315
+
308
316
  Returns:
309
317
  Success message with summary
310
318
  """
311
319
  tool_ctx = create_tool_context(ctx)
312
320
  await tool_ctx.set_tool_info(self.name)
313
-
321
+
314
322
  await tool_ctx.info(f"Summarizing content about {topic}")
315
-
323
+
316
324
  # Use the memory service to create a summary
317
325
  # This would typically use an LLM to summarize, but for now we'll store as-is
318
- summary = f"Summary of {topic}:\n{content[:500]}..." if len(content) > 500 else content
319
-
326
+ summary = (
327
+ f"Summary of {topic}:\n{content[:500]}..."
328
+ if len(content) > 500
329
+ else content
330
+ )
331
+
320
332
  # Store the summary as a memory
321
333
  from hanzo_memory.services.memory import get_memory_service
334
+
322
335
  memory_service = get_memory_service()
323
-
336
+
324
337
  # Determine scope
325
338
  if scope == "global":
326
339
  user_id = "global"
@@ -331,50 +344,54 @@ summarize_to_memory(content="Company guidelines...", topic="Guidelines", scope="
331
344
  else:
332
345
  user_id = self.user_id
333
346
  project_id = self.project_id
334
-
347
+
335
348
  memory = memory_service.create_memory(
336
349
  user_id=user_id,
337
350
  project_id=project_id,
338
351
  content=summary,
339
- metadata={"topic": topic, "type": "summary", "scope": scope}
352
+ metadata={"topic": topic, "type": "summary", "scope": scope},
340
353
  )
341
-
354
+
342
355
  result = f"Stored summary of {topic} in {scope} memory."
343
-
356
+
344
357
  # Optionally extract facts
345
358
  if auto_facts:
346
359
  # In a real implementation, this would use LLM to extract key facts
347
360
  # For now, we'll just note it
348
- result += "\n(Auto-fact extraction would extract key facts from the summary)"
349
-
361
+ result += (
362
+ "\n(Auto-fact extraction would extract key facts from the summary)"
363
+ )
364
+
350
365
  return result
351
-
366
+
352
367
  @override
353
368
  def register(self, mcp_server: FastMCP) -> None:
354
369
  """Register this tool with the MCP server."""
355
370
  tool_self = self
356
-
371
+
357
372
  @mcp_server.tool(name=self.name, description=self.description)
358
373
  async def summarize_to_memory(
359
374
  ctx: MCPContext,
360
375
  content: str,
361
376
  topic: str,
362
377
  scope: str = "project",
363
- auto_facts: bool = True
378
+ auto_facts: bool = True,
364
379
  ) -> str:
365
- return await tool_self.call(ctx, content=content, topic=topic, scope=scope, auto_facts=auto_facts)
380
+ return await tool_self.call(
381
+ ctx, content=content, topic=topic, scope=scope, auto_facts=auto_facts
382
+ )
366
383
 
367
384
 
368
385
  @final
369
386
  class ManageKnowledgeBasesTool(KnowledgeToolBase):
370
387
  """Tool for managing knowledge bases."""
371
-
388
+
372
389
  @property
373
390
  @override
374
391
  def name(self) -> str:
375
392
  """Get the tool name."""
376
393
  return "manage_knowledge_bases"
377
-
394
+
378
395
  @property
379
396
  @override
380
397
  def description(self) -> str:
@@ -389,7 +406,7 @@ manage_knowledge_bases(action="create", kb_name="api_docs", description="API doc
389
406
  manage_knowledge_bases(action="list", scope="project")
390
407
  manage_knowledge_bases(action="delete", kb_name="old_docs")
391
408
  """
392
-
409
+
393
410
  @override
394
411
  async def call(
395
412
  self,
@@ -397,23 +414,23 @@ manage_knowledge_bases(action="delete", kb_name="old_docs")
397
414
  action: str,
398
415
  kb_name: Optional[str] = None,
399
416
  description: Optional[str] = None,
400
- scope: str = "project"
417
+ scope: str = "project",
401
418
  ) -> str:
402
419
  """Manage knowledge bases.
403
-
420
+
404
421
  Args:
405
422
  ctx: MCP context
406
423
  action: Action to perform (create, list, delete)
407
424
  kb_name: Knowledge base name (for create/delete)
408
425
  description: Description (for create)
409
426
  scope: Scope level
410
-
427
+
411
428
  Returns:
412
429
  Result message
413
430
  """
414
431
  tool_ctx = create_tool_context(ctx)
415
432
  await tool_ctx.set_tool_info(self.name)
416
-
433
+
417
434
  # Determine scope
418
435
  if scope == "global":
419
436
  user_id = "global"
@@ -424,46 +441,46 @@ manage_knowledge_bases(action="delete", kb_name="old_docs")
424
441
  else:
425
442
  user_id = self.user_id
426
443
  project_id = self.project_id
427
-
444
+
428
445
  if action == "create":
429
446
  if not kb_name:
430
447
  return "Error: kb_name required for create action"
431
-
448
+
432
449
  # Create a knowledge base entry as a special memory
433
450
  kb_metadata = {
434
451
  "type": "knowledge_base",
435
452
  "kb_name": kb_name,
436
453
  "description": description or "",
437
- "scope": scope
454
+ "scope": scope,
438
455
  }
439
-
456
+
440
457
  memory = self.service.create_memory(
441
458
  user_id=user_id,
442
459
  project_id=project_id,
443
460
  content=f"Knowledge Base: {kb_name}\nDescription: {description or 'No description'}",
444
461
  metadata=kb_metadata,
445
- importance=2.0 # KBs have high importance
462
+ importance=2.0, # KBs have high importance
446
463
  )
447
464
  return f"Created knowledge base '{kb_name}' in {scope} scope."
448
-
465
+
449
466
  elif action == "list":
450
467
  # Search for knowledge base entries
451
468
  kbs = self.service.search_memories(
452
469
  user_id=user_id,
453
470
  query="type:knowledge_base",
454
471
  project_id=project_id,
455
- limit=100
472
+ limit=100,
456
473
  )
457
-
474
+
458
475
  # Filter for KB-type memories
459
476
  kb_list = []
460
477
  for memory in kbs:
461
478
  if memory.metadata and memory.metadata.get("type") == "knowledge_base":
462
479
  kb_list.append(memory)
463
-
480
+
464
481
  if not kb_list:
465
482
  return f"No knowledge bases found in {scope} scope."
466
-
483
+
467
484
  formatted = [f"Knowledge bases in {scope} scope:"]
468
485
  for kb in kb_list:
469
486
  name = kb.metadata.get("kb_name", "Unknown")
@@ -471,48 +488,56 @@ manage_knowledge_bases(action="delete", kb_name="old_docs")
471
488
  desc_text = f" - {desc}" if desc else ""
472
489
  formatted.append(f"- {name}{desc_text}")
473
490
  return "\n".join(formatted)
474
-
491
+
475
492
  elif action == "delete":
476
493
  if not kb_name:
477
494
  return "Error: kb_name required for delete action"
478
-
495
+
479
496
  # Search for the KB entry
480
497
  kbs = self.service.search_memories(
481
498
  user_id=user_id,
482
499
  query=f"type:knowledge_base kb_name:{kb_name}",
483
500
  project_id=project_id,
484
- limit=10
501
+ limit=10,
485
502
  )
486
-
503
+
487
504
  deleted_count = 0
488
505
  for memory in kbs:
489
- if (memory.metadata and
490
- memory.metadata.get("type") == "knowledge_base" and
491
- memory.metadata.get("kb_name") == kb_name):
506
+ if (
507
+ memory.metadata
508
+ and memory.metadata.get("type") == "knowledge_base"
509
+ and memory.metadata.get("kb_name") == kb_name
510
+ ):
492
511
  # Note: delete_memory is not fully implemented
493
512
  # but we'll call it anyway
494
513
  self.service.delete_memory(user_id, memory.memory_id)
495
514
  deleted_count += 1
496
-
515
+
497
516
  if deleted_count > 0:
498
517
  return f"Deleted knowledge base '{kb_name}'."
499
518
  else:
500
519
  return f"Knowledge base '{kb_name}' not found."
501
-
520
+
502
521
  else:
503
522
  return f"Unknown action: {action}. Use create, list, or delete."
504
-
523
+
505
524
  @override
506
525
  def register(self, mcp_server: FastMCP) -> None:
507
526
  """Register this tool with the MCP server."""
508
527
  tool_self = self
509
-
528
+
510
529
  @mcp_server.tool(name=self.name, description=self.description)
511
530
  async def manage_knowledge_bases(
512
531
  ctx: MCPContext,
513
532
  action: str,
514
533
  kb_name: Optional[str] = None,
515
534
  description: Optional[str] = None,
516
- scope: str = "project"
535
+ scope: str = "project",
517
536
  ) -> str:
518
- return await tool_self.call(ctx, action=action, kb_name=kb_name, description=description, scope=scope)
537
+ return await tool_self.call(
538
+ ctx,
539
+ action=action,
540
+ kb_name=kb_name,
541
+ description=description,
542
+ scope=scope,
543
+ )