basic-memory 0.2.12__py3-none-any.whl → 0.16.1__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 (149) hide show
  1. basic_memory/__init__.py +5 -1
  2. basic_memory/alembic/alembic.ini +119 -0
  3. basic_memory/alembic/env.py +27 -3
  4. basic_memory/alembic/migrations.py +4 -9
  5. basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py +51 -0
  6. basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py +108 -0
  7. basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py +104 -0
  8. basic_memory/alembic/versions/9d9c1cb7d8f5_add_mtime_and_size_columns_to_entity_.py +49 -0
  9. basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +49 -0
  10. basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py +44 -0
  11. basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +100 -0
  12. basic_memory/alembic/versions/e7e1f4367280_add_scan_watermark_tracking_to_project.py +37 -0
  13. basic_memory/api/app.py +63 -31
  14. basic_memory/api/routers/__init__.py +4 -1
  15. basic_memory/api/routers/directory_router.py +84 -0
  16. basic_memory/api/routers/importer_router.py +152 -0
  17. basic_memory/api/routers/knowledge_router.py +165 -28
  18. basic_memory/api/routers/management_router.py +80 -0
  19. basic_memory/api/routers/memory_router.py +28 -67
  20. basic_memory/api/routers/project_router.py +406 -0
  21. basic_memory/api/routers/prompt_router.py +260 -0
  22. basic_memory/api/routers/resource_router.py +219 -14
  23. basic_memory/api/routers/search_router.py +21 -13
  24. basic_memory/api/routers/utils.py +130 -0
  25. basic_memory/api/template_loader.py +292 -0
  26. basic_memory/cli/app.py +52 -1
  27. basic_memory/cli/auth.py +277 -0
  28. basic_memory/cli/commands/__init__.py +13 -2
  29. basic_memory/cli/commands/cloud/__init__.py +6 -0
  30. basic_memory/cli/commands/cloud/api_client.py +112 -0
  31. basic_memory/cli/commands/cloud/bisync_commands.py +110 -0
  32. basic_memory/cli/commands/cloud/cloud_utils.py +101 -0
  33. basic_memory/cli/commands/cloud/core_commands.py +195 -0
  34. basic_memory/cli/commands/cloud/rclone_commands.py +301 -0
  35. basic_memory/cli/commands/cloud/rclone_config.py +110 -0
  36. basic_memory/cli/commands/cloud/rclone_installer.py +249 -0
  37. basic_memory/cli/commands/cloud/upload.py +233 -0
  38. basic_memory/cli/commands/cloud/upload_command.py +124 -0
  39. basic_memory/cli/commands/command_utils.py +51 -0
  40. basic_memory/cli/commands/db.py +26 -7
  41. basic_memory/cli/commands/import_chatgpt.py +83 -0
  42. basic_memory/cli/commands/import_claude_conversations.py +86 -0
  43. basic_memory/cli/commands/import_claude_projects.py +85 -0
  44. basic_memory/cli/commands/import_memory_json.py +35 -92
  45. basic_memory/cli/commands/mcp.py +84 -10
  46. basic_memory/cli/commands/project.py +876 -0
  47. basic_memory/cli/commands/status.py +47 -30
  48. basic_memory/cli/commands/tool.py +341 -0
  49. basic_memory/cli/main.py +13 -6
  50. basic_memory/config.py +481 -22
  51. basic_memory/db.py +192 -32
  52. basic_memory/deps.py +252 -22
  53. basic_memory/file_utils.py +113 -58
  54. basic_memory/ignore_utils.py +297 -0
  55. basic_memory/importers/__init__.py +27 -0
  56. basic_memory/importers/base.py +79 -0
  57. basic_memory/importers/chatgpt_importer.py +232 -0
  58. basic_memory/importers/claude_conversations_importer.py +177 -0
  59. basic_memory/importers/claude_projects_importer.py +148 -0
  60. basic_memory/importers/memory_json_importer.py +108 -0
  61. basic_memory/importers/utils.py +58 -0
  62. basic_memory/markdown/entity_parser.py +143 -23
  63. basic_memory/markdown/markdown_processor.py +3 -3
  64. basic_memory/markdown/plugins.py +39 -21
  65. basic_memory/markdown/schemas.py +1 -1
  66. basic_memory/markdown/utils.py +28 -13
  67. basic_memory/mcp/async_client.py +134 -4
  68. basic_memory/mcp/project_context.py +141 -0
  69. basic_memory/mcp/prompts/__init__.py +19 -0
  70. basic_memory/mcp/prompts/ai_assistant_guide.py +70 -0
  71. basic_memory/mcp/prompts/continue_conversation.py +62 -0
  72. basic_memory/mcp/prompts/recent_activity.py +188 -0
  73. basic_memory/mcp/prompts/search.py +57 -0
  74. basic_memory/mcp/prompts/utils.py +162 -0
  75. basic_memory/mcp/resources/ai_assistant_guide.md +283 -0
  76. basic_memory/mcp/resources/project_info.py +71 -0
  77. basic_memory/mcp/server.py +7 -13
  78. basic_memory/mcp/tools/__init__.py +33 -21
  79. basic_memory/mcp/tools/build_context.py +120 -0
  80. basic_memory/mcp/tools/canvas.py +130 -0
  81. basic_memory/mcp/tools/chatgpt_tools.py +187 -0
  82. basic_memory/mcp/tools/delete_note.py +225 -0
  83. basic_memory/mcp/tools/edit_note.py +320 -0
  84. basic_memory/mcp/tools/list_directory.py +167 -0
  85. basic_memory/mcp/tools/move_note.py +545 -0
  86. basic_memory/mcp/tools/project_management.py +200 -0
  87. basic_memory/mcp/tools/read_content.py +271 -0
  88. basic_memory/mcp/tools/read_note.py +255 -0
  89. basic_memory/mcp/tools/recent_activity.py +534 -0
  90. basic_memory/mcp/tools/search.py +369 -14
  91. basic_memory/mcp/tools/utils.py +374 -16
  92. basic_memory/mcp/tools/view_note.py +77 -0
  93. basic_memory/mcp/tools/write_note.py +207 -0
  94. basic_memory/models/__init__.py +3 -2
  95. basic_memory/models/knowledge.py +67 -15
  96. basic_memory/models/project.py +87 -0
  97. basic_memory/models/search.py +10 -6
  98. basic_memory/repository/__init__.py +2 -0
  99. basic_memory/repository/entity_repository.py +229 -7
  100. basic_memory/repository/observation_repository.py +35 -3
  101. basic_memory/repository/project_info_repository.py +10 -0
  102. basic_memory/repository/project_repository.py +103 -0
  103. basic_memory/repository/relation_repository.py +21 -2
  104. basic_memory/repository/repository.py +147 -29
  105. basic_memory/repository/search_repository.py +437 -59
  106. basic_memory/schemas/__init__.py +22 -9
  107. basic_memory/schemas/base.py +97 -8
  108. basic_memory/schemas/cloud.py +50 -0
  109. basic_memory/schemas/directory.py +30 -0
  110. basic_memory/schemas/importer.py +35 -0
  111. basic_memory/schemas/memory.py +188 -23
  112. basic_memory/schemas/project_info.py +211 -0
  113. basic_memory/schemas/prompt.py +90 -0
  114. basic_memory/schemas/request.py +57 -3
  115. basic_memory/schemas/response.py +9 -1
  116. basic_memory/schemas/search.py +33 -35
  117. basic_memory/schemas/sync_report.py +72 -0
  118. basic_memory/services/__init__.py +2 -1
  119. basic_memory/services/context_service.py +251 -106
  120. basic_memory/services/directory_service.py +295 -0
  121. basic_memory/services/entity_service.py +595 -60
  122. basic_memory/services/exceptions.py +21 -0
  123. basic_memory/services/file_service.py +284 -30
  124. basic_memory/services/initialization.py +191 -0
  125. basic_memory/services/link_resolver.py +50 -56
  126. basic_memory/services/project_service.py +863 -0
  127. basic_memory/services/search_service.py +172 -34
  128. basic_memory/sync/__init__.py +3 -2
  129. basic_memory/sync/background_sync.py +26 -0
  130. basic_memory/sync/sync_service.py +1176 -96
  131. basic_memory/sync/watch_service.py +412 -135
  132. basic_memory/templates/prompts/continue_conversation.hbs +110 -0
  133. basic_memory/templates/prompts/search.hbs +101 -0
  134. basic_memory/utils.py +388 -28
  135. basic_memory-0.16.1.dist-info/METADATA +493 -0
  136. basic_memory-0.16.1.dist-info/RECORD +148 -0
  137. {basic_memory-0.2.12.dist-info → basic_memory-0.16.1.dist-info}/entry_points.txt +1 -0
  138. basic_memory/alembic/README +0 -1
  139. basic_memory/cli/commands/sync.py +0 -203
  140. basic_memory/mcp/tools/knowledge.py +0 -56
  141. basic_memory/mcp/tools/memory.py +0 -151
  142. basic_memory/mcp/tools/notes.py +0 -122
  143. basic_memory/schemas/discovery.py +0 -28
  144. basic_memory/sync/file_change_scanner.py +0 -158
  145. basic_memory/sync/utils.py +0 -34
  146. basic_memory-0.2.12.dist-info/METADATA +0 -291
  147. basic_memory-0.2.12.dist-info/RECORD +0 -78
  148. {basic_memory-0.2.12.dist-info → basic_memory-0.16.1.dist-info}/WHEEL +0 -0
  149. {basic_memory-0.2.12.dist-info → basic_memory-0.16.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,203 +0,0 @@
1
- """Command module for basic-memory sync operations."""
2
-
3
- import asyncio
4
- from collections import defaultdict
5
- from dataclasses import dataclass
6
- from pathlib import Path
7
- from typing import List, Dict
8
-
9
- import typer
10
- from loguru import logger
11
- from rich.console import Console
12
- from rich.tree import Tree
13
-
14
- from basic_memory import db
15
- from basic_memory.cli.app import app
16
- from basic_memory.config import config
17
- from basic_memory.db import DatabaseType
18
- from basic_memory.markdown import EntityParser
19
- from basic_memory.markdown.markdown_processor import MarkdownProcessor
20
- from basic_memory.repository import (
21
- EntityRepository,
22
- ObservationRepository,
23
- RelationRepository,
24
- )
25
- from basic_memory.repository.search_repository import SearchRepository
26
- from basic_memory.services import EntityService, FileService
27
- from basic_memory.services.link_resolver import LinkResolver
28
- from basic_memory.services.search_service import SearchService
29
- from basic_memory.sync import SyncService, FileChangeScanner
30
- from basic_memory.sync.utils import SyncReport
31
- from basic_memory.sync.watch_service import WatchService
32
-
33
- console = Console()
34
-
35
-
36
- @dataclass
37
- class ValidationIssue:
38
- file_path: str
39
- error: str
40
-
41
-
42
- async def get_sync_service(db_type=DatabaseType.FILESYSTEM): # pragma: no cover
43
- """Get sync service instance with all dependencies."""
44
- async with db.engine_session_factory(db_path=config.database_path, db_type=db_type) as (
45
- engine,
46
- session_maker,
47
- ):
48
- entity_parser = EntityParser(config.home)
49
- markdown_processor = MarkdownProcessor(entity_parser)
50
- file_service = FileService(config.home, markdown_processor)
51
-
52
- # Initialize repositories
53
- entity_repository = EntityRepository(session_maker)
54
- observation_repository = ObservationRepository(session_maker)
55
- relation_repository = RelationRepository(session_maker)
56
- search_repository = SearchRepository(session_maker)
57
-
58
- # Initialize services
59
- search_service = SearchService(search_repository, entity_repository, file_service)
60
- link_resolver = LinkResolver(entity_repository, search_service)
61
-
62
- # Initialize scanner
63
- file_change_scanner = FileChangeScanner(entity_repository)
64
-
65
- # Initialize services
66
- entity_service = EntityService(
67
- entity_parser,
68
- entity_repository,
69
- observation_repository,
70
- relation_repository,
71
- file_service,
72
- link_resolver,
73
- )
74
-
75
- # Create sync service
76
- sync_service = SyncService(
77
- scanner=file_change_scanner,
78
- entity_service=entity_service,
79
- entity_parser=entity_parser,
80
- entity_repository=entity_repository,
81
- relation_repository=relation_repository,
82
- search_service=search_service,
83
- )
84
-
85
- return sync_service
86
-
87
-
88
- def group_issues_by_directory(issues: List[ValidationIssue]) -> Dict[str, List[ValidationIssue]]:
89
- """Group validation issues by directory."""
90
- grouped = defaultdict(list)
91
- for issue in issues:
92
- dir_name = Path(issue.file_path).parent.name
93
- grouped[dir_name].append(issue)
94
- return dict(grouped)
95
-
96
-
97
- def display_sync_summary(knowledge: SyncReport):
98
- """Display a one-line summary of sync changes."""
99
- total_changes = knowledge.total_changes
100
- if total_changes == 0:
101
- console.print("[green]Everything up to date[/green]")
102
- return
103
-
104
- # Format as: "Synced X files (A new, B modified, C moved, D deleted)"
105
- changes = []
106
- new_count = len(knowledge.new)
107
- mod_count = len(knowledge.modified)
108
- move_count = len(knowledge.moves)
109
- del_count = len(knowledge.deleted)
110
-
111
- if new_count:
112
- changes.append(f"[green]{new_count} new[/green]")
113
- if mod_count:
114
- changes.append(f"[yellow]{mod_count} modified[/yellow]")
115
- if move_count:
116
- changes.append(f"[blue]{move_count} moved[/blue]")
117
- if del_count:
118
- changes.append(f"[red]{del_count} deleted[/red]")
119
-
120
- console.print(f"Synced {total_changes} files ({', '.join(changes)})")
121
-
122
-
123
- def display_detailed_sync_results(knowledge: SyncReport):
124
- """Display detailed sync results with trees."""
125
- if knowledge.total_changes == 0:
126
- console.print("\n[green]Everything up to date[/green]")
127
- return
128
-
129
- console.print("\n[bold]Sync Results[/bold]")
130
-
131
- if knowledge.total_changes > 0:
132
- knowledge_tree = Tree("[bold]Knowledge Files[/bold]")
133
- if knowledge.new:
134
- created = knowledge_tree.add("[green]Created[/green]")
135
- for path in sorted(knowledge.new):
136
- checksum = knowledge.checksums.get(path, "")
137
- created.add(f"[green]{path}[/green] ({checksum[:8]})")
138
- if knowledge.modified:
139
- modified = knowledge_tree.add("[yellow]Modified[/yellow]")
140
- for path in sorted(knowledge.modified):
141
- checksum = knowledge.checksums.get(path, "")
142
- modified.add(f"[yellow]{path}[/yellow] ({checksum[:8]})")
143
- if knowledge.moves:
144
- moved = knowledge_tree.add("[blue]Moved[/blue]")
145
- for old_path, new_path in sorted(knowledge.moves.items()):
146
- checksum = knowledge.checksums.get(new_path, "")
147
- moved.add(f"[blue]{old_path}[/blue] → [blue]{new_path}[/blue] ({checksum[:8]})")
148
- if knowledge.deleted:
149
- deleted = knowledge_tree.add("[red]Deleted[/red]")
150
- for path in sorted(knowledge.deleted):
151
- deleted.add(f"[red]{path}[/red]")
152
- console.print(knowledge_tree)
153
-
154
-
155
- async def run_sync(verbose: bool = False, watch: bool = False):
156
- """Run sync operation."""
157
- sync_service = await get_sync_service()
158
-
159
- # Start watching if requested
160
- if watch:
161
- watch_service = WatchService(
162
- sync_service=sync_service,
163
- file_service=sync_service.entity_service.file_service,
164
- config=config,
165
- )
166
- await watch_service.handle_changes(config.home)
167
- await watch_service.run() # pragma: no cover
168
- else:
169
- # one time sync
170
- knowledge_changes = await sync_service.sync(config.home)
171
- # Display results
172
- if verbose:
173
- display_detailed_sync_results(knowledge_changes)
174
- else:
175
- display_sync_summary(knowledge_changes) # pragma: no cover
176
-
177
-
178
- @app.command()
179
- def sync(
180
- verbose: bool = typer.Option(
181
- False,
182
- "--verbose",
183
- "-v",
184
- help="Show detailed sync information.",
185
- ),
186
- watch: bool = typer.Option(
187
- False,
188
- "--watch",
189
- "-w",
190
- help="Start watching for changes after sync.",
191
- ),
192
- ) -> None:
193
- """Sync knowledge files with the database."""
194
- try:
195
- # Run sync
196
- asyncio.run(run_sync(verbose=verbose, watch=watch))
197
-
198
- except Exception as e: # pragma: no cover
199
- if not isinstance(e, typer.Exit):
200
- logger.exception("Sync failed")
201
- typer.echo(f"Error during sync: {e}", err=True)
202
- raise typer.Exit(1)
203
- raise
@@ -1,56 +0,0 @@
1
- """Knowledge graph management tools for Basic Memory MCP server."""
2
-
3
- from basic_memory.mcp.server import mcp
4
- from basic_memory.mcp.tools.utils import call_get, call_post
5
- from basic_memory.schemas.base import Permalink
6
- from basic_memory.schemas.request import (
7
- GetEntitiesRequest,
8
- )
9
- from basic_memory.schemas.delete import (
10
- DeleteEntitiesRequest,
11
- )
12
- from basic_memory.schemas.response import EntityListResponse, EntityResponse, DeleteEntitiesResponse
13
- from basic_memory.mcp.async_client import client
14
-
15
-
16
- @mcp.tool(
17
- description="Get complete information about a specific entity including observations and relations",
18
- )
19
- async def get_entity(permalink: Permalink) -> EntityResponse:
20
- """Get a specific entity info by its permalink.
21
-
22
- Args:
23
- permalink: Path identifier for the entity
24
- """
25
- url = f"/knowledge/entities/{permalink}"
26
- response = await call_get(client, url)
27
- return EntityResponse.model_validate(response.json())
28
-
29
-
30
- @mcp.tool(
31
- description="Load multiple entities by their permalinks in a single request",
32
- )
33
- async def get_entities(request: GetEntitiesRequest) -> EntityListResponse:
34
- """Load multiple entities by their permalinks.
35
-
36
- Args:
37
- request: OpenNodesRequest containing list of permalinks to load
38
-
39
- Returns:
40
- EntityListResponse containing complete details for each requested entity
41
- """
42
- url = "/knowledge/entities"
43
- response = await call_get(
44
- client, url, params=[("permalink", permalink) for permalink in request.permalinks]
45
- )
46
- return EntityListResponse.model_validate(response.json())
47
-
48
-
49
- @mcp.tool(
50
- description="Permanently delete entities and all related content (observations and relations)",
51
- )
52
- async def delete_entities(request: DeleteEntitiesRequest) -> DeleteEntitiesResponse:
53
- """Delete entities from the knowledge graph."""
54
- url = "/knowledge/entities/delete"
55
- response = await call_post(client, url, json=request.model_dump())
56
- return DeleteEntitiesResponse.model_validate(response.json())
@@ -1,151 +0,0 @@
1
- """Discussion context tools for Basic Memory MCP server."""
2
-
3
- from typing import Optional, Literal, List
4
-
5
- from loguru import logger
6
-
7
- from basic_memory.mcp.async_client import client
8
- from basic_memory.mcp.server import mcp
9
- from basic_memory.mcp.tools.utils import call_get
10
- from basic_memory.schemas.memory import (
11
- GraphContext,
12
- MemoryUrl,
13
- memory_url_path,
14
- normalize_memory_url,
15
- )
16
- from basic_memory.schemas.base import TimeFrame
17
-
18
-
19
- @mcp.tool(
20
- description="""Build context from a memory:// URI to continue conversations naturally.
21
-
22
- Use this to follow up on previous discussions or explore related topics.
23
- Timeframes support natural language like:
24
- - "2 days ago"
25
- - "last week"
26
- - "today"
27
- - "3 months ago"
28
- Or standard formats like "7d", "24h"
29
- """,
30
- )
31
- async def build_context(
32
- url: MemoryUrl,
33
- depth: Optional[int] = 1,
34
- timeframe: Optional[TimeFrame] = "7d",
35
- max_results: int = 10,
36
- ) -> GraphContext:
37
- """Get context needed to continue a discussion.
38
-
39
- This tool enables natural continuation of discussions by loading relevant context
40
- from memory:// URIs. It uses pattern matching to find relevant content and builds
41
- a rich context graph of related information.
42
-
43
- Args:
44
- url: memory:// URI pointing to discussion content (e.g. memory://specs/search)
45
- depth: How many relation hops to traverse (1-3 recommended for performance)
46
- timeframe: How far back to look. Supports natural language like "2 days ago", "last week"
47
- max_results: Maximum number of results to return (default: 10)
48
-
49
- Returns:
50
- GraphContext containing:
51
- - primary_results: Content matching the memory:// URI
52
- - related_results: Connected content via relations
53
- - metadata: Context building details
54
-
55
- Examples:
56
- # Continue a specific discussion
57
- build_context("memory://specs/search")
58
-
59
- # Get deeper context about a component
60
- build_context("memory://components/memory-service", depth=2)
61
-
62
- # Look at recent changes to a specification
63
- build_context("memory://specs/document-format", timeframe="today")
64
-
65
- # Research the history of a feature
66
- build_context("memory://features/knowledge-graph", timeframe="3 months ago")
67
- """
68
- logger.info(f"Building context from {url}")
69
- url = normalize_memory_url(url)
70
- response = await call_get(
71
- client,
72
- f"/memory/{memory_url_path(url)}",
73
- params={"depth": depth, "timeframe": timeframe, "max_results": max_results},
74
- )
75
- return GraphContext.model_validate(response.json())
76
-
77
-
78
- @mcp.tool(
79
- description="""Get recent activity from across the knowledge base.
80
-
81
- Timeframe supports natural language formats like:
82
- - "2 days ago"
83
- - "last week"
84
- - "yesterday"
85
- - "today"
86
- - "3 weeks ago"
87
- Or standard formats like "7d"
88
- """,
89
- )
90
- async def recent_activity(
91
- type: List[Literal["entity", "observation", "relation"]] = [],
92
- depth: Optional[int] = 1,
93
- timeframe: Optional[TimeFrame] = "7d",
94
- max_results: int = 10,
95
- ) -> GraphContext:
96
- """Get recent activity across the knowledge base.
97
-
98
- Args:
99
- type: Filter by content type(s). Valid options:
100
- - ["entity"] for knowledge entities
101
- - ["relation"] for connections between entities
102
- - ["observation"] for notes and observations
103
- Multiple types can be combined: ["entity", "relation"]
104
- depth: How many relation hops to traverse (1-3 recommended)
105
- timeframe: Time window to search. Supports natural language:
106
- - Relative: "2 days ago", "last week", "yesterday"
107
- - Points in time: "2024-01-01", "January 1st"
108
- - Standard format: "7d", "24h"
109
- max_results: Maximum number of results to return (default: 10)
110
-
111
- Returns:
112
- GraphContext containing:
113
- - primary_results: Latest activities matching the filters
114
- - related_results: Connected content via relations
115
- - metadata: Query details and statistics
116
-
117
- Examples:
118
- # Get all entities for the last 10 days (default)
119
- recent_activity()
120
-
121
- # Get all entities from yesterday
122
- recent_activity(type=["entity"], timeframe="yesterday")
123
-
124
- # Get recent relations and observations
125
- recent_activity(type=["relation", "observation"], timeframe="today")
126
-
127
- # Look back further with more context
128
- recent_activity(type=["entity"], depth=2, timeframe="2 weeks ago")
129
-
130
- Notes:
131
- - Higher depth values (>3) may impact performance with large result sets
132
- - For focused queries, consider using build_context with a specific URI
133
- - Max timeframe is 1 year in the past
134
- """
135
- logger.info(
136
- f"Getting recent activity from {type}, depth={depth}, timeframe={timeframe}, max_results={max_results}"
137
- )
138
- params = {
139
- "depth": depth,
140
- "timeframe": timeframe,
141
- "max_results": max_results,
142
- }
143
- if type:
144
- params["type"] = type
145
-
146
- response = await call_get(
147
- client,
148
- "/memory/recent",
149
- params=params,
150
- )
151
- return GraphContext.model_validate(response.json())
@@ -1,122 +0,0 @@
1
- """Note management tools for Basic Memory MCP server.
2
-
3
- These tools provide a natural interface for working with markdown notes
4
- while leveraging the underlying knowledge graph structure.
5
- """
6
-
7
- from typing import Optional, List
8
-
9
- from loguru import logger
10
-
11
- from basic_memory.mcp.server import mcp
12
- from basic_memory.mcp.async_client import client
13
- from basic_memory.schemas import EntityResponse, DeleteEntitiesResponse
14
- from basic_memory.schemas.base import Entity
15
- from basic_memory.mcp.tools.utils import call_get, call_put, call_delete
16
-
17
-
18
- @mcp.tool(
19
- description="Create or update a markdown note. Returns the permalink for referencing.",
20
- )
21
- async def write_note(
22
- title: str,
23
- content: str,
24
- folder: str,
25
- tags: Optional[List[str]] = None,
26
- verbose: bool = False,
27
- ) -> EntityResponse | str:
28
- """Write a markdown note to the knowledge base.
29
-
30
- Args:
31
- title: The title of the note
32
- content: Markdown content for the note
33
- folder: the folder where the file should be saved
34
- tags: Optional list of tags to categorize the note
35
- verbose: If True, returns full EntityResponse with semantic info
36
-
37
- Returns:
38
- If verbose=False: Permalink that can be used to reference the note
39
- If verbose=True: EntityResponse with full semantic details
40
-
41
- Examples:
42
- # Create a simple note
43
- write_note(
44
- tile="Meeting Notes: Project Planning.md",
45
- content="# Key Points\\n\\n- Discussed timeline\\n- Set priorities"
46
- folder="notes"
47
- )
48
-
49
- # Create note with tags
50
- write_note(
51
- title="Security Review",
52
- content="# Findings\\n\\n1. Updated auth flow\\n2. Added rate limiting",
53
- folder="security",
54
- tags=["security", "development"]
55
- )
56
- """
57
- logger.info(f"Writing note folder:'{folder}' title: '{title}'")
58
-
59
- # Create the entity request
60
- metadata = {"tags": [f"#{tag}" for tag in tags]} if tags else None
61
- entity = Entity(
62
- title=title,
63
- folder=folder,
64
- entity_type="note",
65
- content_type="text/markdown",
66
- content=content,
67
- entity_metadata=metadata,
68
- )
69
-
70
- # Use existing knowledge tool
71
- logger.info(f"Creating {entity.permalink}")
72
- url = f"/knowledge/entities/{entity.permalink}"
73
- response = await call_put(client, url, json=entity.model_dump())
74
- result = EntityResponse.model_validate(response.json())
75
- return result if verbose else result.permalink
76
-
77
-
78
- @mcp.tool(description="Read a note's content by its title or permalink")
79
- async def read_note(identifier: str) -> str:
80
- """Get the markdown content of a note.
81
- Uses the resource router to return the actual file content.
82
-
83
- Args:
84
- identifier: Note title or permalink
85
-
86
- Returns:
87
- The note's markdown content
88
-
89
- Examples:
90
- # Read by title
91
- read_note("Meeting Notes: Project Planning")
92
-
93
- # Read by permalink
94
- read_note("notes/project-planning")
95
-
96
- Raises:
97
- ValueError: If the note cannot be found
98
- """
99
- response = await call_get(client, f"/resource/{identifier}")
100
- return response.text
101
-
102
-
103
- @mcp.tool(description="Delete a note by title or permalink")
104
- async def delete_note(identifier: str) -> bool:
105
- """Delete a note from the knowledge base.
106
-
107
- Args:
108
- identifier: Note title or permalink
109
-
110
- Returns:
111
- True if note was deleted, False otherwise
112
-
113
- Examples:
114
- # Delete by title
115
- delete_note("Meeting Notes: Project Planning")
116
-
117
- # Delete by permalink
118
- delete_note("notes/project-planning")
119
- """
120
- response = await call_delete(client, f"/knowledge/entities/{identifier}")
121
- result = DeleteEntitiesResponse.model_validate(response.json())
122
- return result.deleted
@@ -1,28 +0,0 @@
1
- """Schemas for knowledge discovery and analytics endpoints."""
2
-
3
- from typing import List, Optional
4
- from pydantic import BaseModel, Field
5
-
6
- from basic_memory.schemas.response import EntityResponse
7
-
8
-
9
- class EntityTypeList(BaseModel):
10
- """List of unique entity types in the system."""
11
-
12
- types: List[str]
13
-
14
-
15
- class ObservationCategoryList(BaseModel):
16
- """List of unique observation categories in the system."""
17
-
18
- categories: List[str]
19
-
20
-
21
- class TypedEntityList(BaseModel):
22
- """List of entities of a specific type."""
23
-
24
- entity_type: str = Field(..., description="Type of entities in the list")
25
- entities: List[EntityResponse]
26
- total: int = Field(..., description="Total number of entities")
27
- sort_by: Optional[str] = Field(None, description="Field used for sorting")
28
- include_related: bool = Field(False, description="Whether related entities are included")