memorygraphMCP 0.11.7__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.
- memorygraph/__init__.py +50 -0
- memorygraph/__main__.py +12 -0
- memorygraph/advanced_tools.py +509 -0
- memorygraph/analytics/__init__.py +46 -0
- memorygraph/analytics/advanced_queries.py +727 -0
- memorygraph/backends/__init__.py +21 -0
- memorygraph/backends/base.py +179 -0
- memorygraph/backends/cloud.py +75 -0
- memorygraph/backends/cloud_backend.py +858 -0
- memorygraph/backends/factory.py +577 -0
- memorygraph/backends/falkordb_backend.py +749 -0
- memorygraph/backends/falkordblite_backend.py +746 -0
- memorygraph/backends/ladybugdb_backend.py +242 -0
- memorygraph/backends/memgraph_backend.py +327 -0
- memorygraph/backends/neo4j_backend.py +298 -0
- memorygraph/backends/sqlite_fallback.py +463 -0
- memorygraph/backends/turso.py +448 -0
- memorygraph/cli.py +743 -0
- memorygraph/cloud_database.py +297 -0
- memorygraph/config.py +295 -0
- memorygraph/database.py +933 -0
- memorygraph/graph_analytics.py +631 -0
- memorygraph/integration/__init__.py +69 -0
- memorygraph/integration/context_capture.py +426 -0
- memorygraph/integration/project_analysis.py +583 -0
- memorygraph/integration/workflow_tracking.py +492 -0
- memorygraph/intelligence/__init__.py +59 -0
- memorygraph/intelligence/context_retrieval.py +447 -0
- memorygraph/intelligence/entity_extraction.py +386 -0
- memorygraph/intelligence/pattern_recognition.py +420 -0
- memorygraph/intelligence/temporal.py +374 -0
- memorygraph/migration/__init__.py +27 -0
- memorygraph/migration/manager.py +579 -0
- memorygraph/migration/models.py +142 -0
- memorygraph/migration/scripts/__init__.py +17 -0
- memorygraph/migration/scripts/bitemporal_migration.py +595 -0
- memorygraph/migration/scripts/multitenancy_migration.py +452 -0
- memorygraph/migration_tools_module.py +146 -0
- memorygraph/models.py +684 -0
- memorygraph/proactive/__init__.py +46 -0
- memorygraph/proactive/outcome_learning.py +444 -0
- memorygraph/proactive/predictive.py +410 -0
- memorygraph/proactive/session_briefing.py +399 -0
- memorygraph/relationships.py +668 -0
- memorygraph/server.py +883 -0
- memorygraph/sqlite_database.py +1876 -0
- memorygraph/tools/__init__.py +59 -0
- memorygraph/tools/activity_tools.py +262 -0
- memorygraph/tools/memory_tools.py +315 -0
- memorygraph/tools/migration_tools.py +181 -0
- memorygraph/tools/relationship_tools.py +147 -0
- memorygraph/tools/search_tools.py +406 -0
- memorygraph/tools/temporal_tools.py +339 -0
- memorygraph/utils/__init__.py +10 -0
- memorygraph/utils/context_extractor.py +429 -0
- memorygraph/utils/error_handling.py +151 -0
- memorygraph/utils/export_import.py +425 -0
- memorygraph/utils/graph_algorithms.py +200 -0
- memorygraph/utils/pagination.py +149 -0
- memorygraph/utils/project_detection.py +133 -0
- memorygraphmcp-0.11.7.dist-info/METADATA +970 -0
- memorygraphmcp-0.11.7.dist-info/RECORD +65 -0
- memorygraphmcp-0.11.7.dist-info/WHEEL +4 -0
- memorygraphmcp-0.11.7.dist-info/entry_points.txt +2 -0
- memorygraphmcp-0.11.7.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Temporal tool handlers for bi-temporal queries.
|
|
3
|
+
|
|
4
|
+
This module contains handlers for temporal operations:
|
|
5
|
+
- query_as_of: Query relationships as they existed at a specific time
|
|
6
|
+
- get_relationship_history: Get full history of relationships for a memory
|
|
7
|
+
- what_changed: Show relationship changes since a specific time
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from typing import Any, Dict, List, TypedDict, Optional
|
|
14
|
+
|
|
15
|
+
from mcp.types import CallToolResult, TextContent
|
|
16
|
+
|
|
17
|
+
from ..database import MemoryDatabase
|
|
18
|
+
from ..models import RelationshipType
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class QueryAsOfArgs(TypedDict, total=False):
|
|
22
|
+
"""Arguments for query_as_of tool."""
|
|
23
|
+
memory_id: str
|
|
24
|
+
as_of: str
|
|
25
|
+
relationship_types: Optional[List[str]]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class GetRelationshipHistoryArgs(TypedDict, total=False):
|
|
29
|
+
"""Arguments for get_relationship_history tool."""
|
|
30
|
+
memory_id: str
|
|
31
|
+
relationship_types: Optional[List[str]]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class WhatChangedArgs(TypedDict, total=False):
|
|
35
|
+
"""Arguments for what_changed tool."""
|
|
36
|
+
since: str
|
|
37
|
+
|
|
38
|
+
logger = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async def handle_query_as_of(
|
|
42
|
+
memory_db: MemoryDatabase,
|
|
43
|
+
arguments: QueryAsOfArgs
|
|
44
|
+
) -> CallToolResult:
|
|
45
|
+
"""Handle query_as_of tool call for point-in-time queries.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
memory_db: Database instance for memory operations
|
|
49
|
+
arguments: Tool arguments from MCP call containing:
|
|
50
|
+
- memory_id: ID of memory to query
|
|
51
|
+
- as_of: ISO 8601 timestamp (e.g., "2024-12-01T00:00:00Z")
|
|
52
|
+
- relationship_types: Optional list of relationship types to filter
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
CallToolResult with relationships valid at that time or error message
|
|
56
|
+
"""
|
|
57
|
+
try:
|
|
58
|
+
memory_id = arguments["memory_id"]
|
|
59
|
+
as_of_str = arguments["as_of"]
|
|
60
|
+
|
|
61
|
+
# Check if memory exists
|
|
62
|
+
memory = await memory_db.get_memory(memory_id)
|
|
63
|
+
if not memory:
|
|
64
|
+
return CallToolResult(
|
|
65
|
+
content=[TextContent(
|
|
66
|
+
type="text",
|
|
67
|
+
text=f"Memory not found: {memory_id}"
|
|
68
|
+
)],
|
|
69
|
+
isError=True
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Parse ISO 8601 timestamp
|
|
73
|
+
try:
|
|
74
|
+
as_of = datetime.fromisoformat(as_of_str.replace('Z', '+00:00'))
|
|
75
|
+
except ValueError:
|
|
76
|
+
return CallToolResult(
|
|
77
|
+
content=[TextContent(
|
|
78
|
+
type="text",
|
|
79
|
+
text=f"Invalid timestamp format. Expected ISO 8601 (e.g., '2024-12-01T00:00:00Z'), got: {as_of_str}"
|
|
80
|
+
)],
|
|
81
|
+
isError=True
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Get optional relationship type filter
|
|
85
|
+
relationship_types = None
|
|
86
|
+
if "relationship_types" in arguments:
|
|
87
|
+
relationship_types = [RelationshipType(t) for t in arguments["relationship_types"]]
|
|
88
|
+
|
|
89
|
+
# Query as of the specified time
|
|
90
|
+
related_memories = await memory_db.get_related_memories(
|
|
91
|
+
memory_id=memory_id,
|
|
92
|
+
relationship_types=relationship_types,
|
|
93
|
+
as_of=as_of
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if not related_memories:
|
|
97
|
+
return CallToolResult(
|
|
98
|
+
content=[TextContent(
|
|
99
|
+
type="text",
|
|
100
|
+
text=f"No relationships found for memory '{memory_id}' as of {as_of_str}"
|
|
101
|
+
)]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Format results
|
|
105
|
+
results_text = f"**Relationships as of {as_of_str}** ({len(related_memories)} found):\n\n"
|
|
106
|
+
for i, (memory, relationship) in enumerate(related_memories, 1):
|
|
107
|
+
results_text += f"**{i}. {memory.title}** (ID: {memory.id})\n"
|
|
108
|
+
results_text += f"Relationship: {relationship.type.value} (strength: {relationship.properties.strength})\n"
|
|
109
|
+
results_text += f"Valid from: {relationship.properties.valid_from.isoformat()}\n"
|
|
110
|
+
if relationship.properties.valid_until:
|
|
111
|
+
results_text += f"Valid until: {relationship.properties.valid_until.isoformat()}\n"
|
|
112
|
+
else:
|
|
113
|
+
results_text += "Valid until: current\n"
|
|
114
|
+
results_text += f"Type: {memory.type.value} | Importance: {memory.importance}\n\n"
|
|
115
|
+
|
|
116
|
+
return CallToolResult(
|
|
117
|
+
content=[TextContent(type="text", text=results_text)]
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
except KeyError as e:
|
|
121
|
+
return CallToolResult(
|
|
122
|
+
content=[TextContent(
|
|
123
|
+
type="text",
|
|
124
|
+
text=f"Missing required field: {e}"
|
|
125
|
+
)],
|
|
126
|
+
isError=True
|
|
127
|
+
)
|
|
128
|
+
except Exception as e:
|
|
129
|
+
logger.error(f"Failed to query as of: {e}")
|
|
130
|
+
return CallToolResult(
|
|
131
|
+
content=[TextContent(
|
|
132
|
+
type="text",
|
|
133
|
+
text=f"Failed to query as of: {e}"
|
|
134
|
+
)],
|
|
135
|
+
isError=True
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
async def handle_get_relationship_history(
|
|
140
|
+
memory_db: MemoryDatabase,
|
|
141
|
+
arguments: GetRelationshipHistoryArgs
|
|
142
|
+
) -> CallToolResult:
|
|
143
|
+
"""Handle get_relationship_history tool call.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
memory_db: Database instance for memory operations
|
|
147
|
+
arguments: Tool arguments from MCP call containing:
|
|
148
|
+
- memory_id: ID of memory to get history for
|
|
149
|
+
- relationship_types: Optional list of relationship types to filter
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
CallToolResult with full relationship history or error message
|
|
153
|
+
"""
|
|
154
|
+
try:
|
|
155
|
+
memory_id = arguments["memory_id"]
|
|
156
|
+
|
|
157
|
+
# Check if memory exists
|
|
158
|
+
memory = await memory_db.get_memory(memory_id)
|
|
159
|
+
if not memory:
|
|
160
|
+
return CallToolResult(
|
|
161
|
+
content=[TextContent(
|
|
162
|
+
type="text",
|
|
163
|
+
text=f"Memory not found: {memory_id}"
|
|
164
|
+
)],
|
|
165
|
+
isError=True
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Get optional relationship type filter
|
|
169
|
+
relationship_types = None
|
|
170
|
+
if "relationship_types" in arguments:
|
|
171
|
+
relationship_types = [RelationshipType(t) for t in arguments["relationship_types"]]
|
|
172
|
+
|
|
173
|
+
# Get full history (including invalidated relationships)
|
|
174
|
+
history = await memory_db.get_relationship_history(
|
|
175
|
+
memory_id=memory_id,
|
|
176
|
+
relationship_types=relationship_types
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if not history:
|
|
180
|
+
return CallToolResult(
|
|
181
|
+
content=[TextContent(
|
|
182
|
+
type="text",
|
|
183
|
+
text=f"No relationship history found for memory: {memory_id}"
|
|
184
|
+
)]
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Format results chronologically
|
|
188
|
+
results_text = f"**Relationship History for {memory_id}** ({len(history)} relationships):\n\n"
|
|
189
|
+
|
|
190
|
+
# Group by current vs invalidated
|
|
191
|
+
current = [r for r in history if r.properties.valid_until is None]
|
|
192
|
+
invalidated = [r for r in history if r.properties.valid_until is not None]
|
|
193
|
+
|
|
194
|
+
if current:
|
|
195
|
+
results_text += "## Current Relationships:\n\n"
|
|
196
|
+
for i, rel in enumerate(current, 1):
|
|
197
|
+
results_text += f"**{i}. {rel.type.value}**\n"
|
|
198
|
+
results_text += f"From: {rel.from_memory_id} → To: {rel.to_memory_id}\n"
|
|
199
|
+
results_text += f"Valid from: {rel.properties.valid_from.isoformat()}\n"
|
|
200
|
+
results_text += f"Strength: {rel.properties.strength} | Confidence: {rel.properties.confidence}\n"
|
|
201
|
+
if rel.properties.context:
|
|
202
|
+
# Context is stored as JSON string, parse it
|
|
203
|
+
try:
|
|
204
|
+
context = json.loads(rel.properties.context)
|
|
205
|
+
if context.get('summary'):
|
|
206
|
+
results_text += f"Context: {context['summary']}\n"
|
|
207
|
+
except (json.JSONDecodeError, KeyError, TypeError):
|
|
208
|
+
# Malformed or non-JSON context, skip
|
|
209
|
+
pass
|
|
210
|
+
results_text += "\n"
|
|
211
|
+
|
|
212
|
+
if invalidated:
|
|
213
|
+
results_text += "## Historical (Invalidated) Relationships:\n\n"
|
|
214
|
+
for i, rel in enumerate(invalidated, 1):
|
|
215
|
+
results_text += f"**{i}. {rel.type.value}**\n"
|
|
216
|
+
results_text += f"From: {rel.from_memory_id} → To: {rel.to_memory_id}\n"
|
|
217
|
+
results_text += f"Valid from: {rel.properties.valid_from.isoformat()}\n"
|
|
218
|
+
results_text += f"Valid until: {rel.properties.valid_until.isoformat()}\n"
|
|
219
|
+
if rel.properties.invalidated_by:
|
|
220
|
+
results_text += f"Superseded by: {rel.properties.invalidated_by}\n"
|
|
221
|
+
results_text += f"Strength: {rel.properties.strength}\n\n"
|
|
222
|
+
|
|
223
|
+
return CallToolResult(
|
|
224
|
+
content=[TextContent(type="text", text=results_text)]
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
except KeyError as e:
|
|
228
|
+
return CallToolResult(
|
|
229
|
+
content=[TextContent(
|
|
230
|
+
type="text",
|
|
231
|
+
text=f"Missing required field: {e}"
|
|
232
|
+
)],
|
|
233
|
+
isError=True
|
|
234
|
+
)
|
|
235
|
+
except Exception as e:
|
|
236
|
+
logger.error(f"Failed to get relationship history: {e}")
|
|
237
|
+
return CallToolResult(
|
|
238
|
+
content=[TextContent(
|
|
239
|
+
type="text",
|
|
240
|
+
text=f"Failed to get relationship history: {e}"
|
|
241
|
+
)],
|
|
242
|
+
isError=True
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
async def handle_what_changed(
|
|
247
|
+
memory_db: MemoryDatabase,
|
|
248
|
+
arguments: WhatChangedArgs
|
|
249
|
+
) -> CallToolResult:
|
|
250
|
+
"""Handle what_changed tool call.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
memory_db: Database instance for memory operations
|
|
254
|
+
arguments: Tool arguments from MCP call containing:
|
|
255
|
+
- since: ISO 8601 timestamp to query from (e.g., "2024-12-01T00:00:00Z")
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
CallToolResult with changes since the specified time or error message
|
|
259
|
+
"""
|
|
260
|
+
try:
|
|
261
|
+
since_str = arguments["since"]
|
|
262
|
+
|
|
263
|
+
# Parse ISO 8601 timestamp
|
|
264
|
+
try:
|
|
265
|
+
since = datetime.fromisoformat(since_str.replace('Z', '+00:00'))
|
|
266
|
+
except ValueError:
|
|
267
|
+
return CallToolResult(
|
|
268
|
+
content=[TextContent(
|
|
269
|
+
type="text",
|
|
270
|
+
text=f"Invalid timestamp format. Expected ISO 8601 (e.g., '2024-12-01T00:00:00Z'), got: {since_str}"
|
|
271
|
+
)],
|
|
272
|
+
isError=True
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# Query what changed
|
|
276
|
+
changes = await memory_db.what_changed(since=since)
|
|
277
|
+
|
|
278
|
+
new_rels = changes.get("new_relationships", [])
|
|
279
|
+
invalidated_rels = changes.get("invalidated_relationships", [])
|
|
280
|
+
|
|
281
|
+
if not new_rels and not invalidated_rels:
|
|
282
|
+
return CallToolResult(
|
|
283
|
+
content=[TextContent(
|
|
284
|
+
type="text",
|
|
285
|
+
text=f"No relationship changes found since {since_str}"
|
|
286
|
+
)]
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
# Format results
|
|
290
|
+
results_text = f"**Changes since {since_str}**:\n\n"
|
|
291
|
+
|
|
292
|
+
if new_rels:
|
|
293
|
+
results_text += f"## New Relationships ({len(new_rels)}):\n\n"
|
|
294
|
+
for i, rel in enumerate(new_rels, 1):
|
|
295
|
+
results_text += f"**{i}. {rel.type.value}**\n"
|
|
296
|
+
results_text += f"From: {rel.from_memory_id} → To: {rel.to_memory_id}\n"
|
|
297
|
+
results_text += f"Recorded at: {rel.properties.recorded_at.isoformat()}\n"
|
|
298
|
+
results_text += f"Strength: {rel.properties.strength}\n"
|
|
299
|
+
if rel.properties.context:
|
|
300
|
+
try:
|
|
301
|
+
context = json.loads(rel.properties.context)
|
|
302
|
+
if context.get('summary'):
|
|
303
|
+
results_text += f"Context: {context['summary']}\n"
|
|
304
|
+
except (json.JSONDecodeError, KeyError, TypeError):
|
|
305
|
+
# Malformed or non-JSON context, skip
|
|
306
|
+
pass
|
|
307
|
+
results_text += "\n"
|
|
308
|
+
|
|
309
|
+
if invalidated_rels:
|
|
310
|
+
results_text += f"## Invalidated Relationships ({len(invalidated_rels)}):\n\n"
|
|
311
|
+
for i, rel in enumerate(invalidated_rels, 1):
|
|
312
|
+
results_text += f"**{i}. {rel.type.value}**\n"
|
|
313
|
+
results_text += f"From: {rel.from_memory_id} → To: {rel.to_memory_id}\n"
|
|
314
|
+
results_text += f"Invalidated at: {rel.properties.valid_until.isoformat()}\n"
|
|
315
|
+
if rel.properties.invalidated_by:
|
|
316
|
+
results_text += f"Superseded by: {rel.properties.invalidated_by}\n"
|
|
317
|
+
results_text += "\n"
|
|
318
|
+
|
|
319
|
+
return CallToolResult(
|
|
320
|
+
content=[TextContent(type="text", text=results_text)]
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
except KeyError as e:
|
|
324
|
+
return CallToolResult(
|
|
325
|
+
content=[TextContent(
|
|
326
|
+
type="text",
|
|
327
|
+
text=f"Missing required field: {e}"
|
|
328
|
+
)],
|
|
329
|
+
isError=True
|
|
330
|
+
)
|
|
331
|
+
except Exception as e:
|
|
332
|
+
logger.error(f"Failed to get what changed: {e}")
|
|
333
|
+
return CallToolResult(
|
|
334
|
+
content=[TextContent(
|
|
335
|
+
type="text",
|
|
336
|
+
text=f"Failed to get what changed: {e}"
|
|
337
|
+
)],
|
|
338
|
+
isError=True
|
|
339
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility modules for MemoryGraph.
|
|
3
|
+
|
|
4
|
+
This package contains utility functions for context extraction and other
|
|
5
|
+
supporting functionality.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .context_extractor import extract_context_structure, parse_context
|
|
9
|
+
|
|
10
|
+
__all__ = ["extract_context_structure", "parse_context"]
|