hanzo-mcp 0.7.7__py3-none-any.whl → 0.8.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 (178) hide show
  1. hanzo_mcp/__init__.py +6 -0
  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.7.dist-info → hanzo_mcp-0.8.1.dist-info}/METADATA +1 -1
  174. hanzo_mcp-0.8.1.dist-info/RECORD +185 -0
  175. hanzo_mcp-0.7.7.dist-info/RECORD +0 -182
  176. {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.1.dist-info}/WHEEL +0 -0
  177. {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.1.dist-info}/entry_points.txt +0 -0
  178. {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.1.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,15 @@
1
1
  """Graph remove tool for removing nodes and edges from the graph database."""
2
2
 
3
- import json
4
- from typing import Annotated, Optional, TypedDict, Unpack, final, override
3
+ from typing import Unpack, Optional, Annotated, TypedDict, final, override
5
4
 
6
- from mcp.server.fastmcp import Context as MCPContext
7
5
  from pydantic import Field
6
+ from mcp.server.fastmcp import Context as MCPContext
8
7
 
9
8
  from hanzo_mcp.tools.common.base import BaseTool
10
9
  from hanzo_mcp.tools.common.context import create_tool_context
11
10
  from hanzo_mcp.tools.common.permissions import PermissionManager
12
11
  from hanzo_mcp.tools.database.database_manager import DatabaseManager
13
12
 
14
-
15
13
  NodeId = Annotated[
16
14
  Optional[str],
17
15
  Field(
@@ -76,7 +74,9 @@ class GraphRemoveParams(TypedDict, total=False):
76
74
  class GraphRemoveTool(BaseTool):
77
75
  """Tool for removing nodes and edges from graph database."""
78
76
 
79
- def __init__(self, permission_manager: PermissionManager, db_manager: DatabaseManager):
77
+ def __init__(
78
+ self, permission_manager: PermissionManager, db_manager: DatabaseManager
79
+ ):
80
80
  """Initialize the graph remove tool.
81
81
 
82
82
  Args:
@@ -159,8 +159,9 @@ Examples:
159
159
  project_db = self.db_manager.get_project_db(project_path)
160
160
  else:
161
161
  import os
162
+
162
163
  project_db = self.db_manager.get_project_for_path(os.getcwd())
163
-
164
+
164
165
  if not project_db:
165
166
  return "Error: Could not find project database"
166
167
 
@@ -187,75 +188,79 @@ Examples:
187
188
  # Count edges that will be removed
188
189
  cursor.execute(
189
190
  "SELECT COUNT(*) FROM edges WHERE source = ? OR target = ?",
190
- (node_id, node_id)
191
+ (node_id, node_id),
191
192
  )
192
193
  edge_count = cursor.fetchone()[0]
193
-
194
+
194
195
  # Remove connected edges
195
196
  graph_conn.execute(
196
197
  "DELETE FROM edges WHERE source = ? OR target = ?",
197
- (node_id, node_id)
198
+ (node_id, node_id),
198
199
  )
199
-
200
+
200
201
  # Remove node
201
202
  graph_conn.execute("DELETE FROM nodes WHERE id = ?", (node_id,))
202
203
  graph_conn.commit()
203
-
204
+
204
205
  # Save to disk
205
206
  project_db._save_graph_to_disk()
206
-
207
+
207
208
  return f"Successfully removed node '{node_id}' and {edge_count} connected edge(s)"
208
209
  else:
209
210
  # Remove node only
210
211
  graph_conn.execute("DELETE FROM nodes WHERE id = ?", (node_id,))
211
212
  graph_conn.commit()
212
-
213
+
213
214
  # Save to disk
214
215
  project_db._save_graph_to_disk()
215
-
216
+
216
217
  return f"Successfully removed node '{node_id}' (edges preserved)"
217
218
 
218
219
  else:
219
220
  # Remove edge(s)
220
221
  if relationship:
221
222
  # Remove specific edge
222
- await tool_ctx.info(f"Removing edge: {source} --[{relationship}]--> {target}")
223
-
223
+ await tool_ctx.info(
224
+ f"Removing edge: {source} --[{relationship}]--> {target}"
225
+ )
226
+
224
227
  cursor = graph_conn.cursor()
225
228
  cursor.execute(
226
229
  "DELETE FROM edges WHERE source = ? AND target = ? AND relationship = ?",
227
- (source, target, relationship)
230
+ (source, target, relationship),
228
231
  )
229
-
232
+
230
233
  removed = cursor.rowcount
231
234
  graph_conn.commit()
232
-
235
+
233
236
  if removed == 0:
234
237
  return f"No edge found: {source} --[{relationship}]--> {target}"
235
-
238
+
236
239
  # Save to disk
237
240
  project_db._save_graph_to_disk()
238
-
241
+
239
242
  return f"Successfully removed edge: {source} --[{relationship}]--> {target}"
240
243
  else:
241
244
  # Remove all edges between nodes
242
- await tool_ctx.info(f"Removing all edges between {source} and {target}")
243
-
245
+ await tool_ctx.info(
246
+ f"Removing all edges between {source} and {target}"
247
+ )
248
+
244
249
  cursor = graph_conn.cursor()
245
250
  cursor.execute(
246
251
  "DELETE FROM edges WHERE source = ? AND target = ?",
247
- (source, target)
252
+ (source, target),
248
253
  )
249
-
254
+
250
255
  removed = cursor.rowcount
251
256
  graph_conn.commit()
252
-
257
+
253
258
  if removed == 0:
254
259
  return f"No edges found between '{source}' and '{target}'"
255
-
260
+
256
261
  # Save to disk
257
262
  project_db._save_graph_to_disk()
258
-
263
+
259
264
  return f"Successfully removed {removed} edge(s) between '{source}' and '{target}'"
260
265
 
261
266
  except Exception as e:
@@ -2,17 +2,16 @@
2
2
 
3
3
  import json
4
4
  import sqlite3
5
- from typing import Annotated, Optional, TypedDict, Unpack, final, override
5
+ from typing import Unpack, Optional, Annotated, TypedDict, final, override
6
6
 
7
- from mcp.server.fastmcp import Context as MCPContext
8
7
  from pydantic import Field
8
+ from mcp.server.fastmcp import Context as MCPContext
9
9
 
10
10
  from hanzo_mcp.tools.common.base import BaseTool
11
11
  from hanzo_mcp.tools.common.context import create_tool_context
12
12
  from hanzo_mcp.tools.common.permissions import PermissionManager
13
13
  from hanzo_mcp.tools.database.database_manager import DatabaseManager
14
14
 
15
-
16
15
  Pattern = Annotated[
17
16
  str,
18
17
  Field(
@@ -77,7 +76,9 @@ class GraphSearchParams(TypedDict, total=False):
77
76
  class GraphSearchTool(BaseTool):
78
77
  """Tool for searching nodes and edges in graph database."""
79
78
 
80
- def __init__(self, permission_manager: PermissionManager, db_manager: DatabaseManager):
79
+ def __init__(
80
+ self, permission_manager: PermissionManager, db_manager: DatabaseManager
81
+ ):
81
82
  """Initialize the graph search tool.
82
83
 
83
84
  Args:
@@ -157,8 +158,9 @@ Examples:
157
158
  project_db = self.db_manager.get_project_db(project_path)
158
159
  else:
159
160
  import os
161
+
160
162
  project_db = self.db_manager.get_project_for_path(os.getcwd())
161
-
163
+
162
164
  if not project_db:
163
165
  return "Error: Could not find project database"
164
166
 
@@ -175,154 +177,174 @@ Examples:
175
177
 
176
178
  try:
177
179
  cursor = graph_conn.cursor()
178
-
180
+
179
181
  # Search nodes
180
182
  if search_type in ["nodes", "all"]:
181
183
  query = "SELECT id, type, properties FROM nodes WHERE id LIKE ?"
182
184
  params_list = [pattern]
183
-
185
+
184
186
  if node_type:
185
187
  query += " AND type = ?"
186
188
  params_list.append(node_type)
187
-
189
+
188
190
  if search_type == "nodes":
189
191
  query += f" LIMIT {max_results}"
190
-
192
+
191
193
  cursor.execute(query, params_list)
192
-
194
+
193
195
  for row in cursor.fetchall():
194
- results.append({
195
- "type": "node",
196
- "id": row[0],
197
- "node_type": row[1],
198
- "properties": json.loads(row[2]) if row[2] else {},
199
- "match_field": "id"
200
- })
201
-
196
+ results.append(
197
+ {
198
+ "type": "node",
199
+ "id": row[0],
200
+ "node_type": row[1],
201
+ "properties": json.loads(row[2]) if row[2] else {},
202
+ "match_field": "id",
203
+ }
204
+ )
205
+
202
206
  # Search edges
203
207
  if search_type in ["edges", "all"]:
204
208
  query = """SELECT source, target, relationship, weight, properties
205
209
  FROM edges WHERE relationship LIKE ?"""
206
210
  params_list = [pattern]
207
-
211
+
208
212
  if relationship:
209
213
  query += " AND relationship = ?"
210
214
  params_list.append(relationship)
211
-
215
+
212
216
  if search_type == "edges":
213
217
  query += f" LIMIT {max_results}"
214
-
218
+
215
219
  cursor.execute(query, params_list)
216
-
220
+
217
221
  for row in cursor.fetchall():
218
- results.append({
219
- "type": "edge",
220
- "source": row[0],
221
- "target": row[1],
222
- "relationship": row[2],
223
- "weight": row[3],
224
- "properties": json.loads(row[4]) if row[4] else {},
225
- "match_field": "relationship"
226
- })
227
-
222
+ results.append(
223
+ {
224
+ "type": "edge",
225
+ "source": row[0],
226
+ "target": row[1],
227
+ "relationship": row[2],
228
+ "weight": row[3],
229
+ "properties": json.loads(row[4]) if row[4] else {},
230
+ "match_field": "relationship",
231
+ }
232
+ )
233
+
228
234
  # Search in properties
229
235
  if search_type in ["properties", "all"]:
230
236
  # Search node properties
231
237
  query = """SELECT id, type, properties FROM nodes
232
238
  WHERE properties IS NOT NULL AND properties LIKE ?"""
233
239
  params_list = [f"%{pattern}%"]
234
-
240
+
235
241
  if node_type:
236
242
  query += " AND type = ?"
237
243
  params_list.append(node_type)
238
-
244
+
239
245
  cursor.execute(query, params_list)
240
-
246
+
241
247
  for row in cursor.fetchall():
242
248
  props = json.loads(row[2]) if row[2] else {}
243
249
  # Check which property matches
244
250
  matching_props = {}
245
251
  for key, value in props.items():
246
- if pattern.replace('%', '').lower() in str(value).lower():
252
+ if pattern.replace("%", "").lower() in str(value).lower():
247
253
  matching_props[key] = value
248
-
254
+
249
255
  if matching_props:
250
- results.append({
251
- "type": "node",
252
- "id": row[0],
253
- "node_type": row[1],
254
- "properties": props,
255
- "match_field": "properties",
256
- "matching_properties": matching_props
257
- })
258
-
256
+ results.append(
257
+ {
258
+ "type": "node",
259
+ "id": row[0],
260
+ "node_type": row[1],
261
+ "properties": props,
262
+ "match_field": "properties",
263
+ "matching_properties": matching_props,
264
+ }
265
+ )
266
+
259
267
  # Search edge properties
260
268
  query = """SELECT source, target, relationship, weight, properties
261
269
  FROM edges WHERE properties IS NOT NULL AND properties LIKE ?"""
262
270
  params_list = [f"%{pattern}%"]
263
-
271
+
264
272
  if relationship:
265
273
  query += " AND relationship = ?"
266
274
  params_list.append(relationship)
267
-
275
+
268
276
  cursor.execute(query, params_list)
269
-
277
+
270
278
  for row in cursor.fetchall():
271
279
  props = json.loads(row[4]) if row[4] else {}
272
280
  # Check which property matches
273
281
  matching_props = {}
274
282
  for key, value in props.items():
275
- if pattern.replace('%', '').lower() in str(value).lower():
283
+ if pattern.replace("%", "").lower() in str(value).lower():
276
284
  matching_props[key] = value
277
-
285
+
278
286
  if matching_props:
279
- results.append({
280
- "type": "edge",
281
- "source": row[0],
282
- "target": row[1],
283
- "relationship": row[2],
284
- "weight": row[3],
285
- "properties": props,
286
- "match_field": "properties",
287
- "matching_properties": matching_props
288
- })
289
-
287
+ results.append(
288
+ {
289
+ "type": "edge",
290
+ "source": row[0],
291
+ "target": row[1],
292
+ "relationship": row[2],
293
+ "weight": row[3],
294
+ "properties": props,
295
+ "match_field": "properties",
296
+ "matching_properties": matching_props,
297
+ }
298
+ )
299
+
290
300
  # Limit total results if searching all
291
301
  if search_type == "all" and len(results) > max_results:
292
302
  results = results[:max_results]
293
-
303
+
294
304
  if not results:
295
305
  return f"No results found for pattern '{pattern}'"
296
-
306
+
297
307
  # Format results
298
308
  output = [f"Found {len(results)} result(s) for pattern '{pattern}':\n"]
299
-
309
+
300
310
  # Group by type
301
311
  nodes = [r for r in results if r["type"] == "node"]
302
312
  edges = [r for r in results if r["type"] == "edge"]
303
-
313
+
304
314
  if nodes:
305
315
  output.append(f"Nodes ({len(nodes)}):")
306
316
  for node in nodes[:20]: # Show first 20
307
317
  output.append(f" {node['id']} ({node['node_type']})")
308
- if node["match_field"] == "properties" and "matching_properties" in node:
309
- output.append(f" Matched in: {list(node['matching_properties'].keys())}")
318
+ if (
319
+ node["match_field"] == "properties"
320
+ and "matching_properties" in node
321
+ ):
322
+ output.append(
323
+ f" Matched in: {list(node['matching_properties'].keys())}"
324
+ )
310
325
  if node["properties"] and node["match_field"] != "properties":
311
326
  props_str = json.dumps(node["properties"], indent=6)[:100]
312
327
  if len(props_str) == 100:
313
328
  props_str += "..."
314
329
  output.append(f" Properties: {props_str}")
315
-
330
+
316
331
  if len(nodes) > 20:
317
332
  output.append(f" ... and {len(nodes) - 20} more nodes")
318
333
  output.append("")
319
-
334
+
320
335
  if edges:
321
336
  output.append(f"Edges ({len(edges)}):")
322
337
  for edge in edges[:20]: # Show first 20
323
- output.append(f" {edge['source']} --[{edge['relationship']}]--> {edge['target']}")
324
- if edge["match_field"] == "properties" and "matching_properties" in edge:
325
- output.append(f" Matched in: {list(edge['matching_properties'].keys())}")
338
+ output.append(
339
+ f" {edge['source']} --[{edge['relationship']}]--> {edge['target']}"
340
+ )
341
+ if (
342
+ edge["match_field"] == "properties"
343
+ and "matching_properties" in edge
344
+ ):
345
+ output.append(
346
+ f" Matched in: {list(edge['matching_properties'].keys())}"
347
+ )
326
348
  if edge["weight"] != 1.0:
327
349
  output.append(f" Weight: {edge['weight']}")
328
350
  if edge["properties"]:
@@ -330,10 +352,10 @@ Examples:
330
352
  if len(props_str) == 100:
331
353
  props_str += "..."
332
354
  output.append(f" Properties: {props_str}")
333
-
355
+
334
356
  if len(edges) > 20:
335
357
  output.append(f" ... and {len(edges) - 20} more edges")
336
-
358
+
337
359
  return "\n".join(output)
338
360
 
339
361
  except sqlite3.Error as e: