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.
- basic_memory/__init__.py +5 -1
- basic_memory/alembic/alembic.ini +119 -0
- basic_memory/alembic/env.py +27 -3
- basic_memory/alembic/migrations.py +4 -9
- basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py +51 -0
- basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py +108 -0
- basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py +104 -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/b3c3938bacdb_relation_to_name_unique_index.py +44 -0
- basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +100 -0
- basic_memory/alembic/versions/e7e1f4367280_add_scan_watermark_tracking_to_project.py +37 -0
- basic_memory/api/app.py +63 -31
- 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 +165 -28
- basic_memory/api/routers/management_router.py +80 -0
- basic_memory/api/routers/memory_router.py +28 -67
- basic_memory/api/routers/project_router.py +406 -0
- basic_memory/api/routers/prompt_router.py +260 -0
- basic_memory/api/routers/resource_router.py +219 -14
- basic_memory/api/routers/search_router.py +21 -13
- basic_memory/api/routers/utils.py +130 -0
- basic_memory/api/template_loader.py +292 -0
- basic_memory/cli/app.py +52 -1
- basic_memory/cli/auth.py +277 -0
- basic_memory/cli/commands/__init__.py +13 -2
- basic_memory/cli/commands/cloud/__init__.py +6 -0
- basic_memory/cli/commands/cloud/api_client.py +112 -0
- basic_memory/cli/commands/cloud/bisync_commands.py +110 -0
- basic_memory/cli/commands/cloud/cloud_utils.py +101 -0
- basic_memory/cli/commands/cloud/core_commands.py +195 -0
- basic_memory/cli/commands/cloud/rclone_commands.py +301 -0
- basic_memory/cli/commands/cloud/rclone_config.py +110 -0
- basic_memory/cli/commands/cloud/rclone_installer.py +249 -0
- basic_memory/cli/commands/cloud/upload.py +233 -0
- basic_memory/cli/commands/cloud/upload_command.py +124 -0
- basic_memory/cli/commands/command_utils.py +51 -0
- basic_memory/cli/commands/db.py +26 -7
- basic_memory/cli/commands/import_chatgpt.py +83 -0
- basic_memory/cli/commands/import_claude_conversations.py +86 -0
- basic_memory/cli/commands/import_claude_projects.py +85 -0
- basic_memory/cli/commands/import_memory_json.py +35 -92
- basic_memory/cli/commands/mcp.py +84 -10
- basic_memory/cli/commands/project.py +876 -0
- basic_memory/cli/commands/status.py +47 -30
- basic_memory/cli/commands/tool.py +341 -0
- basic_memory/cli/main.py +13 -6
- basic_memory/config.py +481 -22
- basic_memory/db.py +192 -32
- basic_memory/deps.py +252 -22
- basic_memory/file_utils.py +113 -58
- basic_memory/ignore_utils.py +297 -0
- basic_memory/importers/__init__.py +27 -0
- basic_memory/importers/base.py +79 -0
- basic_memory/importers/chatgpt_importer.py +232 -0
- basic_memory/importers/claude_conversations_importer.py +177 -0
- basic_memory/importers/claude_projects_importer.py +148 -0
- basic_memory/importers/memory_json_importer.py +108 -0
- basic_memory/importers/utils.py +58 -0
- basic_memory/markdown/entity_parser.py +143 -23
- basic_memory/markdown/markdown_processor.py +3 -3
- basic_memory/markdown/plugins.py +39 -21
- basic_memory/markdown/schemas.py +1 -1
- basic_memory/markdown/utils.py +28 -13
- basic_memory/mcp/async_client.py +134 -4
- basic_memory/mcp/project_context.py +141 -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 +7 -13
- basic_memory/mcp/tools/__init__.py +33 -21
- basic_memory/mcp/tools/build_context.py +120 -0
- basic_memory/mcp/tools/canvas.py +130 -0
- basic_memory/mcp/tools/chatgpt_tools.py +187 -0
- basic_memory/mcp/tools/delete_note.py +225 -0
- basic_memory/mcp/tools/edit_note.py +320 -0
- basic_memory/mcp/tools/list_directory.py +167 -0
- basic_memory/mcp/tools/move_note.py +545 -0
- basic_memory/mcp/tools/project_management.py +200 -0
- basic_memory/mcp/tools/read_content.py +271 -0
- basic_memory/mcp/tools/read_note.py +255 -0
- basic_memory/mcp/tools/recent_activity.py +534 -0
- basic_memory/mcp/tools/search.py +369 -14
- basic_memory/mcp/tools/utils.py +374 -16
- basic_memory/mcp/tools/view_note.py +77 -0
- basic_memory/mcp/tools/write_note.py +207 -0
- basic_memory/models/__init__.py +3 -2
- basic_memory/models/knowledge.py +67 -15
- basic_memory/models/project.py +87 -0
- basic_memory/models/search.py +10 -6
- basic_memory/repository/__init__.py +2 -0
- basic_memory/repository/entity_repository.py +229 -7
- basic_memory/repository/observation_repository.py +35 -3
- basic_memory/repository/project_info_repository.py +10 -0
- basic_memory/repository/project_repository.py +103 -0
- basic_memory/repository/relation_repository.py +21 -2
- basic_memory/repository/repository.py +147 -29
- basic_memory/repository/search_repository.py +437 -59
- basic_memory/schemas/__init__.py +22 -9
- basic_memory/schemas/base.py +97 -8
- basic_memory/schemas/cloud.py +50 -0
- basic_memory/schemas/directory.py +30 -0
- basic_memory/schemas/importer.py +35 -0
- basic_memory/schemas/memory.py +188 -23
- basic_memory/schemas/project_info.py +211 -0
- basic_memory/schemas/prompt.py +90 -0
- basic_memory/schemas/request.py +57 -3
- basic_memory/schemas/response.py +9 -1
- basic_memory/schemas/search.py +33 -35
- basic_memory/schemas/sync_report.py +72 -0
- basic_memory/services/__init__.py +2 -1
- basic_memory/services/context_service.py +251 -106
- basic_memory/services/directory_service.py +295 -0
- basic_memory/services/entity_service.py +595 -60
- basic_memory/services/exceptions.py +21 -0
- basic_memory/services/file_service.py +284 -30
- basic_memory/services/initialization.py +191 -0
- basic_memory/services/link_resolver.py +50 -56
- basic_memory/services/project_service.py +863 -0
- basic_memory/services/search_service.py +172 -34
- basic_memory/sync/__init__.py +3 -2
- basic_memory/sync/background_sync.py +26 -0
- basic_memory/sync/sync_service.py +1176 -96
- basic_memory/sync/watch_service.py +412 -135
- basic_memory/templates/prompts/continue_conversation.hbs +110 -0
- basic_memory/templates/prompts/search.hbs +101 -0
- basic_memory/utils.py +388 -28
- basic_memory-0.16.1.dist-info/METADATA +493 -0
- basic_memory-0.16.1.dist-info/RECORD +148 -0
- {basic_memory-0.2.12.dist-info → basic_memory-0.16.1.dist-info}/entry_points.txt +1 -0
- basic_memory/alembic/README +0 -1
- basic_memory/cli/commands/sync.py +0 -203
- basic_memory/mcp/tools/knowledge.py +0 -56
- basic_memory/mcp/tools/memory.py +0 -151
- basic_memory/mcp/tools/notes.py +0 -122
- basic_memory/schemas/discovery.py +0 -28
- basic_memory/sync/file_change_scanner.py +0 -158
- basic_memory/sync/utils.py +0 -34
- basic_memory-0.2.12.dist-info/METADATA +0 -291
- basic_memory-0.2.12.dist-info/RECORD +0 -78
- {basic_memory-0.2.12.dist-info → basic_memory-0.16.1.dist-info}/WHEEL +0 -0
- {basic_memory-0.2.12.dist-info → basic_memory-0.16.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Import command for basic-memory CLI to import project data from Claude.ai."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from basic_memory.cli.app import claude_app
|
|
10
|
+
from basic_memory.config import get_project_config
|
|
11
|
+
from basic_memory.importers.claude_projects_importer import ClaudeProjectsImporter
|
|
12
|
+
from basic_memory.markdown import EntityParser, MarkdownProcessor
|
|
13
|
+
from loguru import logger
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
from rich.panel import Panel
|
|
16
|
+
|
|
17
|
+
console = Console()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def get_markdown_processor() -> MarkdownProcessor:
|
|
21
|
+
"""Get MarkdownProcessor instance."""
|
|
22
|
+
config = get_project_config()
|
|
23
|
+
entity_parser = EntityParser(config.home)
|
|
24
|
+
return MarkdownProcessor(entity_parser)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@claude_app.command(name="projects", help="Import projects from Claude.ai.")
|
|
28
|
+
def import_projects(
|
|
29
|
+
projects_json: Annotated[Path, typer.Argument(..., help="Path to projects.json file")] = Path(
|
|
30
|
+
"projects.json"
|
|
31
|
+
),
|
|
32
|
+
base_folder: Annotated[
|
|
33
|
+
str, typer.Option(help="The base folder to place project files in.")
|
|
34
|
+
] = "projects",
|
|
35
|
+
):
|
|
36
|
+
"""Import project data from Claude.ai.
|
|
37
|
+
|
|
38
|
+
This command will:
|
|
39
|
+
1. Create a directory for each project
|
|
40
|
+
2. Store docs in a docs/ subdirectory
|
|
41
|
+
3. Place prompt template in project root
|
|
42
|
+
|
|
43
|
+
After importing, run 'basic-memory sync' to index the new files.
|
|
44
|
+
"""
|
|
45
|
+
config = get_project_config()
|
|
46
|
+
try:
|
|
47
|
+
if not projects_json.exists():
|
|
48
|
+
typer.echo(f"Error: File not found: {projects_json}", err=True)
|
|
49
|
+
raise typer.Exit(1)
|
|
50
|
+
|
|
51
|
+
# Get markdown processor
|
|
52
|
+
markdown_processor = asyncio.run(get_markdown_processor())
|
|
53
|
+
|
|
54
|
+
# Create the importer
|
|
55
|
+
importer = ClaudeProjectsImporter(config.home, markdown_processor)
|
|
56
|
+
|
|
57
|
+
# Process the file
|
|
58
|
+
base_path = config.home / base_folder if base_folder else config.home
|
|
59
|
+
console.print(f"\nImporting projects from {projects_json}...writing to {base_path}")
|
|
60
|
+
|
|
61
|
+
# Run the import
|
|
62
|
+
with projects_json.open("r", encoding="utf-8") as file:
|
|
63
|
+
json_data = json.load(file)
|
|
64
|
+
result = asyncio.run(importer.import_data(json_data, base_folder))
|
|
65
|
+
|
|
66
|
+
if not result.success: # pragma: no cover
|
|
67
|
+
typer.echo(f"Error during import: {result.error_message}", err=True)
|
|
68
|
+
raise typer.Exit(1)
|
|
69
|
+
|
|
70
|
+
# Show results
|
|
71
|
+
console.print(
|
|
72
|
+
Panel(
|
|
73
|
+
f"[green]Import complete![/green]\n\n"
|
|
74
|
+
f"Imported {result.documents} project documents\n"
|
|
75
|
+
f"Imported {result.prompts} prompt templates",
|
|
76
|
+
expand=False,
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
console.print("\nRun 'basic-memory sync' to index the new files.")
|
|
81
|
+
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.error("Import failed")
|
|
84
|
+
typer.echo(f"Error during import: {e}", err=True)
|
|
85
|
+
raise typer.Exit(1)
|
|
@@ -3,104 +3,35 @@
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import json
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Annotated
|
|
7
7
|
|
|
8
8
|
import typer
|
|
9
|
+
from basic_memory.cli.app import import_app
|
|
10
|
+
from basic_memory.config import get_project_config
|
|
11
|
+
from basic_memory.importers.memory_json_importer import MemoryJsonImporter
|
|
12
|
+
from basic_memory.markdown import EntityParser, MarkdownProcessor
|
|
9
13
|
from loguru import logger
|
|
10
14
|
from rich.console import Console
|
|
11
15
|
from rich.panel import Panel
|
|
12
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
|
|
13
|
-
|
|
14
|
-
from basic_memory.cli.app import app
|
|
15
|
-
from basic_memory.config import config
|
|
16
|
-
from basic_memory.markdown import EntityParser, MarkdownProcessor
|
|
17
|
-
from basic_memory.markdown.schemas import EntityMarkdown, EntityFrontmatter, Observation, Relation
|
|
18
|
-
|
|
19
16
|
|
|
20
17
|
console = Console()
|
|
21
18
|
|
|
22
19
|
|
|
23
|
-
async def process_memory_json(
|
|
24
|
-
json_path: Path, base_path: Path, markdown_processor: MarkdownProcessor
|
|
25
|
-
):
|
|
26
|
-
"""Import entities from memory.json using markdown processor."""
|
|
27
|
-
|
|
28
|
-
# First pass - collect all relations by source entity
|
|
29
|
-
entity_relations: Dict[str, List[Relation]] = {}
|
|
30
|
-
entities: Dict[str, Dict[str, Any]] = {}
|
|
31
|
-
|
|
32
|
-
with Progress(
|
|
33
|
-
SpinnerColumn(),
|
|
34
|
-
TextColumn("[progress.description]{task.description}"),
|
|
35
|
-
BarColumn(),
|
|
36
|
-
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
37
|
-
console=console,
|
|
38
|
-
) as progress:
|
|
39
|
-
read_task = progress.add_task("Reading memory.json...", total=None)
|
|
40
|
-
|
|
41
|
-
# First pass - collect entities and relations
|
|
42
|
-
with open(json_path) as f:
|
|
43
|
-
lines = f.readlines()
|
|
44
|
-
progress.update(read_task, total=len(lines))
|
|
45
|
-
|
|
46
|
-
for line in lines:
|
|
47
|
-
data = json.loads(line)
|
|
48
|
-
if data["type"] == "entity":
|
|
49
|
-
entities[data["name"]] = data
|
|
50
|
-
elif data["type"] == "relation":
|
|
51
|
-
# Store relation with its source entity
|
|
52
|
-
source = data.get("from") or data.get("from_id")
|
|
53
|
-
if source not in entity_relations:
|
|
54
|
-
entity_relations[source] = []
|
|
55
|
-
entity_relations[source].append(
|
|
56
|
-
Relation(
|
|
57
|
-
type=data.get("relationType") or data.get("relation_type"),
|
|
58
|
-
target=data.get("to") or data.get("to_id"),
|
|
59
|
-
)
|
|
60
|
-
)
|
|
61
|
-
progress.update(read_task, advance=1)
|
|
62
|
-
|
|
63
|
-
# Second pass - create and write entities
|
|
64
|
-
write_task = progress.add_task("Creating entities...", total=len(entities))
|
|
65
|
-
|
|
66
|
-
entities_created = 0
|
|
67
|
-
for name, entity_data in entities.items():
|
|
68
|
-
entity = EntityMarkdown(
|
|
69
|
-
frontmatter=EntityFrontmatter(
|
|
70
|
-
metadata={
|
|
71
|
-
"type": entity_data["entityType"],
|
|
72
|
-
"title": name,
|
|
73
|
-
"permalink": f"{entity_data['entityType']}/{name}",
|
|
74
|
-
}
|
|
75
|
-
),
|
|
76
|
-
content=f"# {name}\n",
|
|
77
|
-
observations=[Observation(content=obs) for obs in entity_data["observations"]],
|
|
78
|
-
relations=entity_relations.get(
|
|
79
|
-
name, []
|
|
80
|
-
), # Add any relations where this entity is the source
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
# Let markdown processor handle writing
|
|
84
|
-
file_path = base_path / f"{entity_data['entityType']}/{name}.md"
|
|
85
|
-
await markdown_processor.write_file(file_path, entity)
|
|
86
|
-
entities_created += 1
|
|
87
|
-
progress.update(write_task, advance=1)
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
"entities": entities_created,
|
|
91
|
-
"relations": sum(len(rels) for rels in entity_relations.values()),
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
20
|
async def get_markdown_processor() -> MarkdownProcessor:
|
|
96
21
|
"""Get MarkdownProcessor instance."""
|
|
22
|
+
config = get_project_config()
|
|
97
23
|
entity_parser = EntityParser(config.home)
|
|
98
24
|
return MarkdownProcessor(entity_parser)
|
|
99
25
|
|
|
100
26
|
|
|
101
|
-
@
|
|
102
|
-
def
|
|
103
|
-
json_path: Path
|
|
27
|
+
@import_app.command()
|
|
28
|
+
def memory_json(
|
|
29
|
+
json_path: Annotated[Path, typer.Argument(..., help="Path to memory.json file")] = Path(
|
|
30
|
+
"memory.json"
|
|
31
|
+
),
|
|
32
|
+
destination_folder: Annotated[
|
|
33
|
+
str, typer.Option(help="Optional destination folder within the project")
|
|
34
|
+
] = "",
|
|
104
35
|
):
|
|
105
36
|
"""Import entities and relations from a memory.json file.
|
|
106
37
|
|
|
@@ -108,36 +39,48 @@ def import_json(
|
|
|
108
39
|
1. Read entities and relations from the JSON file
|
|
109
40
|
2. Create markdown files for each entity
|
|
110
41
|
3. Include outgoing relations in each entity's markdown
|
|
111
|
-
|
|
112
|
-
After importing, run 'basic-memory sync' to index the new files.
|
|
113
42
|
"""
|
|
114
43
|
|
|
115
44
|
if not json_path.exists():
|
|
116
45
|
typer.echo(f"Error: File not found: {json_path}", err=True)
|
|
117
46
|
raise typer.Exit(1)
|
|
118
47
|
|
|
48
|
+
config = get_project_config()
|
|
119
49
|
try:
|
|
120
50
|
# Get markdown processor
|
|
121
51
|
markdown_processor = asyncio.run(get_markdown_processor())
|
|
122
52
|
|
|
53
|
+
# Create the importer
|
|
54
|
+
importer = MemoryJsonImporter(config.home, markdown_processor)
|
|
55
|
+
|
|
123
56
|
# Process the file
|
|
124
|
-
base_path = config.home
|
|
57
|
+
base_path = config.home if not destination_folder else config.home / destination_folder
|
|
125
58
|
console.print(f"\nImporting from {json_path}...writing to {base_path}")
|
|
126
|
-
|
|
59
|
+
|
|
60
|
+
# Run the import for json log format
|
|
61
|
+
file_data = []
|
|
62
|
+
with json_path.open("r", encoding="utf-8") as file:
|
|
63
|
+
for line in file:
|
|
64
|
+
json_data = json.loads(line)
|
|
65
|
+
file_data.append(json_data)
|
|
66
|
+
result = asyncio.run(importer.import_data(file_data, destination_folder))
|
|
67
|
+
|
|
68
|
+
if not result.success: # pragma: no cover
|
|
69
|
+
typer.echo(f"Error during import: {result.error_message}", err=True)
|
|
70
|
+
raise typer.Exit(1)
|
|
127
71
|
|
|
128
72
|
# Show results
|
|
129
73
|
console.print(
|
|
130
74
|
Panel(
|
|
131
75
|
f"[green]Import complete![/green]\n\n"
|
|
132
|
-
f"Created {
|
|
133
|
-
f"Added {
|
|
76
|
+
f"Created {result.entities} entities\n"
|
|
77
|
+
f"Added {result.relations} relations\n"
|
|
78
|
+
f"Skipped {result.skipped_entities} entities\n",
|
|
134
79
|
expand=False,
|
|
135
80
|
)
|
|
136
81
|
)
|
|
137
82
|
|
|
138
|
-
console.print("\nRun 'basic-memory sync' to index the new files.")
|
|
139
|
-
|
|
140
83
|
except Exception as e:
|
|
141
|
-
logger.
|
|
84
|
+
logger.error("Import failed")
|
|
142
85
|
typer.echo(f"Error during import: {e}", err=True)
|
|
143
86
|
raise typer.Exit(1)
|
basic_memory/cli/commands/mcp.py
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
"""MCP server command."""
|
|
1
|
+
"""MCP server command with streamable HTTP transport."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import os
|
|
5
|
+
import typer
|
|
6
|
+
from typing import Optional
|
|
2
7
|
|
|
3
|
-
from loguru import logger
|
|
4
8
|
from basic_memory.cli.app import app
|
|
5
|
-
from basic_memory.config import
|
|
9
|
+
from basic_memory.config import ConfigManager
|
|
6
10
|
|
|
7
11
|
# Import mcp instance
|
|
8
12
|
from basic_memory.mcp.server import mcp as mcp_server # pragma: no cover
|
|
@@ -10,11 +14,81 @@ from basic_memory.mcp.server import mcp as mcp_server # pragma: no cover
|
|
|
10
14
|
# Import mcp tools to register them
|
|
11
15
|
import basic_memory.mcp.tools # noqa: F401 # pragma: no cover
|
|
12
16
|
|
|
17
|
+
# Import prompts to register them
|
|
18
|
+
import basic_memory.mcp.prompts # noqa: F401 # pragma: no cover
|
|
19
|
+
from loguru import logger
|
|
20
|
+
import threading
|
|
21
|
+
from basic_memory.services.initialization import initialize_file_sync
|
|
22
|
+
|
|
23
|
+
config = ConfigManager().config
|
|
24
|
+
|
|
25
|
+
if not config.cloud_mode_enabled:
|
|
26
|
+
|
|
27
|
+
@app.command()
|
|
28
|
+
def mcp(
|
|
29
|
+
transport: str = typer.Option(
|
|
30
|
+
"stdio", help="Transport type: stdio, streamable-http, or sse"
|
|
31
|
+
),
|
|
32
|
+
host: str = typer.Option(
|
|
33
|
+
"0.0.0.0", help="Host for HTTP transports (use 0.0.0.0 to allow external connections)"
|
|
34
|
+
),
|
|
35
|
+
port: int = typer.Option(8000, help="Port for HTTP transports"),
|
|
36
|
+
path: str = typer.Option("/mcp", help="Path prefix for streamable-http transport"),
|
|
37
|
+
project: Optional[str] = typer.Option(None, help="Restrict MCP server to single project"),
|
|
38
|
+
): # pragma: no cover
|
|
39
|
+
"""Run the MCP server with configurable transport options.
|
|
40
|
+
|
|
41
|
+
This command starts an MCP server using one of three transport options:
|
|
42
|
+
|
|
43
|
+
- stdio: Standard I/O (good for local usage)
|
|
44
|
+
- streamable-http: Recommended for web deployments (default)
|
|
45
|
+
- sse: Server-Sent Events (for compatibility with existing clients)
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
# Validate and set project constraint if specified
|
|
49
|
+
if project:
|
|
50
|
+
config_manager = ConfigManager()
|
|
51
|
+
project_name, _ = config_manager.get_project(project)
|
|
52
|
+
if not project_name:
|
|
53
|
+
typer.echo(f"No project found named: {project}", err=True)
|
|
54
|
+
raise typer.Exit(1)
|
|
55
|
+
|
|
56
|
+
# Set env var with validated project name
|
|
57
|
+
os.environ["BASIC_MEMORY_MCP_PROJECT"] = project_name
|
|
58
|
+
logger.info(f"MCP server constrained to project: {project_name}")
|
|
59
|
+
|
|
60
|
+
app_config = ConfigManager().config
|
|
61
|
+
|
|
62
|
+
def run_file_sync():
|
|
63
|
+
"""Run file sync in a separate thread with its own event loop."""
|
|
64
|
+
loop = asyncio.new_event_loop()
|
|
65
|
+
asyncio.set_event_loop(loop)
|
|
66
|
+
try:
|
|
67
|
+
loop.run_until_complete(initialize_file_sync(app_config))
|
|
68
|
+
except Exception as e:
|
|
69
|
+
logger.error(f"File sync error: {e}", err=True)
|
|
70
|
+
finally:
|
|
71
|
+
loop.close()
|
|
72
|
+
|
|
73
|
+
logger.info(f"Sync changes enabled: {app_config.sync_changes}")
|
|
74
|
+
if app_config.sync_changes:
|
|
75
|
+
# Start the sync thread
|
|
76
|
+
sync_thread = threading.Thread(target=run_file_sync, daemon=True)
|
|
77
|
+
sync_thread.start()
|
|
78
|
+
logger.info("Started file sync in background")
|
|
79
|
+
|
|
80
|
+
# Now run the MCP server (blocks)
|
|
81
|
+
logger.info(f"Starting MCP server with {transport.upper()} transport")
|
|
13
82
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
83
|
+
if transport == "stdio":
|
|
84
|
+
mcp_server.run(
|
|
85
|
+
transport=transport,
|
|
86
|
+
)
|
|
87
|
+
elif transport == "streamable-http" or transport == "sse":
|
|
88
|
+
mcp_server.run(
|
|
89
|
+
transport=transport,
|
|
90
|
+
host=host,
|
|
91
|
+
port=port,
|
|
92
|
+
path=path,
|
|
93
|
+
log_level="INFO",
|
|
94
|
+
)
|