basic-memory 0.14.4__py3-none-any.whl → 0.15.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of basic-memory might be problematic. Click here for more details.
- basic_memory/__init__.py +1 -1
- basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +5 -9
- basic_memory/api/app.py +10 -4
- basic_memory/api/routers/knowledge_router.py +25 -8
- basic_memory/api/routers/project_router.py +99 -4
- basic_memory/cli/app.py +9 -28
- basic_memory/cli/auth.py +277 -0
- basic_memory/cli/commands/cloud/__init__.py +5 -0
- basic_memory/cli/commands/cloud/api_client.py +112 -0
- basic_memory/cli/commands/cloud/bisync_commands.py +818 -0
- basic_memory/cli/commands/cloud/core_commands.py +288 -0
- basic_memory/cli/commands/cloud/mount_commands.py +295 -0
- basic_memory/cli/commands/cloud/rclone_config.py +288 -0
- basic_memory/cli/commands/cloud/rclone_installer.py +198 -0
- basic_memory/cli/commands/command_utils.py +60 -0
- basic_memory/cli/commands/import_memory_json.py +0 -4
- basic_memory/cli/commands/mcp.py +16 -4
- basic_memory/cli/commands/project.py +139 -142
- basic_memory/cli/commands/status.py +34 -22
- basic_memory/cli/commands/sync.py +45 -228
- basic_memory/cli/commands/tool.py +87 -16
- basic_memory/cli/main.py +1 -0
- basic_memory/config.py +76 -12
- basic_memory/db.py +104 -3
- basic_memory/deps.py +20 -3
- basic_memory/file_utils.py +37 -13
- basic_memory/ignore_utils.py +295 -0
- basic_memory/markdown/plugins.py +9 -7
- basic_memory/mcp/async_client.py +22 -10
- basic_memory/mcp/project_context.py +141 -0
- basic_memory/mcp/prompts/ai_assistant_guide.py +49 -4
- basic_memory/mcp/prompts/continue_conversation.py +1 -1
- basic_memory/mcp/prompts/recent_activity.py +116 -32
- basic_memory/mcp/prompts/search.py +1 -1
- basic_memory/mcp/prompts/utils.py +11 -4
- basic_memory/mcp/resources/ai_assistant_guide.md +179 -41
- basic_memory/mcp/resources/project_info.py +20 -6
- basic_memory/mcp/server.py +0 -37
- basic_memory/mcp/tools/__init__.py +5 -6
- basic_memory/mcp/tools/build_context.py +29 -19
- basic_memory/mcp/tools/canvas.py +19 -8
- basic_memory/mcp/tools/chatgpt_tools.py +178 -0
- basic_memory/mcp/tools/delete_note.py +67 -34
- basic_memory/mcp/tools/edit_note.py +55 -39
- basic_memory/mcp/tools/headers.py +44 -0
- basic_memory/mcp/tools/list_directory.py +18 -8
- basic_memory/mcp/tools/move_note.py +119 -41
- basic_memory/mcp/tools/project_management.py +61 -228
- basic_memory/mcp/tools/read_content.py +28 -12
- basic_memory/mcp/tools/read_note.py +83 -46
- basic_memory/mcp/tools/recent_activity.py +441 -42
- basic_memory/mcp/tools/search.py +82 -70
- basic_memory/mcp/tools/sync_status.py +5 -4
- basic_memory/mcp/tools/utils.py +19 -0
- basic_memory/mcp/tools/view_note.py +31 -6
- basic_memory/mcp/tools/write_note.py +65 -14
- basic_memory/models/knowledge.py +12 -6
- basic_memory/models/project.py +6 -2
- basic_memory/repository/entity_repository.py +29 -82
- basic_memory/repository/relation_repository.py +13 -0
- basic_memory/repository/repository.py +2 -2
- basic_memory/repository/search_repository.py +4 -2
- basic_memory/schemas/__init__.py +6 -0
- basic_memory/schemas/base.py +39 -11
- basic_memory/schemas/cloud.py +46 -0
- basic_memory/schemas/memory.py +90 -21
- basic_memory/schemas/project_info.py +9 -10
- basic_memory/schemas/sync_report.py +48 -0
- basic_memory/services/context_service.py +25 -11
- basic_memory/services/entity_service.py +75 -45
- basic_memory/services/initialization.py +30 -11
- basic_memory/services/project_service.py +13 -23
- basic_memory/sync/sync_service.py +145 -21
- basic_memory/sync/watch_service.py +101 -40
- basic_memory/utils.py +14 -4
- {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/METADATA +7 -6
- basic_memory-0.15.0.dist-info/RECORD +147 -0
- basic_memory/mcp/project_session.py +0 -120
- basic_memory-0.14.4.dist-info/RECORD +0 -133
- {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/WHEEL +0 -0
- {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/entry_points.txt +0 -0
- {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -14,6 +14,44 @@ natural conversations. The system automatically creates a semantic knowledge gra
|
|
|
14
14
|
- **Semantic**: Simple patterns create a structured knowledge graph
|
|
15
15
|
- **Persistent**: Knowledge persists across sessions and conversations
|
|
16
16
|
|
|
17
|
+
## Project Management and Configuration
|
|
18
|
+
|
|
19
|
+
Basic Memory uses a **stateless architecture** where each tool call can specify which project to work with. This provides three ways to determine the active project:
|
|
20
|
+
|
|
21
|
+
### Three-Tier Project Resolution
|
|
22
|
+
|
|
23
|
+
1. **CLI Constraint (Highest Priority)**: When Basic Memory is started with `--project project-name`, all operations are constrained to that project
|
|
24
|
+
2. **Explicit Project Parameter (Medium Priority)**: When you specify `project="project-name"` in tool calls
|
|
25
|
+
3. **Default Project Mode (Lowest Priority)**: When `default_project_mode=true` in configuration, tools automatically use the configured `default_project`
|
|
26
|
+
|
|
27
|
+
### Default Project Mode
|
|
28
|
+
|
|
29
|
+
When `default_project_mode` is enabled in the user's configuration:
|
|
30
|
+
- All tools become more convenient - no need to specify project repeatedly
|
|
31
|
+
- Perfect for users who primarily work with a single project
|
|
32
|
+
- Still allows explicit project specification when needed
|
|
33
|
+
- Falls back gracefully to multi-project mode if no default is configured
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
# With default_project_mode enabled, these are equivalent:
|
|
37
|
+
await write_note("My Note", "Content", "folder")
|
|
38
|
+
await write_note("My Note", "Content", "folder", project="default-project")
|
|
39
|
+
|
|
40
|
+
# You can still override with explicit project:
|
|
41
|
+
await write_note("My Note", "Content", "folder", project="other-project")
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Project Discovery
|
|
45
|
+
|
|
46
|
+
If you're unsure which project to use:
|
|
47
|
+
```python
|
|
48
|
+
# Discover available projects
|
|
49
|
+
projects = await list_memory_projects()
|
|
50
|
+
|
|
51
|
+
# See recent activity across projects for recommendations
|
|
52
|
+
activity = await recent_activity() # Shows cross-project activity and suggestions
|
|
53
|
+
```
|
|
54
|
+
|
|
17
55
|
## The Importance of the Knowledge Graph
|
|
18
56
|
|
|
19
57
|
**Basic Memory's value comes from connections between notes, not just the notes themselves.**
|
|
@@ -33,49 +71,120 @@ build these connections!
|
|
|
33
71
|
|
|
34
72
|
## Core Tools Reference
|
|
35
73
|
|
|
74
|
+
### Knowledge Creation and Editing
|
|
75
|
+
|
|
36
76
|
```python
|
|
37
77
|
# Writing knowledge - THE MOST IMPORTANT TOOL!
|
|
38
78
|
response = await write_note(
|
|
39
79
|
title="Search Design", # Required: Note title
|
|
40
80
|
content="# Search Design\n...", # Required: Note content
|
|
41
|
-
folder="specs", #
|
|
81
|
+
folder="specs", # Required: Folder to save in
|
|
42
82
|
tags=["search", "design"], # Optional: Tags for categorization
|
|
43
|
-
|
|
83
|
+
project="my-project" # Optional: Explicit project (uses default if not specified)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Editing existing notes
|
|
87
|
+
await edit_note(
|
|
88
|
+
identifier="Search Design", # Required: Note to edit
|
|
89
|
+
operation="append", # Required: append, prepend, find_replace, replace_section
|
|
90
|
+
content="\n## New Section\nAdditional content", # Required: Content to add/replace
|
|
91
|
+
project="my-project" # Optional: Explicit project
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Moving notes
|
|
95
|
+
await move_note(
|
|
96
|
+
identifier="Search Design", # Required: Note to move
|
|
97
|
+
destination_path="archive/old-search-design.md", # Required: New location
|
|
98
|
+
project="my-project" # Optional: Explicit project
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Deleting notes
|
|
102
|
+
success = await delete_note(
|
|
103
|
+
identifier="Old Draft", # Required: Note to delete
|
|
104
|
+
project="my-project" # Optional: Explicit project
|
|
44
105
|
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Knowledge Reading and Discovery
|
|
45
109
|
|
|
110
|
+
```python
|
|
46
111
|
# Reading knowledge
|
|
47
|
-
content = await read_note("Search Design") # By title
|
|
112
|
+
content = await read_note("Search Design") # By title (uses default project)
|
|
48
113
|
content = await read_note("specs/search-design") # By path
|
|
49
114
|
content = await read_note("memory://specs/search") # By memory URL
|
|
115
|
+
content = await read_note("Search Design", project="work-docs") # Explicit project
|
|
50
116
|
|
|
117
|
+
# Reading raw file content (text, images, binaries)
|
|
118
|
+
file_data = await read_content(
|
|
119
|
+
path="assets/diagram.png", # Required: File path
|
|
120
|
+
project="my-project" # Optional: Explicit project
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Viewing notes as formatted artifacts
|
|
124
|
+
await view_note(
|
|
125
|
+
identifier="Search Design", # Required: Note to view
|
|
126
|
+
project="my-project", # Optional: Explicit project
|
|
127
|
+
page=1, # Optional: Pagination
|
|
128
|
+
page_size=10 # Optional: Items per page
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Browsing directory contents
|
|
132
|
+
listing = await list_directory(
|
|
133
|
+
dir_name="/specs", # Optional: Directory path (default: "/")
|
|
134
|
+
depth=2, # Optional: Recursion depth
|
|
135
|
+
file_name_glob="*.md", # Optional: File pattern filter
|
|
136
|
+
project="my-project" # Optional: Explicit project
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Search and Context
|
|
141
|
+
|
|
142
|
+
```python
|
|
51
143
|
# Searching for knowledge
|
|
52
144
|
results = await search_notes(
|
|
53
|
-
query="authentication system", # Text to search for
|
|
145
|
+
query="authentication system", # Required: Text to search for
|
|
146
|
+
project="my-project", # Optional: Explicit project
|
|
54
147
|
page=1, # Optional: Pagination
|
|
55
|
-
page_size=10 # Optional: Results per page
|
|
148
|
+
page_size=10, # Optional: Results per page
|
|
149
|
+
search_type="text", # Optional: "text", "title", or "permalink"
|
|
150
|
+
types=["entity"], # Optional: Filter by content types
|
|
151
|
+
entity_types=["observation"], # Optional: Filter by entity types
|
|
152
|
+
after_date="1 week" # Optional: Recent content only
|
|
56
153
|
)
|
|
57
154
|
|
|
58
155
|
# Building context from the knowledge graph
|
|
59
156
|
context = await build_context(
|
|
60
|
-
url="memory://specs/search", # Starting point
|
|
157
|
+
url="memory://specs/search", # Required: Starting point
|
|
158
|
+
project="my-project", # Optional: Explicit project
|
|
61
159
|
depth=2, # Optional: How many hops to follow
|
|
62
|
-
timeframe="1 month" # Optional: Recent timeframe
|
|
160
|
+
timeframe="1 month", # Optional: Recent timeframe
|
|
161
|
+
max_related=10 # Optional: Max related items
|
|
63
162
|
)
|
|
64
163
|
|
|
65
164
|
# Checking recent changes
|
|
66
165
|
activity = await recent_activity(
|
|
67
|
-
type="
|
|
166
|
+
type=["entity", "relation"], # Optional: Entity types to include
|
|
68
167
|
depth=1, # Optional: Related items to include
|
|
69
|
-
timeframe="1 week" # Optional: Time window
|
|
168
|
+
timeframe="1 week", # Optional: Time window
|
|
169
|
+
project="my-project" # Optional: Explicit project (None for cross-project discovery)
|
|
70
170
|
)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Visualization and Project Management
|
|
71
174
|
|
|
175
|
+
```python
|
|
72
176
|
# Creating a knowledge visualization
|
|
73
177
|
canvas_result = await canvas(
|
|
74
|
-
nodes=[{"id": "note1", "
|
|
75
|
-
edges=[{"
|
|
76
|
-
title="Project Overview", # Canvas title
|
|
77
|
-
folder="diagrams" # Storage location
|
|
178
|
+
nodes=[{"id": "note1", "type": "file", "file": "Search Design.md"}], # Required: Nodes
|
|
179
|
+
edges=[{"id": "edge1", "fromNode": "note1", "toNode": "note2"}], # Required: Edges
|
|
180
|
+
title="Project Overview", # Required: Canvas title
|
|
181
|
+
folder="diagrams", # Required: Storage location
|
|
182
|
+
project="my-project" # Optional: Explicit project
|
|
78
183
|
)
|
|
184
|
+
|
|
185
|
+
# Project management
|
|
186
|
+
projects = await list_memory_projects() # List all available projects
|
|
187
|
+
project_info = await project_info(project="my-project") # Get project statistics
|
|
79
188
|
```
|
|
80
189
|
|
|
81
190
|
## memory:// URLs Explained
|
|
@@ -135,27 +244,31 @@ Users will interact with Basic Memory in patterns like:
|
|
|
135
244
|
1. **Creating knowledge**:
|
|
136
245
|
```
|
|
137
246
|
Human: "Let's write up what we discussed about search."
|
|
138
|
-
|
|
247
|
+
|
|
139
248
|
You: I'll create a note capturing our discussion about the search functionality.
|
|
140
|
-
|
|
249
|
+
await write_note(
|
|
250
|
+
title="Search Functionality Discussion",
|
|
251
|
+
content="# Search Functionality Discussion\n...",
|
|
252
|
+
folder="discussions"
|
|
253
|
+
)
|
|
141
254
|
```
|
|
142
255
|
|
|
143
256
|
2. **Referencing existing knowledge**:
|
|
144
257
|
```
|
|
145
258
|
Human: "Take a look at memory://specs/search"
|
|
146
|
-
|
|
259
|
+
|
|
147
260
|
You: I'll examine that information.
|
|
148
|
-
|
|
149
|
-
|
|
261
|
+
context = await build_context(url="memory://specs/search")
|
|
262
|
+
content = await read_note("specs/search")
|
|
150
263
|
```
|
|
151
264
|
|
|
152
265
|
3. **Finding information**:
|
|
153
266
|
```
|
|
154
267
|
Human: "What were our decisions about auth?"
|
|
155
|
-
|
|
268
|
+
|
|
156
269
|
You: Let me find that information for you.
|
|
157
|
-
|
|
158
|
-
|
|
270
|
+
results = await search_notes(query="auth decisions")
|
|
271
|
+
context = await build_context(url=f"memory://{results[0].permalink}")
|
|
159
272
|
```
|
|
160
273
|
|
|
161
274
|
## Key Things to Remember
|
|
@@ -263,7 +376,7 @@ When creating relations, you can:
|
|
|
263
376
|
# Example workflow for creating notes with effective relations
|
|
264
377
|
async def create_note_with_effective_relations():
|
|
265
378
|
# Search for existing entities to reference
|
|
266
|
-
search_results = await search_notes("travel")
|
|
379
|
+
search_results = await search_notes(query="travel")
|
|
267
380
|
existing_entities = [result.title for result in search_results.primary_results]
|
|
268
381
|
|
|
269
382
|
# Check if specific entities exist
|
|
@@ -297,7 +410,7 @@ async def create_note_with_effective_relations():
|
|
|
297
410
|
|
|
298
411
|
# Now create the note with both verified and forward relations
|
|
299
412
|
content = f"""# Tokyo Neighborhood Guide
|
|
300
|
-
|
|
413
|
+
|
|
301
414
|
## Overview
|
|
302
415
|
Details about different Tokyo neighborhoods and their unique characteristics.
|
|
303
416
|
|
|
@@ -313,7 +426,7 @@ Details about different Tokyo neighborhoods and their unique characteristics.
|
|
|
313
426
|
result = await write_note(
|
|
314
427
|
title="Tokyo Neighborhood Guide",
|
|
315
428
|
content=content,
|
|
316
|
-
|
|
429
|
+
folder="travel"
|
|
317
430
|
)
|
|
318
431
|
|
|
319
432
|
# You can check which relations were resolved and which are forward references
|
|
@@ -335,7 +448,7 @@ Common issues to watch for:
|
|
|
335
448
|
content = await read_note("Document")
|
|
336
449
|
except:
|
|
337
450
|
# Try search instead
|
|
338
|
-
results = await search_notes("Document")
|
|
451
|
+
results = await search_notes(query="Document")
|
|
339
452
|
if results and results.primary_results:
|
|
340
453
|
# Found something similar
|
|
341
454
|
content = await read_note(results.primary_results[0].permalink)
|
|
@@ -343,23 +456,40 @@ Common issues to watch for:
|
|
|
343
456
|
|
|
344
457
|
2. **Forward References (Unresolved Relations)**
|
|
345
458
|
```python
|
|
346
|
-
response = await write_note(
|
|
459
|
+
response = await write_note(
|
|
460
|
+
title="My Note",
|
|
461
|
+
content="Content with [[Forward Reference]]",
|
|
462
|
+
folder="notes"
|
|
463
|
+
)
|
|
347
464
|
# Check for forward references (unresolved relations)
|
|
348
465
|
forward_refs = []
|
|
349
466
|
for relation in response.get('relations', []):
|
|
350
467
|
if not relation.get('target_id'):
|
|
351
468
|
forward_refs.append(relation.get('to_name'))
|
|
352
|
-
|
|
469
|
+
|
|
353
470
|
if forward_refs:
|
|
354
471
|
# This is a feature, not an error! Inform the user about forward references
|
|
355
472
|
print(f"Note created with forward references to: {forward_refs}")
|
|
356
473
|
print("These will be automatically linked when those notes are created.")
|
|
357
|
-
|
|
474
|
+
|
|
358
475
|
# Optionally suggest creating those entities now
|
|
359
476
|
print("Would you like me to create any of these notes now to complete the connections?")
|
|
360
477
|
```
|
|
361
478
|
|
|
362
|
-
3. **
|
|
479
|
+
3. **Project Discovery Issues**
|
|
480
|
+
```python
|
|
481
|
+
# If user asks about content but no default project is configured
|
|
482
|
+
try:
|
|
483
|
+
results = await search_notes(query="user query")
|
|
484
|
+
except Exception as e:
|
|
485
|
+
if "project" in str(e).lower():
|
|
486
|
+
# Show available projects and ask user to choose
|
|
487
|
+
projects = await list_memory_projects()
|
|
488
|
+
print(f"Available projects: {[p.name for p in projects]}")
|
|
489
|
+
print("Which project should I search in?")
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
4. **Sync Issues**
|
|
363
493
|
```python
|
|
364
494
|
# If information seems outdated
|
|
365
495
|
activity = await recent_activity(timeframe="1 hour")
|
|
@@ -369,14 +499,21 @@ Common issues to watch for:
|
|
|
369
499
|
|
|
370
500
|
## Best Practices
|
|
371
501
|
|
|
372
|
-
1. **
|
|
502
|
+
1. **Smart Project Management**
|
|
503
|
+
- **For new users**: Call `recent_activity()` without project parameter to discover active projects and get recommendations
|
|
504
|
+
- **For known projects**: Use explicit project parameters when switching between multiple projects
|
|
505
|
+
- **For single-project users**: Rely on default_project_mode for convenience
|
|
506
|
+
- **When uncertain**: Use `list_memory_projects()` to show available options and ask the user
|
|
507
|
+
- **Remember choices**: Once a user indicates their preferred project, use it consistently throughout the conversation
|
|
508
|
+
|
|
509
|
+
2. **Proactively Record Context**
|
|
373
510
|
- Offer to capture important discussions
|
|
374
511
|
- Record decisions, rationales, and conclusions
|
|
375
512
|
- Link to related topics
|
|
376
513
|
- Ask for permission first: "Would you like me to save our discussion about [topic]?"
|
|
377
514
|
- Confirm when complete: "I've saved our discussion to Basic Memory"
|
|
378
515
|
|
|
379
|
-
|
|
516
|
+
3. **Create a Rich Semantic Graph**
|
|
380
517
|
- **Add meaningful observations**: Include at least 3-5 categorized observations in each note
|
|
381
518
|
- **Create deliberate relations**: Connect each note to at least 2-3 related entities
|
|
382
519
|
- **Use existing entities**: Before creating a new relation, search for existing entities
|
|
@@ -386,7 +523,7 @@ Common issues to watch for:
|
|
|
386
523
|
of "relates_to")
|
|
387
524
|
- **Consider bidirectional relations**: When appropriate, create inverse relations in both entities
|
|
388
525
|
|
|
389
|
-
|
|
526
|
+
4. **Structure Content Thoughtfully**
|
|
390
527
|
- Use clear, descriptive titles
|
|
391
528
|
- Organize with logical sections (Context, Decision, Implementation, etc.)
|
|
392
529
|
- Include relevant context and background
|
|
@@ -394,20 +531,21 @@ Common issues to watch for:
|
|
|
394
531
|
- Use a consistent format for similar types of notes
|
|
395
532
|
- Balance detail with conciseness
|
|
396
533
|
|
|
397
|
-
|
|
398
|
-
- Start with specific searches
|
|
399
|
-
- Follow relation paths
|
|
534
|
+
5. **Navigate Knowledge Effectively**
|
|
535
|
+
- Start with specific searches using `search_notes()`
|
|
536
|
+
- Follow relation paths with `build_context()`
|
|
400
537
|
- Combine information from multiple sources
|
|
401
|
-
- Verify information is current
|
|
538
|
+
- Verify information is current with `recent_activity()`
|
|
402
539
|
- Build a complete picture before responding
|
|
540
|
+
- Use appropriate project context for searches
|
|
403
541
|
|
|
404
|
-
|
|
405
|
-
- Suggest organizing related topics
|
|
406
|
-
- Identify potential duplicates
|
|
542
|
+
6. **Help Users Maintain Their Knowledge**
|
|
543
|
+
- Suggest organizing related topics across projects when appropriate
|
|
544
|
+
- Identify potential duplicates using search
|
|
407
545
|
- Recommend adding relations between topics
|
|
408
546
|
- Offer to create summaries of scattered information
|
|
409
|
-
- Suggest potential missing relations: "I notice this might relate to [topic], would you like me to add that
|
|
410
|
-
|
|
547
|
+
- Suggest potential missing relations: "I notice this might relate to [topic], would you like me to add that connection?"
|
|
548
|
+
- Help users decide when to use explicit vs default project parameters
|
|
411
549
|
|
|
412
550
|
Built with ♥️ b
|
|
413
551
|
y Basic Machines
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
"""Project info tool for Basic Memory MCP server."""
|
|
2
2
|
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
3
5
|
from loguru import logger
|
|
6
|
+
from fastmcp import Context
|
|
4
7
|
|
|
5
|
-
from basic_memory.mcp.project_session import get_active_project
|
|
6
8
|
from basic_memory.mcp.async_client import client
|
|
9
|
+
from basic_memory.mcp.project_context import get_active_project
|
|
7
10
|
from basic_memory.mcp.server import mcp
|
|
8
11
|
from basic_memory.mcp.tools.utils import call_get
|
|
9
12
|
from basic_memory.schemas import ProjectInfoResponse
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
@mcp.resource(
|
|
13
|
-
uri="memory://
|
|
16
|
+
uri="memory://{project}/info",
|
|
14
17
|
description="Get information and statistics about the current Basic Memory project.",
|
|
15
18
|
)
|
|
16
|
-
async def project_info(
|
|
19
|
+
async def project_info(
|
|
20
|
+
project: Optional[str] = None, context: Context | None = None
|
|
21
|
+
) -> ProjectInfoResponse:
|
|
17
22
|
"""Get comprehensive information about the current Basic Memory project.
|
|
18
23
|
|
|
19
24
|
This tool provides detailed statistics and status information about your
|
|
@@ -31,13 +36,22 @@ async def project_info() -> ProjectInfoResponse:
|
|
|
31
36
|
- Monitor growth and activity over time
|
|
32
37
|
- Identify potential issues like unresolved relations
|
|
33
38
|
|
|
39
|
+
Args:
|
|
40
|
+
project: Optional project name. If not provided, uses default_project
|
|
41
|
+
(if default_project_mode=true) or CLI constraint. If unknown,
|
|
42
|
+
use list_memory_projects() to discover available projects.
|
|
43
|
+
context: Optional FastMCP context for performance caching.
|
|
44
|
+
|
|
34
45
|
Returns:
|
|
35
46
|
Detailed project information and statistics
|
|
36
47
|
|
|
37
48
|
Examples:
|
|
38
|
-
# Get information about the current project
|
|
49
|
+
# Get information about the current/default project
|
|
39
50
|
info = await project_info()
|
|
40
51
|
|
|
52
|
+
# Get information about a specific project
|
|
53
|
+
info = await project_info(project="my-project")
|
|
54
|
+
|
|
41
55
|
# Check entity counts
|
|
42
56
|
print(f"Total entities: {info.statistics.total_entities}")
|
|
43
57
|
|
|
@@ -45,8 +59,8 @@ async def project_info() -> ProjectInfoResponse:
|
|
|
45
59
|
print(f"Basic Memory version: {info.system.version}")
|
|
46
60
|
"""
|
|
47
61
|
logger.info("Getting project info")
|
|
48
|
-
project_config = get_active_project()
|
|
49
|
-
project_url = project_config.
|
|
62
|
+
project_config = await get_active_project(client, project, context)
|
|
63
|
+
project_url = project_config.permalink
|
|
50
64
|
|
|
51
65
|
# Call the API endpoint
|
|
52
66
|
response = await call_get(client, f"{project_url}/project/info")
|
basic_memory/mcp/server.py
CHANGED
|
@@ -2,45 +2,8 @@
|
|
|
2
2
|
Basic Memory FastMCP server.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
import asyncio
|
|
6
|
-
from contextlib import asynccontextmanager
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from typing import AsyncIterator, Optional, Any
|
|
9
|
-
|
|
10
5
|
from fastmcp import FastMCP
|
|
11
6
|
|
|
12
|
-
from basic_memory.config import ConfigManager
|
|
13
|
-
from basic_memory.services.initialization import initialize_app
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@dataclass
|
|
17
|
-
class AppContext:
|
|
18
|
-
watch_task: Optional[asyncio.Task]
|
|
19
|
-
migration_manager: Optional[Any] = None
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@asynccontextmanager
|
|
23
|
-
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: # pragma: no cover
|
|
24
|
-
""" """
|
|
25
|
-
# defer import so tests can monkeypatch
|
|
26
|
-
from basic_memory.mcp.project_session import session
|
|
27
|
-
|
|
28
|
-
app_config = ConfigManager().config
|
|
29
|
-
# Initialize on startup (now returns migration_manager)
|
|
30
|
-
migration_manager = await initialize_app(app_config)
|
|
31
|
-
|
|
32
|
-
# Initialize project session with default project
|
|
33
|
-
session.initialize(app_config.default_project)
|
|
34
|
-
|
|
35
|
-
try:
|
|
36
|
-
yield AppContext(watch_task=None, migration_manager=migration_manager)
|
|
37
|
-
finally:
|
|
38
|
-
# Cleanup on shutdown - migration tasks will be cancelled automatically
|
|
39
|
-
pass
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# Create the shared server instance with custom Stytch auth
|
|
43
7
|
mcp = FastMCP(
|
|
44
8
|
name="Basic Memory",
|
|
45
|
-
lifespan=app_lifespan,
|
|
46
9
|
)
|
|
@@ -21,13 +21,13 @@ from basic_memory.mcp.tools.move_note import move_note
|
|
|
21
21
|
from basic_memory.mcp.tools.sync_status import sync_status
|
|
22
22
|
from basic_memory.mcp.tools.project_management import (
|
|
23
23
|
list_memory_projects,
|
|
24
|
-
switch_project,
|
|
25
|
-
get_current_project,
|
|
26
|
-
set_default_project,
|
|
27
24
|
create_memory_project,
|
|
28
25
|
delete_project,
|
|
29
26
|
)
|
|
30
27
|
|
|
28
|
+
# ChatGPT-compatible tools
|
|
29
|
+
from basic_memory.mcp.tools.chatgpt_tools import search, fetch
|
|
30
|
+
|
|
31
31
|
__all__ = [
|
|
32
32
|
"build_context",
|
|
33
33
|
"canvas",
|
|
@@ -35,16 +35,15 @@ __all__ = [
|
|
|
35
35
|
"delete_note",
|
|
36
36
|
"delete_project",
|
|
37
37
|
"edit_note",
|
|
38
|
-
"
|
|
38
|
+
"fetch",
|
|
39
39
|
"list_directory",
|
|
40
40
|
"list_memory_projects",
|
|
41
41
|
"move_note",
|
|
42
42
|
"read_content",
|
|
43
43
|
"read_note",
|
|
44
44
|
"recent_activity",
|
|
45
|
+
"search",
|
|
45
46
|
"search_notes",
|
|
46
|
-
"set_default_project",
|
|
47
|
-
"switch_project",
|
|
48
47
|
"sync_status",
|
|
49
48
|
"view_note",
|
|
50
49
|
"write_note",
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
5
|
from loguru import logger
|
|
6
|
+
from fastmcp import Context
|
|
6
7
|
|
|
7
8
|
from basic_memory.mcp.async_client import client
|
|
9
|
+
from basic_memory.mcp.project_context import get_active_project
|
|
8
10
|
from basic_memory.mcp.server import mcp
|
|
9
11
|
from basic_memory.mcp.tools.utils import call_get
|
|
10
|
-
from basic_memory.mcp.project_session import get_active_project
|
|
11
12
|
from basic_memory.schemas.base import TimeFrame
|
|
12
13
|
from basic_memory.schemas.memory import (
|
|
13
14
|
GraphContext,
|
|
@@ -17,18 +18,19 @@ from basic_memory.schemas.memory import (
|
|
|
17
18
|
|
|
18
19
|
type StringOrInt = str | int
|
|
19
20
|
|
|
21
|
+
|
|
20
22
|
@mcp.tool(
|
|
21
23
|
description="""Build context from a memory:// URI to continue conversations naturally.
|
|
22
|
-
|
|
24
|
+
|
|
23
25
|
Use this to follow up on previous discussions or explore related topics.
|
|
24
|
-
|
|
26
|
+
|
|
25
27
|
Memory URL Format:
|
|
26
|
-
- Use paths like "folder/note" or "memory://folder/note"
|
|
28
|
+
- Use paths like "folder/note" or "memory://folder/note"
|
|
27
29
|
- Pattern matching: "folder/*" matches all notes in folder
|
|
28
30
|
- Valid characters: letters, numbers, hyphens, underscores, forward slashes
|
|
29
31
|
- Avoid: double slashes (//), angle brackets (<>), quotes, pipes (|)
|
|
30
32
|
- Examples: "specs/search", "projects/basic-memory", "notes/*"
|
|
31
|
-
|
|
33
|
+
|
|
32
34
|
Timeframes support natural language like:
|
|
33
35
|
- "2 days ago", "last week", "today", "3 months ago"
|
|
34
36
|
- Or standard formats like "7d", "24h"
|
|
@@ -36,27 +38,34 @@ type StringOrInt = str | int
|
|
|
36
38
|
)
|
|
37
39
|
async def build_context(
|
|
38
40
|
url: MemoryUrl,
|
|
41
|
+
project: Optional[str] = None,
|
|
39
42
|
depth: Optional[StringOrInt] = 1,
|
|
40
43
|
timeframe: Optional[TimeFrame] = "7d",
|
|
41
44
|
page: int = 1,
|
|
42
45
|
page_size: int = 10,
|
|
43
46
|
max_related: int = 10,
|
|
44
|
-
|
|
47
|
+
context: Context | None = None,
|
|
45
48
|
) -> GraphContext:
|
|
46
|
-
"""Get context needed to continue a discussion.
|
|
49
|
+
"""Get context needed to continue a discussion within a specific project.
|
|
47
50
|
|
|
48
51
|
This tool enables natural continuation of discussions by loading relevant context
|
|
49
52
|
from memory:// URIs. It uses pattern matching to find relevant content and builds
|
|
50
53
|
a rich context graph of related information.
|
|
51
54
|
|
|
55
|
+
Project Resolution:
|
|
56
|
+
Server resolves projects in this order: Single Project Mode → project parameter → default project.
|
|
57
|
+
If project unknown, use list_memory_projects() or recent_activity() first.
|
|
58
|
+
|
|
52
59
|
Args:
|
|
60
|
+
project: Project name to build context from. Optional - server will resolve using hierarchy.
|
|
61
|
+
If unknown, use list_memory_projects() to discover available projects.
|
|
53
62
|
url: memory:// URI pointing to discussion content (e.g. memory://specs/search)
|
|
54
63
|
depth: How many relation hops to traverse (1-3 recommended for performance)
|
|
55
64
|
timeframe: How far back to look. Supports natural language like "2 days ago", "last week"
|
|
56
65
|
page: Page number of results to return (default: 1)
|
|
57
66
|
page_size: Number of results to return per page (default: 10)
|
|
58
67
|
max_related: Maximum number of related results to return (default: 10)
|
|
59
|
-
|
|
68
|
+
context: Optional FastMCP context for performance caching.
|
|
60
69
|
|
|
61
70
|
Returns:
|
|
62
71
|
GraphContext containing:
|
|
@@ -66,34 +75,35 @@ async def build_context(
|
|
|
66
75
|
|
|
67
76
|
Examples:
|
|
68
77
|
# Continue a specific discussion
|
|
69
|
-
build_context("memory://specs/search")
|
|
78
|
+
build_context("my-project", "memory://specs/search")
|
|
70
79
|
|
|
71
80
|
# Get deeper context about a component
|
|
72
|
-
build_context("memory://components/memory-service", depth=2)
|
|
81
|
+
build_context("work-docs", "memory://components/memory-service", depth=2)
|
|
73
82
|
|
|
74
83
|
# Look at recent changes to a specification
|
|
75
|
-
build_context("memory://specs/document-format", timeframe="today")
|
|
84
|
+
build_context("research", "memory://specs/document-format", timeframe="today")
|
|
76
85
|
|
|
77
86
|
# Research the history of a feature
|
|
78
|
-
build_context("memory://features/knowledge-graph", timeframe="3 months ago")
|
|
87
|
+
build_context("dev-notes", "memory://features/knowledge-graph", timeframe="3 months ago")
|
|
79
88
|
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
Raises:
|
|
90
|
+
ToolError: If project doesn't exist or depth parameter is invalid
|
|
82
91
|
"""
|
|
83
|
-
logger.info(f"Building context from {url}")
|
|
84
|
-
|
|
92
|
+
logger.info(f"Building context from {url} in project {project}")
|
|
93
|
+
|
|
85
94
|
# Convert string depth to integer if needed
|
|
86
95
|
if isinstance(depth, str):
|
|
87
96
|
try:
|
|
88
97
|
depth = int(depth)
|
|
89
98
|
except ValueError:
|
|
90
99
|
from mcp.server.fastmcp.exceptions import ToolError
|
|
100
|
+
|
|
91
101
|
raise ToolError(f"Invalid depth parameter: '{depth}' is not a valid integer")
|
|
92
|
-
|
|
102
|
+
|
|
93
103
|
# URL is already validated and normalized by MemoryUrl type annotation
|
|
94
104
|
|
|
95
|
-
# Get the active project
|
|
96
|
-
active_project = get_active_project(project)
|
|
105
|
+
# Get the active project using the new stateless approach
|
|
106
|
+
active_project = await get_active_project(client, project, context)
|
|
97
107
|
|
|
98
108
|
# Check migration status and wait briefly if needed
|
|
99
109
|
from basic_memory.mcp.tools.utils import wait_for_migration_or_return_status
|