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
memorygraph/server.py ADDED
@@ -0,0 +1,883 @@
1
+ """
2
+ Claude Code Memory Server - MCP implementation.
3
+
4
+ This module implements the Model Context Protocol server that provides intelligent
5
+ memory capabilities for Claude Code using Neo4j as the backend storage.
6
+ """
7
+
8
+ import asyncio
9
+ import logging
10
+ import os
11
+ import uuid
12
+ from datetime import datetime
13
+ from typing import Any, Dict, List, Optional, Sequence
14
+
15
+ from mcp.server import Server, NotificationOptions
16
+ from mcp.server.models import InitializationOptions
17
+ from mcp.server.stdio import stdio_server
18
+ from mcp.types import (
19
+ Tool,
20
+ TextContent,
21
+ CallToolResult,
22
+ ListToolsRequest,
23
+ ListToolsResult,
24
+ )
25
+ from pydantic import ValidationError
26
+
27
+ from . import __version__
28
+ from .database import MemoryDatabase
29
+ from .sqlite_database import SQLiteMemoryDatabase
30
+ from .cloud_database import CloudMemoryDatabase
31
+ from .backends.sqlite_fallback import SQLiteFallbackBackend
32
+ from .backends.cloud_backend import CloudBackend
33
+ from .models import (
34
+ Memory,
35
+ MemoryType,
36
+ RelationshipType,
37
+ RelationshipProperties,
38
+ SearchQuery,
39
+ MemoryContext,
40
+ MemoryError,
41
+ MemoryNotFoundError,
42
+ RelationshipError,
43
+ ValidationError as MemoryValidationError,
44
+ DatabaseConnectionError,
45
+ )
46
+ from .advanced_tools import ADVANCED_RELATIONSHIP_TOOLS, AdvancedRelationshipHandlers
47
+ from .migration_tools_module import MIGRATION_TOOLS, MIGRATION_TOOL_HANDLERS
48
+ # Removed: intelligence_tools, integration_tools, proactive_tools (moved to experimental/)
49
+ from .config import Config
50
+ from .tools import (
51
+ handle_store_memory,
52
+ handle_get_memory,
53
+ handle_update_memory,
54
+ handle_delete_memory,
55
+ handle_create_relationship,
56
+ handle_get_related_memories,
57
+ handle_search_memories,
58
+ handle_recall_memories,
59
+ handle_contextual_search,
60
+ handle_get_memory_statistics,
61
+ handle_get_recent_activity,
62
+ handle_search_relationships_by_context,
63
+ # Temporal handlers deferred (backend methods available via Python API):
64
+ # handle_query_as_of, handle_get_relationship_history, handle_what_changed
65
+ )
66
+
67
+
68
+ # Configure logging
69
+ logging.basicConfig(
70
+ level=logging.INFO,
71
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
72
+ )
73
+ logger = logging.getLogger(__name__)
74
+
75
+
76
+ class ClaudeMemoryServer:
77
+ """Claude Code Memory MCP Server implementation."""
78
+
79
+ def __init__(self):
80
+ """Initialize the memory server."""
81
+ self.server = Server("claude-memory")
82
+ self.db_connection = None # GraphBackend instance
83
+ self.memory_db: Optional[MemoryDatabase] = None
84
+ self.advanced_handlers: Optional[AdvancedRelationshipHandlers] = None
85
+
86
+ # Register MCP handlers
87
+ self._register_handlers()
88
+
89
+ # Collect all tools from all modules
90
+ all_tools = self._collect_all_tools()
91
+
92
+ # Filter tools based on profile
93
+ enabled_tool_names = Config.get_enabled_tools()
94
+ if enabled_tool_names is None:
95
+ # Full profile: all tools enabled
96
+ self.tools = all_tools
97
+ logger.info(f"Tool profile: FULL - All {len(all_tools)} tools enabled")
98
+ else:
99
+ # Filter tools by name
100
+ self.tools = [tool for tool in all_tools if tool.name in enabled_tool_names]
101
+ logger.info(f"Tool profile: {Config.TOOL_PROFILE.upper()} - {len(self.tools)}/{len(all_tools)} tools enabled")
102
+
103
+ def _collect_all_tools(self) -> List[Tool]:
104
+ """Collect all tool definitions from all modules."""
105
+ # Basic tools (defined inline below)
106
+ basic_tools = [
107
+ Tool(
108
+ name="recall_memories",
109
+ description="""🎯 RECOMMENDED STARTING POINT for recalling past memories and learnings.
110
+
111
+ This is a convenience tool that wraps search_memories with optimal defaults for natural language queries.
112
+
113
+ WHEN TO USE:
114
+ - This should be your FIRST tool for any recall query
115
+ - User asks "What did we learn about X?"
116
+ - Looking for past solutions, problems, or patterns
117
+ - Understanding project context or history
118
+ - Finding related memories by topic
119
+
120
+ WHY USE THIS vs search_memories:
121
+ - Optimized for natural language queries
122
+ - Automatically uses fuzzy matching (handles plurals, tenses, case)
123
+ - Always includes relationship context
124
+ - Simpler interface for common use cases
125
+ - Best default settings applied
126
+
127
+ HOW TO USE:
128
+ - Pass a natural language query (e.g., "Redis timeout solutions")
129
+ - Optionally filter by memory_types for precision
130
+ - Optionally specify project_path to scope results
131
+ - Results automatically ranked by relevance
132
+
133
+ EXAMPLES:
134
+ - User: "What timeouts have we fixed?" → recall_memories(query="timeout fix")
135
+ - User: "Show me Redis solutions" → recall_memories(query="Redis", memory_types=["solution"])
136
+ - User: "What authentication errors occurred?" → recall_memories(query="authentication error", memory_types=["error"])
137
+ - User: "Catch me up on this project" → recall_memories(project_path="/current/project")
138
+
139
+ RETURNS:
140
+ - Memories with match quality hints
141
+ - Immediate relationships (what solves what, what causes what)
142
+ - Context summaries for quick understanding
143
+
144
+ NOTE: For advanced queries (boolean operators, exact matches, multi-term), use search_memories directly.""",
145
+ inputSchema={
146
+ "type": "object",
147
+ "properties": {
148
+ "query": {
149
+ "type": "string",
150
+ "description": "Natural language query for what you're looking for"
151
+ },
152
+ "memory_types": {
153
+ "type": "array",
154
+ "items": {
155
+ "type": "string",
156
+ "enum": [t.value for t in MemoryType]
157
+ },
158
+ "description": "Optional: Filter by memory types for more precision"
159
+ },
160
+ "project_path": {
161
+ "type": "string",
162
+ "description": "Optional: Filter by project path to scope results"
163
+ },
164
+ "limit": {
165
+ "type": "integer",
166
+ "minimum": 1,
167
+ "maximum": 1000,
168
+ "description": "Maximum number of results per page (default: 20)"
169
+ },
170
+ "offset": {
171
+ "type": "integer",
172
+ "minimum": 0,
173
+ "description": "Number of results to skip for pagination (default: 0)"
174
+ }
175
+ }
176
+ }
177
+ ),
178
+ Tool(
179
+ name="store_memory",
180
+ description="""Store a new memory with context and metadata.
181
+
182
+ WHEN TO USE:
183
+ - Capturing solutions to problems
184
+ - Recording important decisions and rationale
185
+ - Documenting errors and their causes
186
+ - Noting patterns or learnings from work
187
+ - Saving technology choices and trade-offs
188
+ - Recording project context and state
189
+
190
+ HOW TO USE:
191
+ - Choose appropriate type: solution, problem, error, fix, decision, pattern, etc.
192
+ - Write clear, searchable title (this is searched during recall)
193
+ - Include detailed content with specifics
194
+ - Add tags for categorical organization (e.g., "redis", "authentication", "performance")
195
+ - Set importance: 0.8-1.0 for critical info, 0.5-0.7 for normal, 0.0-0.4 for reference
196
+ - Optional: Add project_path in context to scope to current project
197
+
198
+ EXAMPLES:
199
+ - Solved a bug: store_memory(type="solution", title="Fixed Redis timeout in payment flow", content="...", tags=["redis", "payment"], importance=0.8)
200
+ - Learned a pattern: store_memory(type="pattern", title="Use exponential backoff for API retries", content="...", tags=["api", "reliability"])
201
+ - Made a decision: store_memory(type="decision", title="Chose PostgreSQL over MongoDB", content="Rationale: ...", importance=0.9)
202
+ - Hit an error: store_memory(type="error", title="Authentication fails with OAuth2", content="Error details...", tags=["auth", "oauth"])
203
+
204
+ AFTER STORING:
205
+ - Use create_relationship to link related memories (e.g., solution SOLVES problem)
206
+ - Returns memory_id for future reference""",
207
+ inputSchema={
208
+ "type": "object",
209
+ "properties": {
210
+ "type": {
211
+ "type": "string",
212
+ "enum": [t.value for t in MemoryType],
213
+ "description": "Type of memory to store"
214
+ },
215
+ "title": {
216
+ "type": "string",
217
+ "description": "Short descriptive title for the memory"
218
+ },
219
+ "content": {
220
+ "type": "string",
221
+ "description": "Detailed content of the memory"
222
+ },
223
+ "summary": {
224
+ "type": "string",
225
+ "description": "Optional brief summary of the memory"
226
+ },
227
+ "tags": {
228
+ "type": "array",
229
+ "items": {"type": "string"},
230
+ "description": "Tags to categorize the memory"
231
+ },
232
+ "importance": {
233
+ "type": "number",
234
+ "minimum": 0.0,
235
+ "maximum": 1.0,
236
+ "description": "Importance score (0.0-1.0)"
237
+ },
238
+ "context": {
239
+ "type": "object",
240
+ "description": "Context information for the memory"
241
+ }
242
+ },
243
+ "required": ["type", "title", "content"]
244
+ }
245
+ ),
246
+ Tool(
247
+ name="get_memory",
248
+ description="""Retrieve a specific memory by ID with full details.
249
+
250
+ WHEN TO USE:
251
+ - You have a memory_id from search results
252
+ - User asks for details about a specific memory
253
+ - Need to verify memory contents before updating or deleting
254
+ - Drilling down after finding a memory in search
255
+
256
+ HOW TO USE:
257
+ - Pass memory_id from search_memories or store_memory results
258
+ - Set include_relationships=true (default) to see what connects to this memory
259
+ - Returns full memory with all fields
260
+
261
+ EXAMPLE:
262
+ - After search: get_memory(memory_id="abc-123", include_relationships=true)
263
+
264
+ NOTE: Prefer search_memories for discovery. Use get_memory only when you have a specific ID.""",
265
+ inputSchema={
266
+ "type": "object",
267
+ "properties": {
268
+ "memory_id": {
269
+ "type": "string",
270
+ "description": "ID of the memory to retrieve"
271
+ },
272
+ "include_relationships": {
273
+ "type": "boolean",
274
+ "description": "Whether to include related memories"
275
+ }
276
+ },
277
+ "required": ["memory_id"]
278
+ }
279
+ ),
280
+ Tool(
281
+ name="search_memories",
282
+ description="""Advanced search tool with fine-grained control over search parameters.
283
+
284
+ ⚠️ CONSIDER USING recall_memories FIRST - it has better defaults for most queries.
285
+ Use search_memories only when you need:
286
+ - Exact matching (search_tolerance="strict")
287
+ - Multi-term boolean queries
288
+ - Specific tag filtering
289
+ - Advanced parameter control
290
+
291
+ WHEN TO USE:
292
+ - Need strict exact matching instead of fuzzy
293
+ - Complex queries with multiple terms and match_mode
294
+ - Filtering by specific tags or importance thresholds
295
+ - When recall_memories didn't find what you need
296
+
297
+ HOW TO USE:
298
+ - Query searches across title, content, and summary fields
299
+ - Use search_tolerance to control matching:
300
+ • 'normal' (default): Handles plurals, tenses, case variations (e.g., "timeout" matches "timeouts", "timed out")
301
+ • 'strict': Exact substring matches only
302
+ • 'fuzzy': Reserved for future typo tolerance
303
+ - Filter by memory_types to narrow results (e.g., only "solution" or "problem")
304
+ - Filter by tags for categorical search
305
+ - Results include relationship context automatically (what connects to what)
306
+
307
+ EXAMPLES:
308
+ - User: "What timeouts have we fixed?" → search_memories(query="timeout", memory_types=["solution"])
309
+ - User: "Show me Redis issues" → search_memories(query="Redis", memory_types=["problem", "error"])
310
+ - User: "Authentication solutions" → search_memories(query="authentication", memory_types=["solution"])
311
+ - User: "High priority items" → search_memories(min_importance=0.7)
312
+
313
+ RESULTS INCLUDE:
314
+ - Match quality hints (which fields matched)
315
+ - Relationship context (what solves/causes/relates to what)
316
+ - Context summaries for quick scanning""",
317
+ inputSchema={
318
+ "type": "object",
319
+ "properties": {
320
+ "query": {
321
+ "type": "string",
322
+ "description": "Text to search for in memory content"
323
+ },
324
+ "terms": {
325
+ "type": "array",
326
+ "items": {"type": "string"},
327
+ "description": "Multiple search terms for complex queries (alternative to query)"
328
+ },
329
+ "memory_types": {
330
+ "type": "array",
331
+ "items": {
332
+ "type": "string",
333
+ "enum": [t.value for t in MemoryType]
334
+ },
335
+ "description": "Filter by memory types"
336
+ },
337
+ "tags": {
338
+ "type": "array",
339
+ "items": {"type": "string"},
340
+ "description": "Filter by tags"
341
+ },
342
+ "project_path": {
343
+ "type": "string",
344
+ "description": "Filter by project path"
345
+ },
346
+ "min_importance": {
347
+ "type": "number",
348
+ "minimum": 0.0,
349
+ "maximum": 1.0,
350
+ "description": "Minimum importance score"
351
+ },
352
+ "limit": {
353
+ "type": "integer",
354
+ "minimum": 1,
355
+ "maximum": 1000,
356
+ "description": "Maximum number of results per page (default: 50)"
357
+ },
358
+ "offset": {
359
+ "type": "integer",
360
+ "minimum": 0,
361
+ "description": "Number of results to skip for pagination (default: 0)"
362
+ },
363
+ "search_tolerance": {
364
+ "type": "string",
365
+ "enum": ["strict", "normal", "fuzzy"],
366
+ "description": "Search tolerance mode: 'strict' for exact matches, 'normal' for stemming (default), 'fuzzy' for typo tolerance"
367
+ },
368
+ "match_mode": {
369
+ "type": "string",
370
+ "enum": ["any", "all"],
371
+ "description": "Match mode for terms: 'any' returns results matching ANY term (OR), 'all' requires ALL terms (AND)"
372
+ },
373
+ "relationship_filter": {
374
+ "type": "array",
375
+ "items": {"type": "string"},
376
+ "description": "Filter results to only include memories with these relationship types"
377
+ }
378
+ }
379
+ }
380
+ ),
381
+ Tool(
382
+ name="update_memory",
383
+ description="Update an existing memory",
384
+ inputSchema={
385
+ "type": "object",
386
+ "properties": {
387
+ "memory_id": {
388
+ "type": "string",
389
+ "description": "ID of the memory to update"
390
+ },
391
+ "title": {"type": "string"},
392
+ "content": {"type": "string"},
393
+ "summary": {"type": "string"},
394
+ "tags": {
395
+ "type": "array",
396
+ "items": {"type": "string"}
397
+ },
398
+ "importance": {
399
+ "type": "number",
400
+ "minimum": 0.0,
401
+ "maximum": 1.0
402
+ }
403
+ },
404
+ "required": ["memory_id"]
405
+ }
406
+ ),
407
+ Tool(
408
+ name="delete_memory",
409
+ description="Delete a memory and all its relationships",
410
+ inputSchema={
411
+ "type": "object",
412
+ "properties": {
413
+ "memory_id": {
414
+ "type": "string",
415
+ "description": "ID of the memory to delete"
416
+ }
417
+ },
418
+ "required": ["memory_id"]
419
+ }
420
+ ),
421
+ Tool(
422
+ name="create_relationship",
423
+ description="""Create a relationship between two memories to capture how they connect.
424
+
425
+ WHEN TO USE:
426
+ - After storing a solution, link it to the problem it solves
427
+ - Connect an error to its fix
428
+ - Link a decision to what it improves or replaces
429
+ - Associate patterns with where they apply
430
+ - Track what causes what (e.g., error TRIGGERS problem)
431
+ - Document what requires what (dependencies)
432
+
433
+ HOW TO USE:
434
+ - Get memory IDs from store_memory or search_memories
435
+ - Choose appropriate relationship type:
436
+ • SOLVES: solution → problem/error
437
+ • CAUSES/TRIGGERS: cause → effect
438
+ • FIXES/ADDRESSES: fix → error/problem
439
+ • IMPROVES/REPLACES: new approach → old approach
440
+ • REQUIRES/DEPENDS_ON: dependent → dependency
441
+ • USED_IN/APPLIES_TO: pattern → project/context
442
+ • RELATED_TO: general association
443
+ - Optional: Add natural language context (auto-extracted into structured format)
444
+ - Optional: Set strength (defaults to 0.5) and confidence (defaults to 0.8)
445
+
446
+ EXAMPLES:
447
+ - Link solution to problem: create_relationship(from_memory_id="sol-123", to_memory_id="prob-456", relationship_type="SOLVES")
448
+ - Document cause: create_relationship(from_memory_id="config-error", to_memory_id="timeout-problem", relationship_type="CAUSES", context="Missing Redis timeout config causes connection timeouts in production")
449
+ - Track dependency: create_relationship(from_memory_id="auth-module", to_memory_id="jwt-library", relationship_type="REQUIRES")
450
+ - Pattern usage: create_relationship(from_memory_id="retry-pattern", to_memory_id="api-integration", relationship_type="APPLIES_TO")
451
+
452
+ WHY IT MATTERS:
453
+ - Relationships enable "What solved X?" queries
454
+ - Builds knowledge graph for pattern recognition
455
+ - Context is automatically structured for advanced queries""",
456
+ inputSchema={
457
+ "type": "object",
458
+ "properties": {
459
+ "from_memory_id": {
460
+ "type": "string",
461
+ "description": "ID of the source memory"
462
+ },
463
+ "to_memory_id": {
464
+ "type": "string",
465
+ "description": "ID of the target memory"
466
+ },
467
+ "relationship_type": {
468
+ "type": "string",
469
+ "enum": [t.value for t in RelationshipType],
470
+ "description": "Type of relationship to create"
471
+ },
472
+ "strength": {
473
+ "type": "number",
474
+ "minimum": 0.0,
475
+ "maximum": 1.0,
476
+ "description": "Strength of the relationship (0.0-1.0)"
477
+ },
478
+ "confidence": {
479
+ "type": "number",
480
+ "minimum": 0.0,
481
+ "maximum": 1.0,
482
+ "description": "Confidence in the relationship (0.0-1.0)"
483
+ },
484
+ "context": {
485
+ "type": "string",
486
+ "description": "Context or description of the relationship"
487
+ }
488
+ },
489
+ "required": ["from_memory_id", "to_memory_id", "relationship_type"]
490
+ }
491
+ ),
492
+ Tool(
493
+ name="get_related_memories",
494
+ description="""Find memories related to a specific memory by traversing relationships.
495
+
496
+ WHEN TO USE:
497
+ - After finding a memory, explore what connects to it
498
+ - User asks "What caused this?" or "What solves this?"
499
+ - Understanding the context around a specific memory
500
+ - Following chains of reasoning (what led to what)
501
+ - Finding all solutions for a problem
502
+
503
+ HOW TO USE:
504
+ - Pass memory_id from search_memories or get_memory
505
+ - Filter by relationship_types to focus query:
506
+ • ["SOLVES"] → Find all solutions for a problem
507
+ • ["CAUSES", "TRIGGERS"] → Find what causes this
508
+ • ["USED_IN", "APPLIES_TO"] → Find where a pattern applies
509
+ - Set max_depth to control traversal:
510
+ • 1 = immediate connections only (default)
511
+ • 2 = connections of connections
512
+ • 3+ = deeper traversal (rarely needed)
513
+
514
+ EXAMPLES:
515
+ - Find solutions: get_related_memories(memory_id="problem-123", relationship_types=["SOLVES"], max_depth=1)
516
+ - Explore context: get_related_memories(memory_id="decision-456", max_depth=2)
517
+ - Find causes: get_related_memories(memory_id="error-789", relationship_types=["CAUSES", "TRIGGERS"])
518
+
519
+ RETURNS:
520
+ - List of related memories with relationship types and strengths""",
521
+ inputSchema={
522
+ "type": "object",
523
+ "properties": {
524
+ "memory_id": {
525
+ "type": "string",
526
+ "description": "ID of the memory to find relations for"
527
+ },
528
+ "relationship_types": {
529
+ "type": "array",
530
+ "items": {
531
+ "type": "string",
532
+ "enum": [t.value for t in RelationshipType]
533
+ },
534
+ "description": "Filter by relationship types"
535
+ },
536
+ "max_depth": {
537
+ "type": "integer",
538
+ "minimum": 1,
539
+ "maximum": 5,
540
+ "description": "Maximum relationship depth to traverse"
541
+ }
542
+ },
543
+ "required": ["memory_id"]
544
+ }
545
+ ),
546
+ Tool(
547
+ name="get_memory_statistics",
548
+ description="Get statistics about the memory database",
549
+ inputSchema={
550
+ "type": "object",
551
+ "properties": {}
552
+ }
553
+ ),
554
+ Tool(
555
+ name="get_recent_activity",
556
+ description="""Get a summary of recent memory activity for session briefing.
557
+
558
+ WHEN TO USE:
559
+ - User asks "What have we been working on?"
560
+ - User asks "Catch me up" or "What's the status?"
561
+ - Starting a new session and want context
562
+ - Need to understand recent progress
563
+
564
+ HOW TO USE:
565
+ - Specify days (default: 7) to control timeframe
566
+ - Optionally filter by project to scope to current work
567
+ - Returns summary by type, recent memories, and unresolved problems
568
+
569
+ WHAT YOU GET:
570
+ - Count of memories by type (solutions, problems, etc.)
571
+ - List of recent memories (up to 20)
572
+ - Unresolved problems (problems with no solution yet)
573
+ - Time range and filters applied
574
+
575
+ EXAMPLES:
576
+ - User: "What have we been working on?" → get_recent_activity(days=7)
577
+ - User: "Catch me up on this project" → get_recent_activity(days=7, project="/current/project")
578
+ - User: "What happened last month?" → get_recent_activity(days=30)
579
+ - User: "Any unsolved problems?" → get_recent_activity(days=30) // check unresolved_problems
580
+
581
+ WHY IT MATTERS:
582
+ - Provides quick context at session start
583
+ - Identifies work that needs attention (unresolved problems)
584
+ - Shows progress and activity patterns""",
585
+ inputSchema={
586
+ "type": "object",
587
+ "properties": {
588
+ "days": {
589
+ "type": "integer",
590
+ "minimum": 1,
591
+ "maximum": 365,
592
+ "description": "Number of days to look back (default: 7)"
593
+ },
594
+ "project": {
595
+ "type": "string",
596
+ "description": "Optional: Filter by project path"
597
+ }
598
+ }
599
+ }
600
+ ),
601
+ Tool(
602
+ name="search_relationships_by_context",
603
+ description="Search relationships by their structured context fields (scope, conditions, evidence, components)",
604
+ inputSchema={
605
+ "type": "object",
606
+ "properties": {
607
+ "scope": {
608
+ "type": "string",
609
+ "enum": ["partial", "full", "conditional"],
610
+ "description": "Filter by scope (partial, full, or conditional implementation)"
611
+ },
612
+ "conditions": {
613
+ "type": "array",
614
+ "items": {"type": "string"},
615
+ "description": "Filter by conditions (e.g., ['production', 'Redis enabled']). Matches any."
616
+ },
617
+ "has_evidence": {
618
+ "type": "boolean",
619
+ "description": "Filter by presence/absence of evidence (verified by tests, etc.)"
620
+ },
621
+ "evidence": {
622
+ "type": "array",
623
+ "items": {"type": "string"},
624
+ "description": "Filter by specific evidence types (e.g., ['integration tests', 'unit tests']). Matches any."
625
+ },
626
+ "components": {
627
+ "type": "array",
628
+ "items": {"type": "string"},
629
+ "description": "Filter by components mentioned (e.g., ['auth', 'Redis']). Matches any."
630
+ },
631
+ "temporal": {
632
+ "type": "string",
633
+ "description": "Filter by temporal information (e.g., 'v2.1.0', 'since 2024')"
634
+ },
635
+ "limit": {
636
+ "type": "integer",
637
+ "minimum": 1,
638
+ "maximum": 100,
639
+ "description": "Maximum number of results (default: 20)"
640
+ }
641
+ }
642
+ }
643
+ ),
644
+ # Contextual search tool for semantic graph traversal
645
+ Tool(
646
+ name="contextual_search",
647
+ description="""Search only within the context of a given memory (scoped search).
648
+
649
+ Two-phase process: (1) Find related memories, (2) Search only within that set.
650
+ Provides semantic scoping without embeddings.
651
+
652
+ WHEN TO USE:
653
+ - Searching within a specific problem context
654
+ - Finding solutions in related knowledge
655
+ - Scoped discovery
656
+
657
+ HOW TO USE:
658
+ - Specify memory_id (context root)
659
+ - Provide query (search term)
660
+ - Optional: max_depth (default: 2)
661
+
662
+ RETURNS:
663
+ - Matches found only within related memories
664
+ - Context information
665
+ - No leakage outside context""",
666
+ inputSchema={
667
+ "type": "object",
668
+ "properties": {
669
+ "memory_id": {
670
+ "type": "string",
671
+ "description": "Memory ID to use as context root (required)"
672
+ },
673
+ "query": {
674
+ "type": "string",
675
+ "description": "Search query within context (required)"
676
+ },
677
+ "max_depth": {
678
+ "type": "integer",
679
+ "minimum": 1,
680
+ "maximum": 5,
681
+ "description": "Maximum relationship traversal depth (default: 2)"
682
+ }
683
+ },
684
+ "required": ["memory_id", "query"]
685
+ }
686
+ ),
687
+ # Temporal tools deferred per ADR-017 (Context Budget Constraint)
688
+ # Backend methods available via Python API:
689
+ # - query_as_of, get_relationship_history, what_changed
690
+ # MCP tool registration deferred until usage data justifies context cost
691
+ ]
692
+
693
+ # Combine all tools from all modules
694
+ all_tools = (
695
+ basic_tools +
696
+ ADVANCED_RELATIONSHIP_TOOLS +
697
+ MIGRATION_TOOLS
698
+ )
699
+
700
+ return all_tools
701
+
702
+ def _register_handlers(self):
703
+ """Register MCP protocol handlers."""
704
+
705
+ @self.server.list_tools()
706
+ async def handle_list_tools() -> ListToolsResult:
707
+ """List available tools."""
708
+ return ListToolsResult(tools=self.tools)
709
+
710
+ @self.server.call_tool()
711
+ async def handle_call_tool(name: str, arguments: dict) -> CallToolResult:
712
+ """Handle tool calls."""
713
+ try:
714
+ if not self.memory_db:
715
+ return CallToolResult(
716
+ content=[TextContent(
717
+ type="text",
718
+ text="Error: Memory database not initialized"
719
+ )],
720
+ isError=True
721
+ )
722
+
723
+ if name == "recall_memories":
724
+ return await handle_recall_memories(self.memory_db, arguments)
725
+ elif name == "store_memory":
726
+ return await handle_store_memory(self.memory_db, arguments)
727
+ elif name == "get_memory":
728
+ return await handle_get_memory(self.memory_db, arguments)
729
+ elif name == "search_memories":
730
+ return await handle_search_memories(self.memory_db, arguments)
731
+ elif name == "update_memory":
732
+ return await handle_update_memory(self.memory_db, arguments)
733
+ elif name == "delete_memory":
734
+ return await handle_delete_memory(self.memory_db, arguments)
735
+ elif name == "create_relationship":
736
+ return await handle_create_relationship(self.memory_db, arguments)
737
+ elif name == "get_related_memories":
738
+ return await handle_get_related_memories(self.memory_db, arguments)
739
+ elif name == "get_memory_statistics":
740
+ return await handle_get_memory_statistics(self.memory_db, arguments)
741
+ elif name == "get_recent_activity":
742
+ return await handle_get_recent_activity(self.memory_db, arguments)
743
+ elif name == "search_relationships_by_context":
744
+ return await handle_search_relationships_by_context(self.memory_db, arguments)
745
+ # Contextual search tool
746
+ elif name == "contextual_search":
747
+ return await handle_contextual_search(self.memory_db, arguments)
748
+ # Temporal tools deferred (backend methods available via Python API)
749
+ # Advanced relationship tools
750
+ elif name in ["find_memory_path", "analyze_memory_clusters", "find_bridge_memories",
751
+ "suggest_relationship_type", "reinforce_relationship",
752
+ "get_relationship_types_by_category", "analyze_graph_metrics"]:
753
+ # Dispatch to advanced handlers
754
+ method_name = f"handle_{name}"
755
+ handler = getattr(self.advanced_handlers, method_name, None)
756
+ if handler:
757
+ return await handler(arguments)
758
+ else:
759
+ return CallToolResult(
760
+ content=[TextContent(type="text", text=f"Handler not found: {name}")],
761
+ isError=True
762
+ )
763
+
764
+ # Migration tools
765
+ elif name in ["migrate_database", "validate_migration"]:
766
+ handler = MIGRATION_TOOL_HANDLERS.get(name)
767
+ if handler:
768
+ # Migration tools don't need memory_db parameter - they create their own connections
769
+ result = await handler(**arguments)
770
+ # Format result as MCP response
771
+ import json
772
+ return CallToolResult(
773
+ content=[TextContent(
774
+ type="text",
775
+ text=json.dumps(result, indent=2)
776
+ )],
777
+ isError=not result.get("success", False)
778
+ )
779
+ else:
780
+ return CallToolResult(
781
+ content=[TextContent(type="text", text=f"Migration handler not found: {name}")],
782
+ isError=True
783
+ )
784
+
785
+ else:
786
+ return CallToolResult(
787
+ content=[TextContent(
788
+ type="text",
789
+ text=f"Unknown tool: {name}"
790
+ )],
791
+ isError=True
792
+ )
793
+
794
+ except Exception as e:
795
+ logger.error(f"Error handling tool call {name}: {e}")
796
+ return CallToolResult(
797
+ content=[TextContent(
798
+ type="text",
799
+ text=f"Error: {str(e)}"
800
+ )],
801
+ isError=True
802
+ )
803
+
804
+ async def initialize(self):
805
+ """Initialize the server and establish database connection."""
806
+ try:
807
+ # Initialize backend connection using factory
808
+ from .backends.factory import BackendFactory
809
+ self.db_connection = await BackendFactory.create_backend()
810
+
811
+ # Initialize memory database - choose wrapper based on backend type
812
+ if isinstance(self.db_connection, SQLiteFallbackBackend):
813
+ logger.info("Using SQLiteMemoryDatabase for SQLite backend")
814
+ self.memory_db = SQLiteMemoryDatabase(self.db_connection)
815
+ elif isinstance(self.db_connection, CloudBackend):
816
+ logger.info("Using CloudMemoryDatabase for Cloud backend")
817
+ self.memory_db = CloudMemoryDatabase(self.db_connection)
818
+ else:
819
+ logger.info("Using MemoryDatabase for Cypher-compatible backend")
820
+ self.memory_db = MemoryDatabase(self.db_connection)
821
+
822
+ await self.memory_db.initialize_schema()
823
+
824
+ # Initialize advanced relationship handlers
825
+ self.advanced_handlers = AdvancedRelationshipHandlers(self.memory_db)
826
+
827
+ backend_name = getattr(self.db_connection, 'backend_name', lambda: 'Unknown')
828
+ if callable(backend_name):
829
+ backend_name = backend_name()
830
+ logger.info(f"Claude Memory Server initialized successfully")
831
+ logger.info(f"Backend: {backend_name}")
832
+ logger.info(f"Tool profile: {Config.TOOL_PROFILE.upper()} ({len(self.tools)} tools enabled)")
833
+
834
+ except Exception as e:
835
+ logger.error(f"Failed to initialize server: {e}")
836
+ raise
837
+
838
+ async def cleanup(self):
839
+ """Clean up resources."""
840
+ if self.db_connection:
841
+ await self.db_connection.close()
842
+ logger.info("Claude Memory Server cleanup completed")
843
+
844
+
845
+ async def main():
846
+ """Main entry point for the MCP server."""
847
+ server = ClaudeMemoryServer()
848
+
849
+ try:
850
+ # Initialize the server
851
+ await server.initialize()
852
+
853
+ # Create notification options and capabilities BEFORE passing to InitializationOptions
854
+ # This ensures proper object instantiation and avoids potential GC or scoping issues
855
+ notification_options = NotificationOptions()
856
+ capabilities = server.server.get_capabilities(
857
+ notification_options=notification_options,
858
+ experimental_capabilities={},
859
+ )
860
+
861
+ # Run the stdio server
862
+ async with stdio_server() as (read_stream, write_stream):
863
+ await server.server.run(
864
+ read_stream,
865
+ write_stream,
866
+ InitializationOptions(
867
+ server_name="claude-memory",
868
+ server_version=__version__,
869
+ capabilities=capabilities,
870
+ ),
871
+ )
872
+
873
+ except KeyboardInterrupt:
874
+ logger.info("Received interrupt signal")
875
+ except Exception as e:
876
+ logger.error(f"Server error: {e}")
877
+ raise
878
+ finally:
879
+ await server.cleanup()
880
+
881
+
882
+ if __name__ == "__main__":
883
+ asyncio.run(main())