basic-memory 0.7.0__py3-none-any.whl → 0.17.4__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 (195) hide show
  1. basic_memory/__init__.py +5 -1
  2. basic_memory/alembic/alembic.ini +119 -0
  3. basic_memory/alembic/env.py +130 -20
  4. basic_memory/alembic/migrations.py +4 -9
  5. basic_memory/alembic/versions/314f1ea54dc4_add_postgres_full_text_search_support_.py +131 -0
  6. basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py +51 -0
  7. basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py +120 -0
  8. basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py +112 -0
  9. basic_memory/alembic/versions/6830751f5fb6_merge_multiple_heads.py +24 -0
  10. basic_memory/alembic/versions/9d9c1cb7d8f5_add_mtime_and_size_columns_to_entity_.py +49 -0
  11. basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +49 -0
  12. basic_memory/alembic/versions/a2b3c4d5e6f7_add_search_index_entity_cascade.py +56 -0
  13. basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py +44 -0
  14. basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +113 -0
  15. basic_memory/alembic/versions/e7e1f4367280_add_scan_watermark_tracking_to_project.py +37 -0
  16. basic_memory/alembic/versions/f8a9b2c3d4e5_add_pg_trgm_for_fuzzy_link_resolution.py +239 -0
  17. basic_memory/alembic/versions/g9a0b3c4d5e6_add_external_id_to_project_and_entity.py +173 -0
  18. basic_memory/api/app.py +87 -20
  19. basic_memory/api/container.py +133 -0
  20. basic_memory/api/routers/__init__.py +4 -1
  21. basic_memory/api/routers/directory_router.py +84 -0
  22. basic_memory/api/routers/importer_router.py +152 -0
  23. basic_memory/api/routers/knowledge_router.py +180 -23
  24. basic_memory/api/routers/management_router.py +80 -0
  25. basic_memory/api/routers/memory_router.py +9 -64
  26. basic_memory/api/routers/project_router.py +460 -0
  27. basic_memory/api/routers/prompt_router.py +260 -0
  28. basic_memory/api/routers/resource_router.py +136 -11
  29. basic_memory/api/routers/search_router.py +5 -5
  30. basic_memory/api/routers/utils.py +169 -0
  31. basic_memory/api/template_loader.py +292 -0
  32. basic_memory/api/v2/__init__.py +35 -0
  33. basic_memory/api/v2/routers/__init__.py +21 -0
  34. basic_memory/api/v2/routers/directory_router.py +93 -0
  35. basic_memory/api/v2/routers/importer_router.py +181 -0
  36. basic_memory/api/v2/routers/knowledge_router.py +427 -0
  37. basic_memory/api/v2/routers/memory_router.py +130 -0
  38. basic_memory/api/v2/routers/project_router.py +359 -0
  39. basic_memory/api/v2/routers/prompt_router.py +269 -0
  40. basic_memory/api/v2/routers/resource_router.py +286 -0
  41. basic_memory/api/v2/routers/search_router.py +73 -0
  42. basic_memory/cli/app.py +80 -10
  43. basic_memory/cli/auth.py +300 -0
  44. basic_memory/cli/commands/__init__.py +15 -2
  45. basic_memory/cli/commands/cloud/__init__.py +6 -0
  46. basic_memory/cli/commands/cloud/api_client.py +127 -0
  47. basic_memory/cli/commands/cloud/bisync_commands.py +110 -0
  48. basic_memory/cli/commands/cloud/cloud_utils.py +108 -0
  49. basic_memory/cli/commands/cloud/core_commands.py +195 -0
  50. basic_memory/cli/commands/cloud/rclone_commands.py +397 -0
  51. basic_memory/cli/commands/cloud/rclone_config.py +110 -0
  52. basic_memory/cli/commands/cloud/rclone_installer.py +263 -0
  53. basic_memory/cli/commands/cloud/upload.py +240 -0
  54. basic_memory/cli/commands/cloud/upload_command.py +124 -0
  55. basic_memory/cli/commands/command_utils.py +99 -0
  56. basic_memory/cli/commands/db.py +87 -12
  57. basic_memory/cli/commands/format.py +198 -0
  58. basic_memory/cli/commands/import_chatgpt.py +47 -223
  59. basic_memory/cli/commands/import_claude_conversations.py +48 -171
  60. basic_memory/cli/commands/import_claude_projects.py +53 -160
  61. basic_memory/cli/commands/import_memory_json.py +55 -111
  62. basic_memory/cli/commands/mcp.py +67 -11
  63. basic_memory/cli/commands/project.py +889 -0
  64. basic_memory/cli/commands/status.py +52 -34
  65. basic_memory/cli/commands/telemetry.py +81 -0
  66. basic_memory/cli/commands/tool.py +341 -0
  67. basic_memory/cli/container.py +84 -0
  68. basic_memory/cli/main.py +14 -6
  69. basic_memory/config.py +580 -26
  70. basic_memory/db.py +285 -28
  71. basic_memory/deps/__init__.py +293 -0
  72. basic_memory/deps/config.py +26 -0
  73. basic_memory/deps/db.py +56 -0
  74. basic_memory/deps/importers.py +200 -0
  75. basic_memory/deps/projects.py +238 -0
  76. basic_memory/deps/repositories.py +179 -0
  77. basic_memory/deps/services.py +480 -0
  78. basic_memory/deps.py +16 -185
  79. basic_memory/file_utils.py +318 -54
  80. basic_memory/ignore_utils.py +297 -0
  81. basic_memory/importers/__init__.py +27 -0
  82. basic_memory/importers/base.py +100 -0
  83. basic_memory/importers/chatgpt_importer.py +245 -0
  84. basic_memory/importers/claude_conversations_importer.py +192 -0
  85. basic_memory/importers/claude_projects_importer.py +184 -0
  86. basic_memory/importers/memory_json_importer.py +128 -0
  87. basic_memory/importers/utils.py +61 -0
  88. basic_memory/markdown/entity_parser.py +182 -23
  89. basic_memory/markdown/markdown_processor.py +70 -7
  90. basic_memory/markdown/plugins.py +43 -23
  91. basic_memory/markdown/schemas.py +1 -1
  92. basic_memory/markdown/utils.py +38 -14
  93. basic_memory/mcp/async_client.py +135 -4
  94. basic_memory/mcp/clients/__init__.py +28 -0
  95. basic_memory/mcp/clients/directory.py +70 -0
  96. basic_memory/mcp/clients/knowledge.py +176 -0
  97. basic_memory/mcp/clients/memory.py +120 -0
  98. basic_memory/mcp/clients/project.py +89 -0
  99. basic_memory/mcp/clients/resource.py +71 -0
  100. basic_memory/mcp/clients/search.py +65 -0
  101. basic_memory/mcp/container.py +110 -0
  102. basic_memory/mcp/project_context.py +155 -0
  103. basic_memory/mcp/prompts/__init__.py +19 -0
  104. basic_memory/mcp/prompts/ai_assistant_guide.py +70 -0
  105. basic_memory/mcp/prompts/continue_conversation.py +62 -0
  106. basic_memory/mcp/prompts/recent_activity.py +188 -0
  107. basic_memory/mcp/prompts/search.py +57 -0
  108. basic_memory/mcp/prompts/utils.py +162 -0
  109. basic_memory/mcp/resources/ai_assistant_guide.md +283 -0
  110. basic_memory/mcp/resources/project_info.py +71 -0
  111. basic_memory/mcp/server.py +61 -9
  112. basic_memory/mcp/tools/__init__.py +33 -21
  113. basic_memory/mcp/tools/build_context.py +120 -0
  114. basic_memory/mcp/tools/canvas.py +152 -0
  115. basic_memory/mcp/tools/chatgpt_tools.py +190 -0
  116. basic_memory/mcp/tools/delete_note.py +249 -0
  117. basic_memory/mcp/tools/edit_note.py +325 -0
  118. basic_memory/mcp/tools/list_directory.py +157 -0
  119. basic_memory/mcp/tools/move_note.py +549 -0
  120. basic_memory/mcp/tools/project_management.py +204 -0
  121. basic_memory/mcp/tools/read_content.py +281 -0
  122. basic_memory/mcp/tools/read_note.py +265 -0
  123. basic_memory/mcp/tools/recent_activity.py +528 -0
  124. basic_memory/mcp/tools/search.py +377 -24
  125. basic_memory/mcp/tools/utils.py +402 -16
  126. basic_memory/mcp/tools/view_note.py +78 -0
  127. basic_memory/mcp/tools/write_note.py +230 -0
  128. basic_memory/models/__init__.py +3 -2
  129. basic_memory/models/knowledge.py +82 -17
  130. basic_memory/models/project.py +93 -0
  131. basic_memory/models/search.py +68 -8
  132. basic_memory/project_resolver.py +222 -0
  133. basic_memory/repository/__init__.py +2 -0
  134. basic_memory/repository/entity_repository.py +437 -8
  135. basic_memory/repository/observation_repository.py +36 -3
  136. basic_memory/repository/postgres_search_repository.py +451 -0
  137. basic_memory/repository/project_info_repository.py +10 -0
  138. basic_memory/repository/project_repository.py +140 -0
  139. basic_memory/repository/relation_repository.py +79 -4
  140. basic_memory/repository/repository.py +148 -29
  141. basic_memory/repository/search_index_row.py +95 -0
  142. basic_memory/repository/search_repository.py +79 -268
  143. basic_memory/repository/search_repository_base.py +241 -0
  144. basic_memory/repository/sqlite_search_repository.py +437 -0
  145. basic_memory/runtime.py +61 -0
  146. basic_memory/schemas/__init__.py +22 -9
  147. basic_memory/schemas/base.py +131 -12
  148. basic_memory/schemas/cloud.py +50 -0
  149. basic_memory/schemas/directory.py +31 -0
  150. basic_memory/schemas/importer.py +35 -0
  151. basic_memory/schemas/memory.py +194 -25
  152. basic_memory/schemas/project_info.py +213 -0
  153. basic_memory/schemas/prompt.py +90 -0
  154. basic_memory/schemas/request.py +56 -2
  155. basic_memory/schemas/response.py +85 -28
  156. basic_memory/schemas/search.py +36 -35
  157. basic_memory/schemas/sync_report.py +72 -0
  158. basic_memory/schemas/v2/__init__.py +27 -0
  159. basic_memory/schemas/v2/entity.py +133 -0
  160. basic_memory/schemas/v2/resource.py +47 -0
  161. basic_memory/services/__init__.py +2 -1
  162. basic_memory/services/context_service.py +451 -138
  163. basic_memory/services/directory_service.py +310 -0
  164. basic_memory/services/entity_service.py +636 -71
  165. basic_memory/services/exceptions.py +21 -0
  166. basic_memory/services/file_service.py +402 -33
  167. basic_memory/services/initialization.py +216 -0
  168. basic_memory/services/link_resolver.py +50 -56
  169. basic_memory/services/project_service.py +888 -0
  170. basic_memory/services/search_service.py +232 -37
  171. basic_memory/sync/__init__.py +4 -2
  172. basic_memory/sync/background_sync.py +26 -0
  173. basic_memory/sync/coordinator.py +160 -0
  174. basic_memory/sync/sync_service.py +1200 -109
  175. basic_memory/sync/watch_service.py +432 -135
  176. basic_memory/telemetry.py +249 -0
  177. basic_memory/templates/prompts/continue_conversation.hbs +110 -0
  178. basic_memory/templates/prompts/search.hbs +101 -0
  179. basic_memory/utils.py +407 -54
  180. basic_memory-0.17.4.dist-info/METADATA +617 -0
  181. basic_memory-0.17.4.dist-info/RECORD +193 -0
  182. {basic_memory-0.7.0.dist-info → basic_memory-0.17.4.dist-info}/WHEEL +1 -1
  183. {basic_memory-0.7.0.dist-info → basic_memory-0.17.4.dist-info}/entry_points.txt +1 -0
  184. basic_memory/alembic/README +0 -1
  185. basic_memory/cli/commands/sync.py +0 -206
  186. basic_memory/cli/commands/tools.py +0 -157
  187. basic_memory/mcp/tools/knowledge.py +0 -68
  188. basic_memory/mcp/tools/memory.py +0 -170
  189. basic_memory/mcp/tools/notes.py +0 -202
  190. basic_memory/schemas/discovery.py +0 -28
  191. basic_memory/sync/file_change_scanner.py +0 -158
  192. basic_memory/sync/utils.py +0 -31
  193. basic_memory-0.7.0.dist-info/METADATA +0 -378
  194. basic_memory-0.7.0.dist-info/RECORD +0 -82
  195. {basic_memory-0.7.0.dist-info → basic_memory-0.17.4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,57 @@
1
+ """Search prompts for Basic Memory MCP server.
2
+
3
+ These prompts help users search and explore their knowledge base.
4
+ """
5
+
6
+ from typing import Annotated, Optional
7
+
8
+ from loguru import logger
9
+ from pydantic import Field
10
+
11
+ from basic_memory.config import get_project_config
12
+ from basic_memory.mcp.async_client import get_client
13
+ from basic_memory.mcp.server import mcp
14
+ from basic_memory.mcp.tools.utils import call_post
15
+ from basic_memory.schemas.base import TimeFrame
16
+ from basic_memory.schemas.prompt import SearchPromptRequest
17
+
18
+
19
+ @mcp.prompt(
20
+ name="search_knowledge_base",
21
+ description="Search across all content in basic-memory",
22
+ )
23
+ async def search_prompt(
24
+ query: str,
25
+ timeframe: Annotated[
26
+ Optional[TimeFrame],
27
+ Field(description="How far back to search (e.g. '1d', '1 week')"),
28
+ ] = None,
29
+ ) -> str:
30
+ """Search across all content in basic-memory.
31
+
32
+ This prompt helps search for content in the knowledge base and
33
+ provides helpful context about the results.
34
+
35
+ Args:
36
+ query: The search text to look for
37
+ timeframe: Optional timeframe to limit results (e.g. '1d', '1 week')
38
+
39
+ Returns:
40
+ Formatted search results with context
41
+ """
42
+ logger.info(f"Searching knowledge base, query: {query}, timeframe: {timeframe}")
43
+
44
+ async with get_client() as client:
45
+ # Create request model
46
+ request = SearchPromptRequest(query=query, timeframe=timeframe)
47
+
48
+ project_url = get_project_config().project_url
49
+
50
+ # Call the prompt API endpoint
51
+ response = await call_post(
52
+ client, f"{project_url}/prompt/search", json=request.model_dump(exclude_none=True)
53
+ )
54
+
55
+ # Extract the rendered prompt from the response
56
+ result = response.json()
57
+ return result["prompt"]
@@ -0,0 +1,162 @@
1
+ """Utility functions for formatting prompt responses.
2
+
3
+ These utilities help format data from various tools into consistent,
4
+ user-friendly markdown summaries.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from textwrap import dedent
9
+ from typing import List
10
+
11
+ from basic_memory.schemas.base import TimeFrame
12
+ from basic_memory.schemas.memory import (
13
+ normalize_memory_url,
14
+ EntitySummary,
15
+ RelationSummary,
16
+ ObservationSummary,
17
+ )
18
+
19
+
20
+ @dataclass
21
+ class PromptContextItem:
22
+ primary_results: List[EntitySummary]
23
+ related_results: List[EntitySummary | RelationSummary | ObservationSummary]
24
+
25
+
26
+ @dataclass
27
+ class PromptContext:
28
+ timeframe: TimeFrame
29
+ topic: str
30
+ results: List[PromptContextItem]
31
+
32
+
33
+ def format_prompt_context(context: PromptContext) -> str:
34
+ """Format continuation context into a helpful summary.
35
+ Returns:
36
+ Formatted continuation summary
37
+ """
38
+ if not context.results: # pragma: no cover
39
+ return dedent(f"""
40
+ # Continuing conversation on: {context.topic}
41
+
42
+ This is a memory retrieval session.
43
+ The supplied query did not return any information specifically on this topic.
44
+
45
+ ## Opportunity to Capture New Knowledge!
46
+
47
+ This is an excellent chance to start documenting this topic:
48
+
49
+ ```python
50
+ await write_note(
51
+ title="{context.topic}",
52
+ content=f'''
53
+ # {context.topic}
54
+
55
+ ## Overview
56
+ [Summary of what we know about {context.topic}]
57
+
58
+ ## Key Points
59
+ [Main aspects or components of {context.topic}]
60
+
61
+ ## Observations
62
+ - [category] [First important observation about {context.topic}]
63
+ - [category] [Second observation about {context.topic}]
64
+
65
+ ## Relations
66
+ - relates_to [[Related Topic]]
67
+ - part_of [[Broader Context]]
68
+ '''
69
+ )
70
+ ```
71
+
72
+ ## Other Options
73
+
74
+ Please use the available basic-memory tools to gather relevant context before responding.
75
+ You can also:
76
+ - Try a different search term
77
+ - Check recent activity with `recent_activity(timeframe="1w")`
78
+ """)
79
+
80
+ # Start building our summary with header - add knowledge capture emphasis
81
+ summary = dedent(f"""
82
+ # Continuing conversation on: {context.topic}
83
+
84
+ This is a memory retrieval session.
85
+
86
+ Please use the available basic-memory tools to gather relevant context before responding.
87
+ Start by executing one of the suggested commands below to retrieve content.
88
+
89
+ Here's what I found from previous conversations:
90
+
91
+ > **Knowledge Capture Recommendation:** As you continue this conversation, actively look for opportunities to record new information, decisions, or insights that emerge. Use `write_note()` to document important context.
92
+ """)
93
+
94
+ # Track what we've added to avoid duplicates
95
+ added_permalinks = set()
96
+ sections = []
97
+
98
+ # Process each context
99
+ for context in context.results: # pyright: ignore
100
+ for primary in context.primary_results: # pyright: ignore
101
+ if primary.permalink not in added_permalinks:
102
+ primary_permalink = primary.permalink
103
+
104
+ added_permalinks.add(primary_permalink)
105
+
106
+ # Use permalink if available, otherwise use file_path
107
+ if primary_permalink:
108
+ memory_url = normalize_memory_url(primary_permalink)
109
+ read_command = f'read_note("{primary_permalink}")'
110
+ else:
111
+ memory_url = f"file://{primary.file_path}"
112
+ read_command = f'read_file("{primary.file_path}")'
113
+
114
+ section = dedent(f"""
115
+ --- {memory_url}
116
+
117
+ ## {primary.title}
118
+ - **Type**: {primary.type}
119
+ """)
120
+
121
+ # Add creation date
122
+ section += f"- **Created**: {primary.created_at.strftime('%Y-%m-%d %H:%M')}\n"
123
+
124
+ # Add content snippet
125
+ if hasattr(primary, "content") and primary.content: # pyright: ignore
126
+ content = primary.content or "" # pyright: ignore # pragma: no cover
127
+ if content: # pragma: no cover
128
+ section += f"\n**Excerpt**:\n{content}\n" # pragma: no cover
129
+
130
+ section += dedent(f"""
131
+
132
+ You can read this document with: `{read_command}`
133
+ """)
134
+ sections.append(section)
135
+
136
+ if context.related_results: # pyright: ignore
137
+ section += dedent( # pyright: ignore
138
+ """
139
+ ## Related Context
140
+ """
141
+ )
142
+
143
+ for related in context.related_results: # pyright: ignore
144
+ section_content = dedent(f"""
145
+ - type: **{related.type}**
146
+ - title: {related.title}
147
+ """)
148
+ if related.permalink: # pragma: no cover
149
+ section_content += (
150
+ f'You can view this document with: `read_note("{related.permalink}")`'
151
+ )
152
+ else: # pragma: no cover
153
+ section_content += (
154
+ f'You can view this file with: `read_file("{related.file_path}")`'
155
+ )
156
+
157
+ section += section_content
158
+ sections.append(section)
159
+
160
+ # Add all sections
161
+ summary += "\n".join(sections)
162
+ return summary
@@ -0,0 +1,283 @@
1
+ # AI Assistant Guide for Basic Memory
2
+
3
+ Quick reference for using Basic Memory tools effectively through MCP.
4
+
5
+ **For comprehensive coverage**: See the [Extended AI Assistant Guide](https://github.com/basicmachines-co/basic-memory/blob/main/docs/ai-assistant-guide-extended.md) with detailed examples, advanced patterns, and self-contained sections.
6
+
7
+ ## Overview
8
+
9
+ Basic Memory creates a semantic knowledge graph from markdown files. Focus on building rich connections between notes.
10
+
11
+ - **Local-First**: Plain text files on user's computer
12
+ - **Persistent**: Knowledge survives across sessions
13
+ - **Semantic**: Observations and relations create a knowledge graph
14
+
15
+ **Your role**: You're helping humans build enduring knowledge they'll own forever. The semantic graph (observations, relations, context) helps you provide better assistance by understanding connections and maintaining continuity. Think: lasting insights worth keeping, not disposable chat logs.
16
+
17
+ ## Project Management
18
+
19
+ All tools require explicit project specification.
20
+
21
+ **Three-tier resolution:**
22
+ 1. CLI constraint: `--project name` (highest priority)
23
+ 2. Explicit parameter: `project="name"` in tool calls
24
+ 3. Default mode: `default_project_mode=true` in config (fallback)
25
+
26
+ ### Quick Setup Check
27
+
28
+ ```python
29
+ # Discover projects
30
+ projects = await list_memory_projects()
31
+
32
+ # Check if default_project_mode enabled
33
+ # If yes: project parameter optional
34
+ # If no: project parameter required
35
+ ```
36
+
37
+ ### Default Project Mode
38
+
39
+ When `default_project_mode=true`:
40
+ ```python
41
+ # These are equivalent:
42
+ await write_note("Note", "Content", "folder")
43
+ await write_note("Note", "Content", "folder", project="main")
44
+ ```
45
+
46
+ When `default_project_mode=false` (default):
47
+ ```python
48
+ # Project required:
49
+ await write_note("Note", "Content", "folder", project="main") # ✓
50
+ await write_note("Note", "Content", "folder") # ✗ Error
51
+ ```
52
+
53
+ ## Core Tools
54
+
55
+ ### Writing Knowledge
56
+
57
+ ```python
58
+ await write_note(
59
+ title="Topic",
60
+ content="# Topic\n## Observations\n- [category] fact\n## Relations\n- relates_to [[Other]]",
61
+ folder="notes",
62
+ project="main" # Required unless default_project_mode=true
63
+ )
64
+ ```
65
+
66
+ ### Reading Knowledge
67
+
68
+ ```python
69
+ # By identifier
70
+ content = await read_note("Topic", project="main")
71
+
72
+ # By memory:// URL
73
+ content = await read_note("memory://folder/topic", project="main")
74
+ ```
75
+
76
+ ### Searching
77
+
78
+ ```python
79
+ results = await search_notes(
80
+ query="authentication",
81
+ project="main",
82
+ page_size=10
83
+ )
84
+ ```
85
+
86
+ ### Building Context
87
+
88
+ ```python
89
+ context = await build_context(
90
+ url="memory://specs/auth",
91
+ project="main",
92
+ depth=2,
93
+ timeframe="1 week"
94
+ )
95
+ ```
96
+
97
+ ## Knowledge Graph Essentials
98
+
99
+ ### Observations
100
+
101
+ Categorized facts with optional tags:
102
+ ```markdown
103
+ - [decision] Use JWT for authentication #security
104
+ - [technique] Hash passwords with bcrypt #best-practice
105
+ - [requirement] Support OAuth 2.0 providers
106
+ ```
107
+
108
+ ### Relations
109
+
110
+ Directional links between entities:
111
+ ```markdown
112
+ - implements [[Authentication Spec]]
113
+ - requires [[User Database]]
114
+ - extends [[Base Security Model]]
115
+ ```
116
+
117
+ **Common relation types:** `relates_to`, `implements`, `requires`, `extends`, `part_of`, `contrasts_with`
118
+
119
+ ### Forward References
120
+
121
+ Reference entities that don't exist yet:
122
+ ```python
123
+ # Create note with forward reference
124
+ await write_note(
125
+ title="Login Flow",
126
+ content="## Relations\n- requires [[OAuth Provider]]", # Doesn't exist yet
127
+ folder="auth",
128
+ project="main"
129
+ )
130
+
131
+ # Later, create referenced entity
132
+ await write_note(
133
+ title="OAuth Provider",
134
+ content="# OAuth Provider\n...",
135
+ folder="auth",
136
+ project="main"
137
+ )
138
+ # → Relation automatically resolved
139
+ ```
140
+
141
+ ## Best Practices
142
+
143
+ ### 1. Project Management
144
+
145
+ **Single-project users:**
146
+ - Enable `default_project_mode=true`
147
+ - Simpler tool calls
148
+
149
+ **Multi-project users:**
150
+ - Keep `default_project_mode=false`
151
+ - Always specify project explicitly
152
+
153
+ **Discovery:**
154
+ ```python
155
+ # Start with discovery
156
+ projects = await list_memory_projects()
157
+
158
+ # Cross-project activity (no project param = all projects)
159
+ activity = await recent_activity()
160
+
161
+ # Or specific project
162
+ activity = await recent_activity(project="main")
163
+ ```
164
+
165
+ ### 2. Building Rich Graphs
166
+
167
+ **Always include:**
168
+ - 3-5 observations per note
169
+ - 2-3 relations per note
170
+ - Meaningful categories and relation types
171
+
172
+ **Search before creating:**
173
+ ```python
174
+ # Find existing entities to reference
175
+ results = await search_notes(query="authentication", project="main")
176
+ # Use exact titles in [[WikiLinks]]
177
+ ```
178
+
179
+ ### 3. Writing Effective Notes
180
+
181
+ **Structure:**
182
+ ```markdown
183
+ # Title
184
+
185
+ ## Context
186
+ Background information
187
+
188
+ ## Observations
189
+ - [category] Fact with #tags
190
+ - [category] Another fact
191
+
192
+ ## Relations
193
+ - relation_type [[Exact Entity Title]]
194
+ ```
195
+
196
+ **Categories:** `[idea]`, `[decision]`, `[fact]`, `[technique]`, `[requirement]`
197
+
198
+ ### 4. Error Handling
199
+
200
+ **Missing project:**
201
+ ```python
202
+ try:
203
+ await search_notes(query="test") # Missing project parameter - will error
204
+ except:
205
+ # Show available projects
206
+ projects = await list_memory_projects()
207
+ # Then retry with project
208
+ results = await search_notes(query="test", project=projects[0].name)
209
+ ```
210
+
211
+ **Forward references:**
212
+ ```python
213
+ # Check response for unresolved relations
214
+ response = await write_note(
215
+ title="New Topic",
216
+ content="## Relations\n- relates_to [[Future Topic]]",
217
+ folder="notes",
218
+ project="main"
219
+ )
220
+ # Forward refs will resolve when target created
221
+ ```
222
+
223
+ ### 5. Recording Context
224
+
225
+ **Ask permission:**
226
+ > "Would you like me to save our discussion about [topic] to Basic Memory?"
227
+
228
+ **Confirm when done:**
229
+ > "I've saved our discussion to Basic Memory."
230
+
231
+ **What to record:**
232
+ - Decisions and rationales
233
+ - Important discoveries
234
+ - Action items and plans
235
+ - Connected topics
236
+
237
+ ## Common Patterns
238
+
239
+ ### Capture Decision
240
+
241
+ ```python
242
+ await write_note(
243
+ title="DB Choice",
244
+ content="""# DB Choice\n## Decision\nUse PostgreSQL\n## Observations\n- [requirement] ACID compliance #reliability\n- [decision] PostgreSQL over MySQL\n## Relations\n- implements [[Data Architecture]]""",
245
+ folder="decisions",
246
+ project="main"
247
+ )
248
+ ```
249
+
250
+ ### Link Topics & Build Context
251
+
252
+ ```python
253
+ # Link bidirectionally
254
+ await write_note(title="API Auth", content="## Relations\n- part_of [[API Design]]", folder="api", project="main")
255
+ await edit_note(identifier="API Design", operation="append", content="\n- includes [[API Auth]]", project="main")
256
+
257
+ # Search and build context
258
+ results = await search_notes(query="authentication", project="main")
259
+ context = await build_context(url=f"memory://{results[0].permalink}", project="main", depth=2)
260
+ ```
261
+
262
+ ## Tool Quick Reference
263
+
264
+ | Tool | Purpose | Key Params |
265
+ |------|---------|------------|
266
+ | `write_note` | Create/update | title, content, folder, project |
267
+ | `read_note` | Read content | identifier, project |
268
+ | `edit_note` | Modify existing | identifier, operation, content, project |
269
+ | `search_notes` | Find notes | query, project |
270
+ | `build_context` | Graph traversal | url, depth, project |
271
+ | `recent_activity` | Recent changes | timeframe, project |
272
+ | `list_memory_projects` | Show projects | (none) |
273
+
274
+ ## memory:// URL Format
275
+
276
+ - `memory://title` - By title
277
+ - `memory://folder/title` - By folder + title
278
+ - `memory://permalink` - By permalink
279
+ - `memory://folder/*` - All in folder
280
+
281
+ For full documentation: https://docs.basicmemory.com
282
+
283
+ Built with ♥️ by Basic Machines
@@ -0,0 +1,71 @@
1
+ """Project info tool for Basic Memory MCP server."""
2
+
3
+ from typing import Optional
4
+
5
+ from loguru import logger
6
+ from fastmcp import Context
7
+
8
+ from basic_memory.mcp.async_client import get_client
9
+ from basic_memory.mcp.project_context import get_active_project
10
+ from basic_memory.mcp.server import mcp
11
+ from basic_memory.mcp.tools.utils import call_get
12
+ from basic_memory.schemas import ProjectInfoResponse
13
+
14
+
15
+ @mcp.resource(
16
+ uri="memory://{project}/info",
17
+ description="Get information and statistics about the current Basic Memory project.",
18
+ )
19
+ async def project_info(
20
+ project: Optional[str] = None, context: Context | None = None
21
+ ) -> ProjectInfoResponse:
22
+ """Get comprehensive information about the current Basic Memory project.
23
+
24
+ This tool provides detailed statistics and status information about your
25
+ Basic Memory project, including:
26
+
27
+ - Project configuration
28
+ - Entity, observation, and relation counts
29
+ - Graph metrics (most connected entities, isolated entities)
30
+ - Recent activity and growth over time
31
+ - System status (database, watch service, version)
32
+
33
+ Use this tool to:
34
+ - Verify your Basic Memory installation is working correctly
35
+ - Get insights into your knowledge base structure
36
+ - Monitor growth and activity over time
37
+ - Identify potential issues like unresolved relations
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
+
45
+ Returns:
46
+ Detailed project information and statistics
47
+
48
+ Examples:
49
+ # Get information about the current/default project
50
+ info = await project_info()
51
+
52
+ # Get information about a specific project
53
+ info = await project_info(project="my-project")
54
+
55
+ # Check entity counts
56
+ print(f"Total entities: {info.statistics.total_entities}")
57
+
58
+ # Check system status
59
+ print(f"Basic Memory version: {info.system.version}")
60
+ """
61
+ logger.info("Getting project info")
62
+
63
+ async with get_client() as client:
64
+ project_config = await get_active_project(client, project, context)
65
+ project_url = project_config.permalink
66
+
67
+ # Call the API endpoint
68
+ response = await call_get(client, f"{project_url}/project/info")
69
+
70
+ # Convert response to ProjectInfoResponse
71
+ return ProjectInfoResponse.model_validate(response.json())
@@ -1,15 +1,67 @@
1
- """Enhanced FastMCP server instance for Basic Memory."""
1
+ """
2
+ Basic Memory FastMCP server.
3
+ """
2
4
 
