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.
- hanzo_mcp/__init__.py +7 -1
- hanzo_mcp/__main__.py +1 -1
- hanzo_mcp/analytics/__init__.py +2 -2
- hanzo_mcp/analytics/posthog_analytics.py +76 -82
- hanzo_mcp/cli.py +31 -36
- hanzo_mcp/cli_enhanced.py +94 -72
- hanzo_mcp/cli_plugin.py +27 -17
- hanzo_mcp/config/__init__.py +2 -2
- hanzo_mcp/config/settings.py +112 -88
- hanzo_mcp/config/tool_config.py +32 -34
- hanzo_mcp/dev_server.py +66 -67
- hanzo_mcp/prompts/__init__.py +94 -12
- hanzo_mcp/prompts/enhanced_prompts.py +809 -0
- hanzo_mcp/prompts/example_custom_prompt.py +6 -5
- hanzo_mcp/prompts/project_todo_reminder.py +0 -1
- hanzo_mcp/prompts/tool_explorer.py +10 -7
- hanzo_mcp/server.py +17 -21
- hanzo_mcp/server_enhanced.py +15 -22
- hanzo_mcp/tools/__init__.py +56 -28
- hanzo_mcp/tools/agent/__init__.py +16 -19
- hanzo_mcp/tools/agent/agent.py +82 -65
- hanzo_mcp/tools/agent/agent_tool.py +152 -122
- hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +66 -62
- hanzo_mcp/tools/agent/clarification_protocol.py +55 -50
- hanzo_mcp/tools/agent/clarification_tool.py +11 -10
- hanzo_mcp/tools/agent/claude_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/claude_desktop_auth.py +130 -144
- hanzo_mcp/tools/agent/cli_agent_base.py +59 -53
- hanzo_mcp/tools/agent/code_auth.py +102 -107
- hanzo_mcp/tools/agent/code_auth_tool.py +28 -27
- hanzo_mcp/tools/agent/codex_cli_tool.py +20 -19
- hanzo_mcp/tools/agent/critic_tool.py +86 -73
- hanzo_mcp/tools/agent/gemini_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/grok_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/iching_tool.py +404 -139
- hanzo_mcp/tools/agent/network_tool.py +89 -73
- hanzo_mcp/tools/agent/prompt.py +2 -1
- hanzo_mcp/tools/agent/review_tool.py +101 -98
- hanzo_mcp/tools/agent/swarm_alias.py +87 -0
- hanzo_mcp/tools/agent/swarm_tool.py +246 -161
- hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +134 -92
- hanzo_mcp/tools/agent/tool_adapter.py +21 -11
- hanzo_mcp/tools/common/__init__.py +1 -1
- hanzo_mcp/tools/common/base.py +3 -5
- hanzo_mcp/tools/common/batch_tool.py +46 -39
- hanzo_mcp/tools/common/config_tool.py +120 -84
- hanzo_mcp/tools/common/context.py +1 -5
- hanzo_mcp/tools/common/context_fix.py +5 -3
- hanzo_mcp/tools/common/critic_tool.py +4 -8
- hanzo_mcp/tools/common/decorators.py +58 -56
- hanzo_mcp/tools/common/enhanced_base.py +29 -32
- hanzo_mcp/tools/common/fastmcp_pagination.py +91 -94
- hanzo_mcp/tools/common/forgiving_edit.py +91 -87
- hanzo_mcp/tools/common/mode.py +15 -17
- hanzo_mcp/tools/common/mode_loader.py +27 -24
- hanzo_mcp/tools/common/paginated_base.py +61 -53
- hanzo_mcp/tools/common/paginated_response.py +72 -79
- hanzo_mcp/tools/common/pagination.py +50 -53
- hanzo_mcp/tools/common/permissions.py +4 -4
- hanzo_mcp/tools/common/personality.py +186 -138
- hanzo_mcp/tools/common/plugin_loader.py +54 -54
- hanzo_mcp/tools/common/stats.py +65 -47
- hanzo_mcp/tools/common/test_helpers.py +31 -0
- hanzo_mcp/tools/common/thinking_tool.py +4 -8
- hanzo_mcp/tools/common/tool_disable.py +17 -12
- hanzo_mcp/tools/common/tool_enable.py +13 -14
- hanzo_mcp/tools/common/tool_list.py +36 -28
- hanzo_mcp/tools/common/truncate.py +23 -23
- hanzo_mcp/tools/config/__init__.py +4 -4
- hanzo_mcp/tools/config/config_tool.py +42 -29
- hanzo_mcp/tools/config/index_config.py +37 -34
- hanzo_mcp/tools/config/mode_tool.py +175 -55
- hanzo_mcp/tools/database/__init__.py +15 -12
- hanzo_mcp/tools/database/database_manager.py +77 -75
- hanzo_mcp/tools/database/graph.py +137 -91
- hanzo_mcp/tools/database/graph_add.py +30 -18
- hanzo_mcp/tools/database/graph_query.py +178 -102
- hanzo_mcp/tools/database/graph_remove.py +33 -28
- hanzo_mcp/tools/database/graph_search.py +97 -75
- hanzo_mcp/tools/database/graph_stats.py +91 -59
- hanzo_mcp/tools/database/sql.py +107 -79
- hanzo_mcp/tools/database/sql_query.py +30 -24
- hanzo_mcp/tools/database/sql_search.py +29 -25
- hanzo_mcp/tools/database/sql_stats.py +47 -35
- hanzo_mcp/tools/editor/neovim_command.py +25 -28
- hanzo_mcp/tools/editor/neovim_edit.py +21 -23
- hanzo_mcp/tools/editor/neovim_session.py +60 -54
- hanzo_mcp/tools/filesystem/__init__.py +31 -30
- hanzo_mcp/tools/filesystem/ast_multi_edit.py +329 -249
- hanzo_mcp/tools/filesystem/ast_tool.py +4 -4
- hanzo_mcp/tools/filesystem/base.py +1 -1
- hanzo_mcp/tools/filesystem/batch_search.py +316 -224
- hanzo_mcp/tools/filesystem/content_replace.py +4 -4
- hanzo_mcp/tools/filesystem/diff.py +71 -59
- hanzo_mcp/tools/filesystem/directory_tree.py +7 -7
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +49 -37
- hanzo_mcp/tools/filesystem/edit.py +4 -4
- hanzo_mcp/tools/filesystem/find.py +173 -80
- hanzo_mcp/tools/filesystem/find_files.py +73 -52
- hanzo_mcp/tools/filesystem/git_search.py +157 -104
- hanzo_mcp/tools/filesystem/grep.py +8 -8
- hanzo_mcp/tools/filesystem/multi_edit.py +4 -8
- hanzo_mcp/tools/filesystem/read.py +12 -10
- hanzo_mcp/tools/filesystem/rules_tool.py +59 -43
- hanzo_mcp/tools/filesystem/search_tool.py +263 -207
- hanzo_mcp/tools/filesystem/symbols_tool.py +94 -54
- hanzo_mcp/tools/filesystem/tree.py +35 -33
- hanzo_mcp/tools/filesystem/unix_aliases.py +13 -18
- hanzo_mcp/tools/filesystem/watch.py +37 -36
- hanzo_mcp/tools/filesystem/write.py +4 -8
- hanzo_mcp/tools/jupyter/__init__.py +4 -4
- hanzo_mcp/tools/jupyter/base.py +4 -5
- hanzo_mcp/tools/jupyter/jupyter.py +67 -47
- hanzo_mcp/tools/jupyter/notebook_edit.py +4 -4
- hanzo_mcp/tools/jupyter/notebook_read.py +4 -7
- hanzo_mcp/tools/llm/__init__.py +5 -7
- hanzo_mcp/tools/llm/consensus_tool.py +72 -52
- hanzo_mcp/tools/llm/llm_manage.py +101 -60
- hanzo_mcp/tools/llm/llm_tool.py +226 -166
- hanzo_mcp/tools/llm/provider_tools.py +25 -26
- hanzo_mcp/tools/lsp/__init__.py +1 -1
- hanzo_mcp/tools/lsp/lsp_tool.py +228 -143
- hanzo_mcp/tools/mcp/__init__.py +2 -3
- hanzo_mcp/tools/mcp/mcp_add.py +27 -25
- hanzo_mcp/tools/mcp/mcp_remove.py +7 -8
- hanzo_mcp/tools/mcp/mcp_stats.py +23 -22
- hanzo_mcp/tools/mcp/mcp_tool.py +129 -98
- hanzo_mcp/tools/memory/__init__.py +39 -21
- hanzo_mcp/tools/memory/knowledge_tools.py +124 -99
- hanzo_mcp/tools/memory/memory_tools.py +90 -108
- hanzo_mcp/tools/search/__init__.py +7 -2
- hanzo_mcp/tools/search/find_tool.py +297 -212
- hanzo_mcp/tools/search/unified_search.py +366 -314
- hanzo_mcp/tools/shell/__init__.py +8 -7
- hanzo_mcp/tools/shell/auto_background.py +56 -49
- hanzo_mcp/tools/shell/base.py +1 -1
- hanzo_mcp/tools/shell/base_process.py +75 -75
- hanzo_mcp/tools/shell/bash_session.py +2 -2
- hanzo_mcp/tools/shell/bash_session_executor.py +4 -4
- hanzo_mcp/tools/shell/bash_tool.py +24 -31
- hanzo_mcp/tools/shell/command_executor.py +12 -12
- hanzo_mcp/tools/shell/logs.py +43 -33
- hanzo_mcp/tools/shell/npx.py +13 -13
- hanzo_mcp/tools/shell/npx_background.py +24 -21
- hanzo_mcp/tools/shell/npx_tool.py +18 -22
- hanzo_mcp/tools/shell/open.py +19 -21
- hanzo_mcp/tools/shell/pkill.py +31 -26
- hanzo_mcp/tools/shell/process_tool.py +32 -32
- hanzo_mcp/tools/shell/processes.py +57 -58
- hanzo_mcp/tools/shell/run_background.py +24 -25
- hanzo_mcp/tools/shell/run_command.py +5 -5
- hanzo_mcp/tools/shell/run_command_windows.py +5 -5
- hanzo_mcp/tools/shell/session_storage.py +3 -3
- hanzo_mcp/tools/shell/streaming_command.py +141 -126
- hanzo_mcp/tools/shell/uvx.py +24 -25
- hanzo_mcp/tools/shell/uvx_background.py +35 -33
- hanzo_mcp/tools/shell/uvx_tool.py +18 -22
- hanzo_mcp/tools/todo/__init__.py +6 -2
- hanzo_mcp/tools/todo/todo.py +50 -37
- hanzo_mcp/tools/todo/todo_read.py +5 -8
- hanzo_mcp/tools/todo/todo_write.py +5 -7
- hanzo_mcp/tools/vector/__init__.py +40 -28
- hanzo_mcp/tools/vector/ast_analyzer.py +176 -143
- hanzo_mcp/tools/vector/git_ingester.py +170 -179
- hanzo_mcp/tools/vector/index_tool.py +96 -44
- hanzo_mcp/tools/vector/infinity_store.py +283 -228
- hanzo_mcp/tools/vector/mock_infinity.py +39 -40
- hanzo_mcp/tools/vector/project_manager.py +88 -78
- hanzo_mcp/tools/vector/vector.py +59 -42
- hanzo_mcp/tools/vector/vector_index.py +30 -27
- hanzo_mcp/tools/vector/vector_search.py +64 -45
- hanzo_mcp/types.py +6 -4
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/METADATA +1 -1
- hanzo_mcp-0.8.0.dist-info/RECORD +185 -0
- hanzo_mcp-0.7.6.dist-info/RECORD +0 -182
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.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
|
|
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__(
|
|
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(
|
|
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(
|
|
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
|
|
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__(
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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(
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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(
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
|
309
|
-
|
|
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(
|
|
324
|
-
|
|
325
|
-
|
|
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:
|