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.

Files changed (82) hide show
  1. basic_memory/__init__.py +1 -1
  2. basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +5 -9
  3. basic_memory/api/app.py +10 -4
  4. basic_memory/api/routers/knowledge_router.py +25 -8
  5. basic_memory/api/routers/project_router.py +99 -4
  6. basic_memory/cli/app.py +9 -28
  7. basic_memory/cli/auth.py +277 -0
  8. basic_memory/cli/commands/cloud/__init__.py +5 -0
  9. basic_memory/cli/commands/cloud/api_client.py +112 -0
  10. basic_memory/cli/commands/cloud/bisync_commands.py +818 -0
  11. basic_memory/cli/commands/cloud/core_commands.py +288 -0
  12. basic_memory/cli/commands/cloud/mount_commands.py +295 -0
  13. basic_memory/cli/commands/cloud/rclone_config.py +288 -0
  14. basic_memory/cli/commands/cloud/rclone_installer.py +198 -0
  15. basic_memory/cli/commands/command_utils.py +60 -0
  16. basic_memory/cli/commands/import_memory_json.py +0 -4
  17. basic_memory/cli/commands/mcp.py +16 -4
  18. basic_memory/cli/commands/project.py +139 -142
  19. basic_memory/cli/commands/status.py +34 -22
  20. basic_memory/cli/commands/sync.py +45 -228
  21. basic_memory/cli/commands/tool.py +87 -16
  22. basic_memory/cli/main.py +1 -0
  23. basic_memory/config.py +76 -12
  24. basic_memory/db.py +104 -3
  25. basic_memory/deps.py +20 -3
  26. basic_memory/file_utils.py +37 -13
  27. basic_memory/ignore_utils.py +295 -0
  28. basic_memory/markdown/plugins.py +9 -7
  29. basic_memory/mcp/async_client.py +22 -10
  30. basic_memory/mcp/project_context.py +141 -0
  31. basic_memory/mcp/prompts/ai_assistant_guide.py +49 -4
  32. basic_memory/mcp/prompts/continue_conversation.py +1 -1
  33. basic_memory/mcp/prompts/recent_activity.py +116 -32
  34. basic_memory/mcp/prompts/search.py +1 -1
  35. basic_memory/mcp/prompts/utils.py +11 -4
  36. basic_memory/mcp/resources/ai_assistant_guide.md +179 -41
  37. basic_memory/mcp/resources/project_info.py +20 -6
  38. basic_memory/mcp/server.py +0 -37
  39. basic_memory/mcp/tools/__init__.py +5 -6
  40. basic_memory/mcp/tools/build_context.py +29 -19
  41. basic_memory/mcp/tools/canvas.py +19 -8
  42. basic_memory/mcp/tools/chatgpt_tools.py +178 -0
  43. basic_memory/mcp/tools/delete_note.py +67 -34
  44. basic_memory/mcp/tools/edit_note.py +55 -39
  45. basic_memory/mcp/tools/headers.py +44 -0
  46. basic_memory/mcp/tools/list_directory.py +18 -8
  47. basic_memory/mcp/tools/move_note.py +119 -41
  48. basic_memory/mcp/tools/project_management.py +61 -228
  49. basic_memory/mcp/tools/read_content.py +28 -12
  50. basic_memory/mcp/tools/read_note.py +83 -46
  51. basic_memory/mcp/tools/recent_activity.py +441 -42
  52. basic_memory/mcp/tools/search.py +82 -70
  53. basic_memory/mcp/tools/sync_status.py +5 -4
  54. basic_memory/mcp/tools/utils.py +19 -0
  55. basic_memory/mcp/tools/view_note.py +31 -6
  56. basic_memory/mcp/tools/write_note.py +65 -14
  57. basic_memory/models/knowledge.py +12 -6
  58. basic_memory/models/project.py +6 -2
  59. basic_memory/repository/entity_repository.py +29 -82
  60. basic_memory/repository/relation_repository.py +13 -0
  61. basic_memory/repository/repository.py +2 -2
  62. basic_memory/repository/search_repository.py +4 -2
  63. basic_memory/schemas/__init__.py +6 -0
  64. basic_memory/schemas/base.py +39 -11
  65. basic_memory/schemas/cloud.py +46 -0
  66. basic_memory/schemas/memory.py +90 -21
  67. basic_memory/schemas/project_info.py +9 -10
  68. basic_memory/schemas/sync_report.py +48 -0
  69. basic_memory/services/context_service.py +25 -11
  70. basic_memory/services/entity_service.py +75 -45
  71. basic_memory/services/initialization.py +30 -11
  72. basic_memory/services/project_service.py +13 -23
  73. basic_memory/sync/sync_service.py +145 -21
  74. basic_memory/sync/watch_service.py +101 -40
  75. basic_memory/utils.py +14 -4
  76. {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/METADATA +7 -6
  77. basic_memory-0.15.0.dist-info/RECORD +147 -0
  78. basic_memory/mcp/project_session.py +0 -120
  79. basic_memory-0.14.4.dist-info/RECORD +0 -133
  80. {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/WHEEL +0 -0
  81. {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/entry_points.txt +0 -0
  82. {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", # Optional: Folder to save in
81
+ folder="specs", # Required: Folder to save in
42
82
  tags=["search", "design"], # Optional: Tags for categorization
43
- verbose=True # Optional: Get parsing details
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="all", # Optional: Entity types to include
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", "label": "Search Design"}], # Nodes to display
75
- edges=[{"from": "note1", "to": "note2"}], # Connections
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
- [Use write_note() to record the conversation details]
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
- [Use build_context() to gather related information]
149
- [Then read_note() to access specific content]
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
- [Use search_notes() to find relevant notes]
158
- [Then build_context() to understand connections]
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
- verbose=True
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(..., verbose=True)
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. **Sync Issues**
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. **Proactively Record Context**
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
- 2. **Create a Rich Semantic Graph**
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
- 3. **Structure Content Thoughtfully**
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
- 4. **Navigate Knowledge Effectively**
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
- 5. **Help Users Maintain Their Knowledge**
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
- connection?"
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://project_info",
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() -> ProjectInfoResponse:
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.project_url
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")
@@ -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
- "get_current_project",
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
- project: Optional[str] = None,
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
- project: Optional project name to build context from. If not provided, uses current active project.
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
- # Build context from specific project
81
- build_context("memory://specs/search", project="work-project")
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 first to check project-specific sync status
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