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,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool handlers for the MCP server.
|
|
3
|
+
|
|
4
|
+
This package contains modular tool handlers organized by functionality:
|
|
5
|
+
- memory_tools: CRUD operations for memories
|
|
6
|
+
- relationship_tools: Create and query relationships
|
|
7
|
+
- search_tools: Search, recall, and contextual search for memories
|
|
8
|
+
- activity_tools: Activity summaries and statistics
|
|
9
|
+
- temporal_tools: Bi-temporal queries and time-travel operations
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .memory_tools import (
|
|
13
|
+
handle_store_memory,
|
|
14
|
+
handle_get_memory,
|
|
15
|
+
handle_update_memory,
|
|
16
|
+
handle_delete_memory,
|
|
17
|
+
)
|
|
18
|
+
from .relationship_tools import (
|
|
19
|
+
handle_create_relationship,
|
|
20
|
+
handle_get_related_memories,
|
|
21
|
+
)
|
|
22
|
+
from .search_tools import (
|
|
23
|
+
handle_search_memories,
|
|
24
|
+
handle_recall_memories,
|
|
25
|
+
handle_contextual_search,
|
|
26
|
+
)
|
|
27
|
+
from .activity_tools import (
|
|
28
|
+
handle_get_memory_statistics,
|
|
29
|
+
handle_get_recent_activity,
|
|
30
|
+
handle_search_relationships_by_context,
|
|
31
|
+
)
|
|
32
|
+
from .temporal_tools import (
|
|
33
|
+
handle_query_as_of,
|
|
34
|
+
handle_get_relationship_history,
|
|
35
|
+
handle_what_changed,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
# Memory CRUD operations
|
|
40
|
+
"handle_store_memory",
|
|
41
|
+
"handle_get_memory",
|
|
42
|
+
"handle_update_memory",
|
|
43
|
+
"handle_delete_memory",
|
|
44
|
+
# Relationship operations
|
|
45
|
+
"handle_create_relationship",
|
|
46
|
+
"handle_get_related_memories",
|
|
47
|
+
# Search operations
|
|
48
|
+
"handle_search_memories",
|
|
49
|
+
"handle_recall_memories",
|
|
50
|
+
"handle_contextual_search",
|
|
51
|
+
# Activity and statistics
|
|
52
|
+
"handle_get_memory_statistics",
|
|
53
|
+
"handle_get_recent_activity",
|
|
54
|
+
"handle_search_relationships_by_context",
|
|
55
|
+
# Temporal operations
|
|
56
|
+
"handle_query_as_of",
|
|
57
|
+
"handle_get_relationship_history",
|
|
58
|
+
"handle_what_changed",
|
|
59
|
+
]
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Activity and statistics tool handlers for the MCP server.
|
|
3
|
+
|
|
4
|
+
This module contains handlers for activity and statistics operations:
|
|
5
|
+
- get_recent_activity: Get summary of recent memory activity
|
|
6
|
+
- get_memory_statistics: Get statistics about the memory database
|
|
7
|
+
- search_relationships_by_context: Search relationships by structured context fields
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Any, Dict
|
|
12
|
+
|
|
13
|
+
from mcp.types import CallToolResult, TextContent
|
|
14
|
+
|
|
15
|
+
from ..database import MemoryDatabase
|
|
16
|
+
from ..sqlite_database import SQLiteMemoryDatabase
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async def handle_get_memory_statistics(
|
|
22
|
+
memory_db: MemoryDatabase,
|
|
23
|
+
arguments: Dict[str, Any]
|
|
24
|
+
) -> CallToolResult:
|
|
25
|
+
"""Handle get_memory_statistics tool call.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
memory_db: Database instance for memory operations
|
|
29
|
+
arguments: Tool arguments from MCP call (no parameters required)
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
CallToolResult with formatted statistics or error message
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
stats = await memory_db.get_memory_statistics()
|
|
36
|
+
|
|
37
|
+
# Format statistics
|
|
38
|
+
stats_text = "**Memory Database Statistics**\n\n"
|
|
39
|
+
|
|
40
|
+
if stats.get("total_memories"):
|
|
41
|
+
stats_text += f"Total Memories: {stats['total_memories']['count']}\n"
|
|
42
|
+
|
|
43
|
+
if stats.get("memories_by_type"):
|
|
44
|
+
stats_text += "\n**Memories by Type:**\n"
|
|
45
|
+
for mem_type, count in stats["memories_by_type"].items():
|
|
46
|
+
stats_text += f"- {mem_type}: {count}\n"
|
|
47
|
+
|
|
48
|
+
if stats.get("total_relationships"):
|
|
49
|
+
stats_text += f"\nTotal Relationships: {stats['total_relationships']['count']}\n"
|
|
50
|
+
|
|
51
|
+
if stats.get("avg_importance"):
|
|
52
|
+
stats_text += f"Average Importance: {stats['avg_importance']['avg_importance']:.2f}\n"
|
|
53
|
+
|
|
54
|
+
if stats.get("avg_confidence"):
|
|
55
|
+
stats_text += f"Average Confidence: {stats['avg_confidence']['avg_confidence']:.2f}\n"
|
|
56
|
+
|
|
57
|
+
return CallToolResult(
|
|
58
|
+
content=[TextContent(type="text", text=stats_text)]
|
|
59
|
+
)
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.error(f"Failed to get memory statistics: {e}")
|
|
62
|
+
return CallToolResult(
|
|
63
|
+
content=[TextContent(
|
|
64
|
+
type="text",
|
|
65
|
+
text=f"Failed to get memory statistics: {e}"
|
|
66
|
+
)],
|
|
67
|
+
isError=True
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def handle_get_recent_activity(
|
|
72
|
+
memory_db: MemoryDatabase,
|
|
73
|
+
arguments: Dict[str, Any]
|
|
74
|
+
) -> CallToolResult:
|
|
75
|
+
"""Handle get_recent_activity tool call.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
memory_db: Database instance for memory operations
|
|
79
|
+
arguments: Tool arguments from MCP call containing:
|
|
80
|
+
- days: Number of days to look back (default: 7)
|
|
81
|
+
- project: Optional project path to filter by
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
CallToolResult with formatted activity summary or error message
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
# Check if database supports get_recent_activity
|
|
88
|
+
if not isinstance(memory_db, SQLiteMemoryDatabase):
|
|
89
|
+
return CallToolResult(
|
|
90
|
+
content=[TextContent(
|
|
91
|
+
type="text",
|
|
92
|
+
text="Recent activity summary is only available with SQLite backend"
|
|
93
|
+
)],
|
|
94
|
+
isError=True
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
days = arguments.get("days", 7)
|
|
98
|
+
project = arguments.get("project")
|
|
99
|
+
|
|
100
|
+
# Auto-detect project if not specified
|
|
101
|
+
if not project:
|
|
102
|
+
from ..utils.project_detection import detect_project_context
|
|
103
|
+
project_info = detect_project_context()
|
|
104
|
+
if project_info:
|
|
105
|
+
project = project_info.get("project_path")
|
|
106
|
+
|
|
107
|
+
activity = await memory_db.get_recent_activity(days=days, project=project)
|
|
108
|
+
|
|
109
|
+
# Format results
|
|
110
|
+
result_text = f"**Recent Activity Summary (Last {days} days)**\n\n"
|
|
111
|
+
|
|
112
|
+
if project:
|
|
113
|
+
result_text += f"**Project**: {project}\n\n"
|
|
114
|
+
|
|
115
|
+
# Total count
|
|
116
|
+
result_text += f"**Total Memories**: {activity['total_count']}\n\n"
|
|
117
|
+
|
|
118
|
+
# Memories by type
|
|
119
|
+
if activity['memories_by_type']:
|
|
120
|
+
result_text += "**Breakdown by Type**:\n"
|
|
121
|
+
for mem_type, count in sorted(activity['memories_by_type'].items(), key=lambda x: x[1], reverse=True):
|
|
122
|
+
result_text += f"- {mem_type.replace('_', ' ').title()}: {count}\n"
|
|
123
|
+
result_text += "\n"
|
|
124
|
+
|
|
125
|
+
# Unresolved problems
|
|
126
|
+
if activity['unresolved_problems']:
|
|
127
|
+
result_text += f"**⚠️ Unresolved Problems ({len(activity['unresolved_problems'])})**:\n"
|
|
128
|
+
for problem in activity['unresolved_problems']:
|
|
129
|
+
result_text += f"- **{problem.title}** (importance: {problem.importance:.1f})\n"
|
|
130
|
+
if problem.summary:
|
|
131
|
+
result_text += f" {problem.summary}\n"
|
|
132
|
+
result_text += "\n"
|
|
133
|
+
|
|
134
|
+
# Recent memories
|
|
135
|
+
if activity['recent_memories']:
|
|
136
|
+
result_text += f"**Recent Memories** (showing {min(10, len(activity['recent_memories']))}):\n"
|
|
137
|
+
for i, memory in enumerate(activity['recent_memories'][:10], 1):
|
|
138
|
+
result_text += f"{i}. **{memory.title}** ({memory.type.value})\n"
|
|
139
|
+
if memory.summary:
|
|
140
|
+
result_text += f" {memory.summary}\n"
|
|
141
|
+
result_text += "\n"
|
|
142
|
+
|
|
143
|
+
# Next steps suggestion
|
|
144
|
+
result_text += "**💡 Next Steps**:\n"
|
|
145
|
+
if activity['unresolved_problems']:
|
|
146
|
+
result_text += "- Review unresolved problems and consider solutions\n"
|
|
147
|
+
result_text += f"- Use `get_memory(memory_id=\"...\")` for details\n"
|
|
148
|
+
else:
|
|
149
|
+
result_text += "- All problems have been addressed!\n"
|
|
150
|
+
|
|
151
|
+
return CallToolResult(
|
|
152
|
+
content=[TextContent(type="text", text=result_text)]
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
logger.error(f"Error in get_recent_activity: {e}")
|
|
157
|
+
return CallToolResult(
|
|
158
|
+
content=[TextContent(
|
|
159
|
+
type="text",
|
|
160
|
+
text=f"Failed to get recent activity: {e}"
|
|
161
|
+
)],
|
|
162
|
+
isError=True
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
async def handle_search_relationships_by_context(
|
|
167
|
+
memory_db: MemoryDatabase,
|
|
168
|
+
arguments: Dict[str, Any]
|
|
169
|
+
) -> CallToolResult:
|
|
170
|
+
"""Handle search_relationships_by_context tool call.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
memory_db: Database instance for memory operations
|
|
174
|
+
arguments: Tool arguments from MCP call containing:
|
|
175
|
+
- scope: Filter by scope (partial/full/conditional, optional)
|
|
176
|
+
- conditions: Filter by conditions (optional)
|
|
177
|
+
- has_evidence: Filter by presence/absence of evidence (optional)
|
|
178
|
+
- evidence: Filter by specific evidence types (optional)
|
|
179
|
+
- components: Filter by components mentioned (optional)
|
|
180
|
+
- temporal: Filter by temporal information (optional)
|
|
181
|
+
- limit: Maximum results (default: 20)
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
CallToolResult with formatted relationship results or error message
|
|
185
|
+
"""
|
|
186
|
+
try:
|
|
187
|
+
# Use SQLiteMemoryDatabase's search_relationships_by_context method
|
|
188
|
+
if not isinstance(memory_db, SQLiteMemoryDatabase):
|
|
189
|
+
return CallToolResult(
|
|
190
|
+
content=[TextContent(
|
|
191
|
+
type="text",
|
|
192
|
+
text="Context-based relationship search is only available with SQLite backend"
|
|
193
|
+
)],
|
|
194
|
+
isError=True
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
relationships = await memory_db.search_relationships_by_context(
|
|
198
|
+
scope=arguments.get("scope"),
|
|
199
|
+
conditions=arguments.get("conditions"),
|
|
200
|
+
has_evidence=arguments.get("has_evidence"),
|
|
201
|
+
evidence=arguments.get("evidence"),
|
|
202
|
+
components=arguments.get("components"),
|
|
203
|
+
temporal=arguments.get("temporal"),
|
|
204
|
+
limit=arguments.get("limit", 20)
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
if not relationships:
|
|
208
|
+
return CallToolResult(
|
|
209
|
+
content=[TextContent(
|
|
210
|
+
type="text",
|
|
211
|
+
text="No relationships found matching the specified context criteria"
|
|
212
|
+
)]
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Format results
|
|
216
|
+
result_text = f"**Found {len(relationships)} relationships matching context criteria**\n\n"
|
|
217
|
+
|
|
218
|
+
# Show applied filters
|
|
219
|
+
filters_applied = []
|
|
220
|
+
if arguments.get("scope"):
|
|
221
|
+
filters_applied.append(f"Scope: {arguments['scope']}")
|
|
222
|
+
if arguments.get("conditions"):
|
|
223
|
+
filters_applied.append(f"Conditions: {', '.join(arguments['conditions'])}")
|
|
224
|
+
if arguments.get("has_evidence") is not None:
|
|
225
|
+
filters_applied.append(f"Has Evidence: {arguments['has_evidence']}")
|
|
226
|
+
if arguments.get("evidence"):
|
|
227
|
+
filters_applied.append(f"Evidence: {', '.join(arguments['evidence'])}")
|
|
228
|
+
if arguments.get("components"):
|
|
229
|
+
filters_applied.append(f"Components: {', '.join(arguments['components'])}")
|
|
230
|
+
if arguments.get("temporal"):
|
|
231
|
+
filters_applied.append(f"Temporal: {arguments['temporal']}")
|
|
232
|
+
|
|
233
|
+
if filters_applied:
|
|
234
|
+
result_text += "**Filters Applied:**\n"
|
|
235
|
+
for f in filters_applied:
|
|
236
|
+
result_text += f"- {f}\n"
|
|
237
|
+
result_text += "\n"
|
|
238
|
+
|
|
239
|
+
# List relationships
|
|
240
|
+
for i, rel in enumerate(relationships, 1):
|
|
241
|
+
result_text += f"{i}. **{rel.type.value}**\n"
|
|
242
|
+
result_text += f" - ID: {rel.id}\n"
|
|
243
|
+
result_text += f" - From: {rel.from_memory_id}\n"
|
|
244
|
+
result_text += f" - To: {rel.to_memory_id}\n"
|
|
245
|
+
result_text += f" - Strength: {rel.properties.strength:.2f}\n"
|
|
246
|
+
if rel.properties.context:
|
|
247
|
+
result_text += f" - Context: {rel.properties.context}\n"
|
|
248
|
+
result_text += "\n"
|
|
249
|
+
|
|
250
|
+
return CallToolResult(
|
|
251
|
+
content=[TextContent(type="text", text=result_text)]
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
except Exception as e:
|
|
255
|
+
logger.error(f"Error in search_relationships_by_context: {e}")
|
|
256
|
+
return CallToolResult(
|
|
257
|
+
content=[TextContent(
|
|
258
|
+
type="text",
|
|
259
|
+
text=f"Failed to search relationships by context: {e}"
|
|
260
|
+
)],
|
|
261
|
+
isError=True
|
|
262
|
+
)
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Memory CRUD tool handlers for the MCP server.
|
|
3
|
+
|
|
4
|
+
This module contains handlers for basic memory operations:
|
|
5
|
+
- store_memory: Create new memories
|
|
6
|
+
- get_memory: Retrieve a memory by ID
|
|
7
|
+
- update_memory: Modify an existing memory
|
|
8
|
+
- delete_memory: Remove a memory and its relationships
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from typing import Any, Dict
|
|
13
|
+
|
|
14
|
+
from mcp.types import CallToolResult, TextContent
|
|
15
|
+
from pydantic import ValidationError
|
|
16
|
+
|
|
17
|
+
from ..database import MemoryDatabase
|
|
18
|
+
from ..models import Memory, MemoryType, MemoryContext
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def handle_store_memory(
|
|
24
|
+
memory_db: MemoryDatabase,
|
|
25
|
+
arguments: Dict[str, Any]
|
|
26
|
+
) -> CallToolResult:
|
|
27
|
+
"""Handle store_memory tool call.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
memory_db: Database instance for memory operations
|
|
31
|
+
arguments: Tool arguments from MCP call containing:
|
|
32
|
+
- type: Memory type (solution, problem, error, etc.)
|
|
33
|
+
- title: Short descriptive title
|
|
34
|
+
- content: Detailed content
|
|
35
|
+
- summary: Optional brief summary
|
|
36
|
+
- tags: Optional list of tags
|
|
37
|
+
- importance: Optional importance score (0.0-1.0)
|
|
38
|
+
- context: Optional context information
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
CallToolResult with memory ID on success or error message on failure
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
# Extract context if provided
|
|
45
|
+
context = None
|
|
46
|
+
if "context" in arguments:
|
|
47
|
+
context = MemoryContext(**arguments["context"])
|
|
48
|
+
|
|
49
|
+
# Create memory object
|
|
50
|
+
memory = Memory(
|
|
51
|
+
type=MemoryType(arguments["type"]),
|
|
52
|
+
title=arguments["title"],
|
|
53
|
+
content=arguments["content"],
|
|
54
|
+
summary=arguments.get("summary"),
|
|
55
|
+
tags=arguments.get("tags", []),
|
|
56
|
+
importance=arguments.get("importance", 0.5),
|
|
57
|
+
context=context
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Store in database
|
|
61
|
+
memory_id = await memory_db.store_memory(memory)
|
|
62
|
+
|
|
63
|
+
return CallToolResult(
|
|
64
|
+
content=[TextContent(
|
|
65
|
+
type="text",
|
|
66
|
+
text=f"Memory stored successfully with ID: {memory_id}"
|
|
67
|
+
)]
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
except (ValidationError, KeyError, ValueError) as e:
|
|
71
|
+
return CallToolResult(
|
|
72
|
+
content=[TextContent(
|
|
73
|
+
type="text",
|
|
74
|
+
text=f"Validation error: {e}"
|
|
75
|
+
)],
|
|
76
|
+
isError=True
|
|
77
|
+
)
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logger.error(f"Failed to store memory: {e}")
|
|
80
|
+
return CallToolResult(
|
|
81
|
+
content=[TextContent(
|
|
82
|
+
type="text",
|
|
83
|
+
text=f"Failed to store memory: {e}"
|
|
84
|
+
)],
|
|
85
|
+
isError=True
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
async def handle_get_memory(
|
|
90
|
+
memory_db: MemoryDatabase,
|
|
91
|
+
arguments: Dict[str, Any]
|
|
92
|
+
) -> CallToolResult:
|
|
93
|
+
"""Handle get_memory tool call.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
memory_db: Database instance for memory operations
|
|
97
|
+
arguments: Tool arguments from MCP call containing:
|
|
98
|
+
- memory_id: ID of memory to retrieve
|
|
99
|
+
- include_relationships: Whether to include related memories (default: True)
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
CallToolResult with formatted memory details or error message
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
memory_id = arguments["memory_id"]
|
|
106
|
+
include_relationships = arguments.get("include_relationships", True)
|
|
107
|
+
|
|
108
|
+
memory = await memory_db.get_memory(memory_id, include_relationships)
|
|
109
|
+
|
|
110
|
+
if not memory:
|
|
111
|
+
return CallToolResult(
|
|
112
|
+
content=[TextContent(
|
|
113
|
+
type="text",
|
|
114
|
+
text=f"Memory not found: {memory_id}"
|
|
115
|
+
)],
|
|
116
|
+
isError=True
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Format memory for display
|
|
120
|
+
memory_text = f"""**Memory: {memory.title}**
|
|
121
|
+
Type: {memory.type.value}
|
|
122
|
+
Created: {memory.created_at}
|
|
123
|
+
Importance: {memory.importance}
|
|
124
|
+
Tags: {', '.join(memory.tags) if memory.tags else 'None'}
|
|
125
|
+
|
|
126
|
+
**Content:**
|
|
127
|
+
{memory.content}"""
|
|
128
|
+
|
|
129
|
+
if memory.summary:
|
|
130
|
+
memory_text = f"**Summary:** {memory.summary}\n\n" + memory_text
|
|
131
|
+
|
|
132
|
+
# Add context information if available
|
|
133
|
+
if memory.context:
|
|
134
|
+
context_parts = []
|
|
135
|
+
|
|
136
|
+
if memory.context.project_path:
|
|
137
|
+
context_parts.append(f"Project: {memory.context.project_path}")
|
|
138
|
+
|
|
139
|
+
if memory.context.files_involved:
|
|
140
|
+
files_str = ', '.join(memory.context.files_involved[:3])
|
|
141
|
+
if len(memory.context.files_involved) > 3:
|
|
142
|
+
files_str += f" (+{len(memory.context.files_involved) - 3} more)"
|
|
143
|
+
context_parts.append(f"Files: {files_str}")
|
|
144
|
+
|
|
145
|
+
if memory.context.languages:
|
|
146
|
+
context_parts.append(f"Languages: {', '.join(memory.context.languages)}")
|
|
147
|
+
|
|
148
|
+
if memory.context.frameworks:
|
|
149
|
+
context_parts.append(f"Frameworks: {', '.join(memory.context.frameworks)}")
|
|
150
|
+
|
|
151
|
+
if memory.context.technologies:
|
|
152
|
+
context_parts.append(f"Technologies: {', '.join(memory.context.technologies)}")
|
|
153
|
+
|
|
154
|
+
if memory.context.git_branch:
|
|
155
|
+
context_parts.append(f"Branch: {memory.context.git_branch}")
|
|
156
|
+
|
|
157
|
+
if context_parts:
|
|
158
|
+
context_text = "\n**Context:**\n" + "\n".join(f" {part}" for part in context_parts)
|
|
159
|
+
memory_text += "\n" + context_text
|
|
160
|
+
|
|
161
|
+
return CallToolResult(
|
|
162
|
+
content=[TextContent(type="text", text=memory_text)]
|
|
163
|
+
)
|
|
164
|
+
except KeyError as e:
|
|
165
|
+
return CallToolResult(
|
|
166
|
+
content=[TextContent(
|
|
167
|
+
type="text",
|
|
168
|
+
text=f"Missing required field: {e}"
|
|
169
|
+
)],
|
|
170
|
+
isError=True
|
|
171
|
+
)
|
|
172
|
+
except Exception as e:
|
|
173
|
+
logger.error(f"Failed to get memory: {e}")
|
|
174
|
+
return CallToolResult(
|
|
175
|
+
content=[TextContent(
|
|
176
|
+
type="text",
|
|
177
|
+
text=f"Failed to get memory: {e}"
|
|
178
|
+
)],
|
|
179
|
+
isError=True
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
async def handle_update_memory(
|
|
184
|
+
memory_db: MemoryDatabase,
|
|
185
|
+
arguments: Dict[str, Any]
|
|
186
|
+
) -> CallToolResult:
|
|
187
|
+
"""Handle update_memory tool call.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
memory_db: Database instance for memory operations
|
|
191
|
+
arguments: Tool arguments from MCP call containing:
|
|
192
|
+
- memory_id: ID of memory to update (required)
|
|
193
|
+
- title: New title (optional)
|
|
194
|
+
- content: New content (optional)
|
|
195
|
+
- summary: New summary (optional)
|
|
196
|
+
- tags: New tags list (optional)
|
|
197
|
+
- importance: New importance score (optional)
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
CallToolResult with success message or error
|
|
201
|
+
"""
|
|
202
|
+
try:
|
|
203
|
+
memory_id = arguments["memory_id"]
|
|
204
|
+
|
|
205
|
+
# Get existing memory
|
|
206
|
+
memory = await memory_db.get_memory(memory_id, include_relationships=False)
|
|
207
|
+
if not memory:
|
|
208
|
+
return CallToolResult(
|
|
209
|
+
content=[TextContent(
|
|
210
|
+
type="text",
|
|
211
|
+
text=f"Memory not found: {memory_id}"
|
|
212
|
+
)],
|
|
213
|
+
isError=True
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Update fields
|
|
217
|
+
if "title" in arguments:
|
|
218
|
+
memory.title = arguments["title"]
|
|
219
|
+
if "content" in arguments:
|
|
220
|
+
memory.content = arguments["content"]
|
|
221
|
+
if "summary" in arguments:
|
|
222
|
+
memory.summary = arguments["summary"]
|
|
223
|
+
if "tags" in arguments:
|
|
224
|
+
memory.tags = arguments["tags"]
|
|
225
|
+
if "importance" in arguments:
|
|
226
|
+
memory.importance = arguments["importance"]
|
|
227
|
+
|
|
228
|
+
# Update in database
|
|
229
|
+
success = await memory_db.update_memory(memory)
|
|
230
|
+
|
|
231
|
+
if success:
|
|
232
|
+
return CallToolResult(
|
|
233
|
+
content=[TextContent(
|
|
234
|
+
type="text",
|
|
235
|
+
text=f"Memory updated successfully: {memory_id}"
|
|
236
|
+
)]
|
|
237
|
+
)
|
|
238
|
+
else:
|
|
239
|
+
return CallToolResult(
|
|
240
|
+
content=[TextContent(
|
|
241
|
+
type="text",
|
|
242
|
+
text=f"Failed to update memory: {memory_id}"
|
|
243
|
+
)],
|
|
244
|
+
isError=True
|
|
245
|
+
)
|
|
246
|
+
except KeyError as e:
|
|
247
|
+
return CallToolResult(
|
|
248
|
+
content=[TextContent(
|
|
249
|
+
type="text",
|
|
250
|
+
text=f"Missing required field: {e}"
|
|
251
|
+
)],
|
|
252
|
+
isError=True
|
|
253
|
+
)
|
|
254
|
+
except Exception as e:
|
|
255
|
+
logger.error(f"Failed to update memory: {e}")
|
|
256
|
+
return CallToolResult(
|
|
257
|
+
content=[TextContent(
|
|
258
|
+
type="text",
|
|
259
|
+
text=f"Failed to update memory: {e}"
|
|
260
|
+
)],
|
|
261
|
+
isError=True
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
async def handle_delete_memory(
|
|
266
|
+
memory_db: MemoryDatabase,
|
|
267
|
+
arguments: Dict[str, Any]
|
|
268
|
+
) -> CallToolResult:
|
|
269
|
+
"""Handle delete_memory tool call.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
memory_db: Database instance for memory operations
|
|
273
|
+
arguments: Tool arguments from MCP call containing:
|
|
274
|
+
- memory_id: ID of memory to delete
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
CallToolResult with success message or error
|
|
278
|
+
"""
|
|
279
|
+
try:
|
|
280
|
+
memory_id = arguments["memory_id"]
|
|
281
|
+
|
|
282
|
+
success = await memory_db.delete_memory(memory_id)
|
|
283
|
+
|
|
284
|
+
if success:
|
|
285
|
+
return CallToolResult(
|
|
286
|
+
content=[TextContent(
|
|
287
|
+
type="text",
|
|
288
|
+
text=f"Memory deleted successfully: {memory_id}"
|
|
289
|
+
)]
|
|
290
|
+
)
|
|
291
|
+
else:
|
|
292
|
+
return CallToolResult(
|
|
293
|
+
content=[TextContent(
|
|
294
|
+
type="text",
|
|
295
|
+
text=f"Failed to delete memory (may not exist): {memory_id}"
|
|
296
|
+
)],
|
|
297
|
+
isError=True
|
|
298
|
+
)
|
|
299
|
+
except KeyError as e:
|
|
300
|
+
return CallToolResult(
|
|
301
|
+
content=[TextContent(
|
|
302
|
+
type="text",
|
|
303
|
+
text=f"Missing required field: {e}"
|
|
304
|
+
)],
|
|
305
|
+
isError=True
|
|
306
|
+
)
|
|
307
|
+
except Exception as e:
|
|
308
|
+
logger.error(f"Failed to delete memory: {e}")
|
|
309
|
+
return CallToolResult(
|
|
310
|
+
content=[TextContent(
|
|
311
|
+
type="text",
|
|
312
|
+
text=f"Failed to delete memory: {e}"
|
|
313
|
+
)],
|
|
314
|
+
isError=True
|
|
315
|
+
)
|