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.
- basic_memory/__init__.py +5 -1
- basic_memory/alembic/alembic.ini +119 -0
- basic_memory/alembic/env.py +130 -20
- basic_memory/alembic/migrations.py +4 -9
- basic_memory/alembic/versions/314f1ea54dc4_add_postgres_full_text_search_support_.py +131 -0
- basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py +51 -0
- basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py +120 -0
- basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py +112 -0
- basic_memory/alembic/versions/6830751f5fb6_merge_multiple_heads.py +24 -0
- basic_memory/alembic/versions/9d9c1cb7d8f5_add_mtime_and_size_columns_to_entity_.py +49 -0
- basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +49 -0
- basic_memory/alembic/versions/a2b3c4d5e6f7_add_search_index_entity_cascade.py +56 -0
- basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py +44 -0
- basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +113 -0
- basic_memory/alembic/versions/e7e1f4367280_add_scan_watermark_tracking_to_project.py +37 -0
- basic_memory/alembic/versions/f8a9b2c3d4e5_add_pg_trgm_for_fuzzy_link_resolution.py +239 -0
- basic_memory/alembic/versions/g9a0b3c4d5e6_add_external_id_to_project_and_entity.py +173 -0
- basic_memory/api/app.py +87 -20
- basic_memory/api/container.py +133 -0
- basic_memory/api/routers/__init__.py +4 -1
- basic_memory/api/routers/directory_router.py +84 -0
- basic_memory/api/routers/importer_router.py +152 -0
- basic_memory/api/routers/knowledge_router.py +180 -23
- basic_memory/api/routers/management_router.py +80 -0
- basic_memory/api/routers/memory_router.py +9 -64
- basic_memory/api/routers/project_router.py +460 -0
- basic_memory/api/routers/prompt_router.py +260 -0
- basic_memory/api/routers/resource_router.py +136 -11
- basic_memory/api/routers/search_router.py +5 -5
- basic_memory/api/routers/utils.py +169 -0
- basic_memory/api/template_loader.py +292 -0
- basic_memory/api/v2/__init__.py +35 -0
- basic_memory/api/v2/routers/__init__.py +21 -0
- basic_memory/api/v2/routers/directory_router.py +93 -0
- basic_memory/api/v2/routers/importer_router.py +181 -0
- basic_memory/api/v2/routers/knowledge_router.py +427 -0
- basic_memory/api/v2/routers/memory_router.py +130 -0
- basic_memory/api/v2/routers/project_router.py +359 -0
- basic_memory/api/v2/routers/prompt_router.py +269 -0
- basic_memory/api/v2/routers/resource_router.py +286 -0
- basic_memory/api/v2/routers/search_router.py +73 -0
- basic_memory/cli/app.py +80 -10
- basic_memory/cli/auth.py +300 -0
- basic_memory/cli/commands/__init__.py +15 -2
- basic_memory/cli/commands/cloud/__init__.py +6 -0
- basic_memory/cli/commands/cloud/api_client.py +127 -0
- basic_memory/cli/commands/cloud/bisync_commands.py +110 -0
- basic_memory/cli/commands/cloud/cloud_utils.py +108 -0
- basic_memory/cli/commands/cloud/core_commands.py +195 -0
- basic_memory/cli/commands/cloud/rclone_commands.py +397 -0
- basic_memory/cli/commands/cloud/rclone_config.py +110 -0
- basic_memory/cli/commands/cloud/rclone_installer.py +263 -0
- basic_memory/cli/commands/cloud/upload.py +240 -0
- basic_memory/cli/commands/cloud/upload_command.py +124 -0
- basic_memory/cli/commands/command_utils.py +99 -0
- basic_memory/cli/commands/db.py +87 -12
- basic_memory/cli/commands/format.py +198 -0
- basic_memory/cli/commands/import_chatgpt.py +47 -223
- basic_memory/cli/commands/import_claude_conversations.py +48 -171
- basic_memory/cli/commands/import_claude_projects.py +53 -160
- basic_memory/cli/commands/import_memory_json.py +55 -111
- basic_memory/cli/commands/mcp.py +67 -11
- basic_memory/cli/commands/project.py +889 -0
- basic_memory/cli/commands/status.py +52 -34
- basic_memory/cli/commands/telemetry.py +81 -0
- basic_memory/cli/commands/tool.py +341 -0
- basic_memory/cli/container.py +84 -0
- basic_memory/cli/main.py +14 -6
- basic_memory/config.py +580 -26
- basic_memory/db.py +285 -28
- basic_memory/deps/__init__.py +293 -0
- basic_memory/deps/config.py +26 -0
- basic_memory/deps/db.py +56 -0
- basic_memory/deps/importers.py +200 -0
- basic_memory/deps/projects.py +238 -0
- basic_memory/deps/repositories.py +179 -0
- basic_memory/deps/services.py +480 -0
- basic_memory/deps.py +16 -185
- basic_memory/file_utils.py +318 -54
- basic_memory/ignore_utils.py +297 -0
- basic_memory/importers/__init__.py +27 -0
- basic_memory/importers/base.py +100 -0
- basic_memory/importers/chatgpt_importer.py +245 -0
- basic_memory/importers/claude_conversations_importer.py +192 -0
- basic_memory/importers/claude_projects_importer.py +184 -0
- basic_memory/importers/memory_json_importer.py +128 -0
- basic_memory/importers/utils.py +61 -0
- basic_memory/markdown/entity_parser.py +182 -23
- basic_memory/markdown/markdown_processor.py +70 -7
- basic_memory/markdown/plugins.py +43 -23
- basic_memory/markdown/schemas.py +1 -1
- basic_memory/markdown/utils.py +38 -14
- basic_memory/mcp/async_client.py +135 -4
- basic_memory/mcp/clients/__init__.py +28 -0
- basic_memory/mcp/clients/directory.py +70 -0
- basic_memory/mcp/clients/knowledge.py +176 -0
- basic_memory/mcp/clients/memory.py +120 -0
- basic_memory/mcp/clients/project.py +89 -0
- basic_memory/mcp/clients/resource.py +71 -0
- basic_memory/mcp/clients/search.py +65 -0
- basic_memory/mcp/container.py +110 -0
- basic_memory/mcp/project_context.py +155 -0
- basic_memory/mcp/prompts/__init__.py +19 -0
- basic_memory/mcp/prompts/ai_assistant_guide.py +70 -0
- basic_memory/mcp/prompts/continue_conversation.py +62 -0
- basic_memory/mcp/prompts/recent_activity.py +188 -0
- basic_memory/mcp/prompts/search.py +57 -0
- basic_memory/mcp/prompts/utils.py +162 -0
- basic_memory/mcp/resources/ai_assistant_guide.md +283 -0
- basic_memory/mcp/resources/project_info.py +71 -0
- basic_memory/mcp/server.py +61 -9
- basic_memory/mcp/tools/__init__.py +33 -21
- basic_memory/mcp/tools/build_context.py +120 -0
- basic_memory/mcp/tools/canvas.py +152 -0
- basic_memory/mcp/tools/chatgpt_tools.py +190 -0
- basic_memory/mcp/tools/delete_note.py +249 -0
- basic_memory/mcp/tools/edit_note.py +325 -0
- basic_memory/mcp/tools/list_directory.py +157 -0
- basic_memory/mcp/tools/move_note.py +549 -0
- basic_memory/mcp/tools/project_management.py +204 -0
- basic_memory/mcp/tools/read_content.py +281 -0
- basic_memory/mcp/tools/read_note.py +265 -0
- basic_memory/mcp/tools/recent_activity.py +528 -0
- basic_memory/mcp/tools/search.py +377 -24
- basic_memory/mcp/tools/utils.py +402 -16
- basic_memory/mcp/tools/view_note.py +78 -0
- basic_memory/mcp/tools/write_note.py +230 -0
- basic_memory/models/__init__.py +3 -2
- basic_memory/models/knowledge.py +82 -17
- basic_memory/models/project.py +93 -0
- basic_memory/models/search.py +68 -8
- basic_memory/project_resolver.py +222 -0
- basic_memory/repository/__init__.py +2 -0
- basic_memory/repository/entity_repository.py +437 -8
- basic_memory/repository/observation_repository.py +36 -3
- basic_memory/repository/postgres_search_repository.py +451 -0
- basic_memory/repository/project_info_repository.py +10 -0
- basic_memory/repository/project_repository.py +140 -0
- basic_memory/repository/relation_repository.py +79 -4
- basic_memory/repository/repository.py +148 -29
- basic_memory/repository/search_index_row.py +95 -0
- basic_memory/repository/search_repository.py +79 -268
- basic_memory/repository/search_repository_base.py +241 -0
- basic_memory/repository/sqlite_search_repository.py +437 -0
- basic_memory/runtime.py +61 -0
- basic_memory/schemas/__init__.py +22 -9
- basic_memory/schemas/base.py +131 -12
- basic_memory/schemas/cloud.py +50 -0
- basic_memory/schemas/directory.py +31 -0
- basic_memory/schemas/importer.py +35 -0
- basic_memory/schemas/memory.py +194 -25
- basic_memory/schemas/project_info.py +213 -0
- basic_memory/schemas/prompt.py +90 -0
- basic_memory/schemas/request.py +56 -2
- basic_memory/schemas/response.py +85 -28
- basic_memory/schemas/search.py +36 -35
- basic_memory/schemas/sync_report.py +72 -0
- basic_memory/schemas/v2/__init__.py +27 -0
- basic_memory/schemas/v2/entity.py +133 -0
- basic_memory/schemas/v2/resource.py +47 -0
- basic_memory/services/__init__.py +2 -1
- basic_memory/services/context_service.py +451 -138
- basic_memory/services/directory_service.py +310 -0
- basic_memory/services/entity_service.py +636 -71
- basic_memory/services/exceptions.py +21 -0
- basic_memory/services/file_service.py +402 -33
- basic_memory/services/initialization.py +216 -0
- basic_memory/services/link_resolver.py +50 -56
- basic_memory/services/project_service.py +888 -0
- basic_memory/services/search_service.py +232 -37
- basic_memory/sync/__init__.py +4 -2
- basic_memory/sync/background_sync.py +26 -0
- basic_memory/sync/coordinator.py +160 -0
- basic_memory/sync/sync_service.py +1200 -109
- basic_memory/sync/watch_service.py +432 -135
- basic_memory/telemetry.py +249 -0
- basic_memory/templates/prompts/continue_conversation.hbs +110 -0
- basic_memory/templates/prompts/search.hbs +101 -0
- basic_memory/utils.py +407 -54
- basic_memory-0.17.4.dist-info/METADATA +617 -0
- basic_memory-0.17.4.dist-info/RECORD +193 -0
- {basic_memory-0.7.0.dist-info → basic_memory-0.17.4.dist-info}/WHEEL +1 -1
- {basic_memory-0.7.0.dist-info → basic_memory-0.17.4.dist-info}/entry_points.txt +1 -0
- basic_memory/alembic/README +0 -1
- basic_memory/cli/commands/sync.py +0 -206
- basic_memory/cli/commands/tools.py +0 -157
- basic_memory/mcp/tools/knowledge.py +0 -68
- basic_memory/mcp/tools/memory.py +0 -170
- basic_memory/mcp/tools/notes.py +0 -202
- basic_memory/schemas/discovery.py +0 -28
- basic_memory/sync/file_change_scanner.py +0 -158
- basic_memory/sync/utils.py +0 -31
- basic_memory-0.7.0.dist-info/METADATA +0 -378
- basic_memory-0.7.0.dist-info/RECORD +0 -82
- {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())
|
basic_memory/mcp/server.py
CHANGED
|
@@ -1,15 +1,67 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Basic Memory FastMCP server.
|
|
3
|
+
"""
|
|
2
4
|
|
|
3
|
-
from
|
|
5
|
+
from contextlib import asynccontextmanager
|
|
4
6
|
|
|
5
|
-
from
|
|
7
|
+
from fastmcp import FastMCP
|
|
8
|
+
from loguru import logger
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
12
|
-
|
|
16
|
+
@asynccontextmanager
|
|
17
|
+
async def lifespan(app: FastMCP):
|
|
18
|
+
"""Lifecycle manager for the MCP server.
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
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
|
+
)
|