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.
Files changed (65) hide show
  1. memorygraph/__init__.py +50 -0
  2. memorygraph/__main__.py +12 -0
  3. memorygraph/advanced_tools.py +509 -0
  4. memorygraph/analytics/__init__.py +46 -0
  5. memorygraph/analytics/advanced_queries.py +727 -0
  6. memorygraph/backends/__init__.py +21 -0
  7. memorygraph/backends/base.py +179 -0
  8. memorygraph/backends/cloud.py +75 -0
  9. memorygraph/backends/cloud_backend.py +858 -0
  10. memorygraph/backends/factory.py +577 -0
  11. memorygraph/backends/falkordb_backend.py +749 -0
  12. memorygraph/backends/falkordblite_backend.py +746 -0
  13. memorygraph/backends/ladybugdb_backend.py +242 -0
  14. memorygraph/backends/memgraph_backend.py +327 -0
  15. memorygraph/backends/neo4j_backend.py +298 -0
  16. memorygraph/backends/sqlite_fallback.py +463 -0
  17. memorygraph/backends/turso.py +448 -0
  18. memorygraph/cli.py +743 -0
  19. memorygraph/cloud_database.py +297 -0
  20. memorygraph/config.py +295 -0
  21. memorygraph/database.py +933 -0
  22. memorygraph/graph_analytics.py +631 -0
  23. memorygraph/integration/__init__.py +69 -0
  24. memorygraph/integration/context_capture.py +426 -0
  25. memorygraph/integration/project_analysis.py +583 -0
  26. memorygraph/integration/workflow_tracking.py +492 -0
  27. memorygraph/intelligence/__init__.py +59 -0
  28. memorygraph/intelligence/context_retrieval.py +447 -0
  29. memorygraph/intelligence/entity_extraction.py +386 -0
  30. memorygraph/intelligence/pattern_recognition.py +420 -0
  31. memorygraph/intelligence/temporal.py +374 -0
  32. memorygraph/migration/__init__.py +27 -0
  33. memorygraph/migration/manager.py +579 -0
  34. memorygraph/migration/models.py +142 -0
  35. memorygraph/migration/scripts/__init__.py +17 -0
  36. memorygraph/migration/scripts/bitemporal_migration.py +595 -0
  37. memorygraph/migration/scripts/multitenancy_migration.py +452 -0
  38. memorygraph/migration_tools_module.py +146 -0
  39. memorygraph/models.py +684 -0
  40. memorygraph/proactive/__init__.py +46 -0
  41. memorygraph/proactive/outcome_learning.py +444 -0
  42. memorygraph/proactive/predictive.py +410 -0
  43. memorygraph/proactive/session_briefing.py +399 -0
  44. memorygraph/relationships.py +668 -0
  45. memorygraph/server.py +883 -0
  46. memorygraph/sqlite_database.py +1876 -0
  47. memorygraph/tools/__init__.py +59 -0
  48. memorygraph/tools/activity_tools.py +262 -0
  49. memorygraph/tools/memory_tools.py +315 -0
  50. memorygraph/tools/migration_tools.py +181 -0
  51. memorygraph/tools/relationship_tools.py +147 -0
  52. memorygraph/tools/search_tools.py +406 -0
  53. memorygraph/tools/temporal_tools.py +339 -0
  54. memorygraph/utils/__init__.py +10 -0
  55. memorygraph/utils/context_extractor.py +429 -0
  56. memorygraph/utils/error_handling.py +151 -0
  57. memorygraph/utils/export_import.py +425 -0
  58. memorygraph/utils/graph_algorithms.py +200 -0
  59. memorygraph/utils/pagination.py +149 -0
  60. memorygraph/utils/project_detection.py +133 -0
  61. memorygraphmcp-0.11.7.dist-info/METADATA +970 -0
  62. memorygraphmcp-0.11.7.dist-info/RECORD +65 -0
  63. memorygraphmcp-0.11.7.dist-info/WHEEL +4 -0
  64. memorygraphmcp-0.11.7.dist-info/entry_points.txt +2 -0
  65. 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"]