3
- from mcp.server.fastmcp import FastMCP
5
+ from contextlib import asynccontextmanager
4
6
 
5
- from basic_memory.utils import setup_logging
7
+ from fastmcp import FastMCP
8
+ from loguru import logger
6
9
 
7
- # mcp console logging
8
- # configure_logging(level='INFO')
10
+ from basic_memory import db
11
+ from basic_memory.mcp.container import McpContainer, set_container
12
+ from basic_memory.services.initialization import initialize_app
13
+ from basic_memory.telemetry import show_notice_if_needed, track_app_started
9
14
 
10
15
 
11
- # start our out file logging
12
- setup_logging(log_file=".basic-memory/basic-memory.log")
16
+ @asynccontextmanager
17
+ async def lifespan(app: FastMCP):
18
+ """Lifecycle manager for the MCP server.
13
19
 
14
- # Create the shared server instance
15
- mcp = FastMCP("Basic Memory")
20
+ Handles:
21
+ - Database initialization and migrations
22
+ - Telemetry notice and tracking
23
+ - File sync via SyncCoordinator (if enabled and not in cloud mode)
24
+ - Proper cleanup on shutdown
25
+ """
26
+ # --- Composition Root ---
27
+ # Create container and read config (single point of config access)
28
+ container = McpContainer.create()
29
+ set_container(container)
30
+
31
+ logger.info(f"Starting Basic Memory MCP server (mode={container.mode.name})")
32
+
33
+ # Show telemetry notice (first run only) and track startup
34
+ show_notice_if_needed()
35
+ track_app_started("mcp")
36
+
37
+ # Track if we created the engine (vs test fixtures providing it)
38
+ # This prevents disposing an engine provided by test fixtures when
39
+ # multiple Client connections are made in the same test
40
+ engine_was_none = db._engine is None
41
+
42
+ # Initialize app (runs migrations, reconciles projects)
43
+ await initialize_app(container.config)
44
+
45
+ # Create and start sync coordinator (lifecycle centralized in coordinator)
46
+ sync_coordinator = container.create_sync_coordinator()
47
+ await sync_coordinator.start()
48
+
49
+ try:
50
+ yield
51
+ finally:
52
+ # Shutdown - coordinator handles clean task cancellation
53
+ logger.info("Shutting down Basic Memory MCP server")
54
+ await sync_coordinator.stop()
55
+
56
+ # Only shutdown DB if we created it (not if test fixture provided it)
57
+ if engine_was_none:
58
+ await db.shutdown_db()
59
+ logger.info("Database connections closed")
60
+ else: # pragma: no cover
61
+ logger.debug("Skipping DB shutdown - engine provided externally")
62
+
63
+
64
+ mcp = FastMCP(
65
+ name="Basic Memory",
66
+ lifespan=lifespan,
67
+ )