basic-memory 0.12.3__py3-none-any.whl → 0.13.0b2__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 (107) hide show
  1. basic_memory/__init__.py +7 -1
  2. basic_memory/alembic/env.py +1 -1
  3. basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py +108 -0
  4. basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +0 -5
  5. basic_memory/api/app.py +43 -13
  6. basic_memory/api/routers/__init__.py +4 -2
  7. basic_memory/api/routers/directory_router.py +63 -0
  8. basic_memory/api/routers/importer_router.py +152 -0
  9. basic_memory/api/routers/knowledge_router.py +127 -38
  10. basic_memory/api/routers/management_router.py +78 -0
  11. basic_memory/api/routers/memory_router.py +4 -59
  12. basic_memory/api/routers/project_router.py +230 -0
  13. basic_memory/api/routers/prompt_router.py +260 -0
  14. basic_memory/api/routers/search_router.py +3 -21
  15. basic_memory/api/routers/utils.py +130 -0
  16. basic_memory/api/template_loader.py +292 -0
  17. basic_memory/cli/app.py +20 -21
  18. basic_memory/cli/commands/__init__.py +2 -1
  19. basic_memory/cli/commands/auth.py +136 -0
  20. basic_memory/cli/commands/db.py +3 -3
  21. basic_memory/cli/commands/import_chatgpt.py +31 -207
  22. basic_memory/cli/commands/import_claude_conversations.py +16 -142
  23. basic_memory/cli/commands/import_claude_projects.py +33 -143
  24. basic_memory/cli/commands/import_memory_json.py +26 -83
  25. basic_memory/cli/commands/mcp.py +71 -18
  26. basic_memory/cli/commands/project.py +99 -67
  27. basic_memory/cli/commands/status.py +19 -9
  28. basic_memory/cli/commands/sync.py +44 -58
  29. basic_memory/cli/main.py +1 -5
  30. basic_memory/config.py +144 -88
  31. basic_memory/db.py +6 -4
  32. basic_memory/deps.py +227 -30
  33. basic_memory/importers/__init__.py +27 -0
  34. basic_memory/importers/base.py +79 -0
  35. basic_memory/importers/chatgpt_importer.py +222 -0
  36. basic_memory/importers/claude_conversations_importer.py +172 -0
  37. basic_memory/importers/claude_projects_importer.py +148 -0
  38. basic_memory/importers/memory_json_importer.py +93 -0
  39. basic_memory/importers/utils.py +58 -0
  40. basic_memory/markdown/entity_parser.py +5 -2
  41. basic_memory/mcp/auth_provider.py +270 -0
  42. basic_memory/mcp/external_auth_provider.py +321 -0
  43. basic_memory/mcp/project_session.py +103 -0
  44. basic_memory/mcp/prompts/continue_conversation.py +18 -68
  45. basic_memory/mcp/prompts/recent_activity.py +19 -3
  46. basic_memory/mcp/prompts/search.py +14 -140
  47. basic_memory/mcp/prompts/utils.py +3 -3
  48. basic_memory/mcp/{tools → resources}/project_info.py +6 -2
  49. basic_memory/mcp/server.py +82 -8
  50. basic_memory/mcp/supabase_auth_provider.py +463 -0
  51. basic_memory/mcp/tools/__init__.py +20 -0
  52. basic_memory/mcp/tools/build_context.py +11 -1
  53. basic_memory/mcp/tools/canvas.py +15 -2
  54. basic_memory/mcp/tools/delete_note.py +12 -4
  55. basic_memory/mcp/tools/edit_note.py +297 -0
  56. basic_memory/mcp/tools/list_directory.py +154 -0
  57. basic_memory/mcp/tools/move_note.py +87 -0
  58. basic_memory/mcp/tools/project_management.py +300 -0
  59. basic_memory/mcp/tools/read_content.py +15 -6
  60. basic_memory/mcp/tools/read_note.py +17 -5
  61. basic_memory/mcp/tools/recent_activity.py +11 -2
  62. basic_memory/mcp/tools/search.py +10 -1
  63. basic_memory/mcp/tools/utils.py +137 -12
  64. basic_memory/mcp/tools/write_note.py +11 -15
  65. basic_memory/models/__init__.py +3 -2
  66. basic_memory/models/knowledge.py +16 -4
  67. basic_memory/models/project.py +80 -0
  68. basic_memory/models/search.py +8 -5
  69. basic_memory/repository/__init__.py +2 -0
  70. basic_memory/repository/entity_repository.py +8 -3
  71. basic_memory/repository/observation_repository.py +35 -3
  72. basic_memory/repository/project_info_repository.py +3 -2
  73. basic_memory/repository/project_repository.py +85 -0
  74. basic_memory/repository/relation_repository.py +8 -2
  75. basic_memory/repository/repository.py +107 -15
  76. basic_memory/repository/search_repository.py +87 -27
  77. basic_memory/schemas/__init__.py +6 -0
  78. basic_memory/schemas/directory.py +30 -0
  79. basic_memory/schemas/importer.py +34 -0
  80. basic_memory/schemas/memory.py +26 -12
  81. basic_memory/schemas/project_info.py +112 -2
  82. basic_memory/schemas/prompt.py +90 -0
  83. basic_memory/schemas/request.py +56 -2
  84. basic_memory/schemas/search.py +1 -1
  85. basic_memory/services/__init__.py +2 -1
  86. basic_memory/services/context_service.py +208 -95
  87. basic_memory/services/directory_service.py +167 -0
  88. basic_memory/services/entity_service.py +385 -5
  89. basic_memory/services/exceptions.py +6 -0
  90. basic_memory/services/file_service.py +14 -15
  91. basic_memory/services/initialization.py +144 -67
  92. basic_memory/services/link_resolver.py +16 -8
  93. basic_memory/services/project_service.py +548 -0
  94. basic_memory/services/search_service.py +77 -2
  95. basic_memory/sync/background_sync.py +25 -0
  96. basic_memory/sync/sync_service.py +10 -9
  97. basic_memory/sync/watch_service.py +63 -39
  98. basic_memory/templates/prompts/continue_conversation.hbs +110 -0
  99. basic_memory/templates/prompts/search.hbs +101 -0
  100. {basic_memory-0.12.3.dist-info → basic_memory-0.13.0b2.dist-info}/METADATA +23 -1
  101. basic_memory-0.13.0b2.dist-info/RECORD +132 -0
  102. basic_memory/api/routers/project_info_router.py +0 -274
  103. basic_memory/mcp/main.py +0 -24
  104. basic_memory-0.12.3.dist-info/RECORD +0 -100
  105. {basic_memory-0.12.3.dist-info → basic_memory-0.13.0b2.dist-info}/WHEEL +0 -0
  106. {basic_memory-0.12.3.dist-info → basic_memory-0.13.0b2.dist-info}/entry_points.txt +0 -0
  107. {basic_memory-0.12.3.dist-info → basic_memory-0.13.0b2.dist-info}/licenses/LICENSE +0 -0
@@ -4,20 +4,17 @@ These prompts help users continue conversations and work across sessions,
4
4
  providing context from previous interactions to maintain continuity.
5
5
  """
6
6
 
7
- from textwrap import dedent
8
7
  from typing import Annotated, Optional
9
8
 
10
9
  from loguru import logger
11
10
  from pydantic import Field
12
11
 
13
- from basic_memory.mcp.prompts.utils import PromptContext, PromptContextItem, format_prompt_context
12
+ from basic_memory.config import get_project_config
13
+ from basic_memory.mcp.async_client import client
14
14
  from basic_memory.mcp.server import mcp
15
- from basic_memory.mcp.tools.build_context import build_context
16
- from basic_memory.mcp.tools.recent_activity import recent_activity
17
- from basic_memory.mcp.tools.search import search_notes
15
+ from basic_memory.mcp.tools.utils import call_post
18
16
  from basic_memory.schemas.base import TimeFrame
19
- from basic_memory.schemas.memory import GraphContext
20
- from basic_memory.schemas.search import SearchItemType
17
+ from basic_memory.schemas.prompt import ContinueConversationRequest
21
18
 
22
19
 
23
20
  @mcp.prompt(
@@ -45,67 +42,20 @@ async def continue_conversation(
45
42
  """
46
43
  logger.info(f"Continuing session, topic: {topic}, timeframe: {timeframe}")
47
44
 
48
- # If topic provided, search for it
49
- if topic:
50
- search_results = await search_notes(
51
- query=topic, after_date=timeframe, entity_types=[SearchItemType.ENTITY]
52
- )
45
+ # Create request model
46
+ request = ContinueConversationRequest( # pyright: ignore [reportCallIssue]
47
+ topic=topic, timeframe=timeframe
48
+ )
53
49
 
54
- # Build context from results
55
- contexts = []
56
- for result in search_results.results:
57
- if hasattr(result, "permalink") and result.permalink:
58
- context: GraphContext = await build_context(f"memory://{result.permalink}")
59
- if context.primary_results:
60
- contexts.append(
61
- PromptContextItem(
62
- primary_results=context.primary_results[:1], # pyright: ignore
63
- related_results=context.related_results[:3], # pyright: ignore
64
- )
65
- )
50
+ project_url = get_project_config().project_url
66
51
 
67
- # get context for the top 3 results
68
- prompt_context = format_prompt_context(
69
- PromptContext(topic=topic, timeframe=timeframe, results=contexts) # pyright: ignore
70
- )
52
+ # Call the prompt API endpoint
53
+ response = await call_post(
54
+ client,
55
+ f"{project_url}/prompt/continue-conversation",
56
+ json=request.model_dump(exclude_none=True),
57
+ )
71
58
 
72
- else:
73
- # If no topic, get recent activity
74
- timeframe = timeframe or "7d"
75
- recent: GraphContext = await recent_activity(
76
- timeframe=timeframe, type=[SearchItemType.ENTITY]
77
- )
78
- prompt_context = format_prompt_context(
79
- PromptContext(
80
- topic=f"Recent Activity from ({timeframe})",
81
- timeframe=timeframe,
82
- results=[
83
- PromptContextItem(
84
- primary_results=recent.primary_results[:5], # pyright: ignore
85
- related_results=recent.related_results[:2], # pyright: ignore
86
- )
87
- ],
88
- )
89
- )
90
-
91
- # Add next steps with strong encouragement to write
92
- next_steps = dedent(f"""
93
- ## Next Steps
94
-
95
- You can:
96
- - Explore more with: `search_notes({{"text": "{topic}"}})`
97
- - See what's changed: `recent_activity(timeframe="{timeframe or "7d"}")`
98
- - **Record new learnings or decisions from this conversation:** `write_note(title="[Create a meaningful title]", content="[Content with observations and relations]")`
99
-
100
- ## Knowledge Capture Recommendation
101
-
102
- As you continue this conversation, **actively look for opportunities to:**
103
- 1. Record key information, decisions, or insights that emerge
104
- 2. Link new knowledge to existing topics
105
- 3. Suggest capturing important context when appropriate
106
- 4. Create forward references to topics that might be created later
107
-
108
- Remember that capturing knowledge during conversations is one of the most valuable aspects of Basic Memory.
109
- """)
110
-
111
- return prompt_context + next_steps
59
+ # Extract the rendered prompt from the response
60
+ result = response.json()
61
+ return result["prompt"]
@@ -40,20 +40,36 @@ async def recent_activity_prompt(
40
40
 
41
41
  recent = await recent_activity(timeframe=timeframe, type=[SearchItemType.ENTITY])
42
42
 
43
+ # Extract primary results from the hierarchical structure
44
+ primary_results = []
45
+ related_results = []
46
+
47
+ if recent.results:
48
+ # Take up to 5 primary results
49
+ for item in recent.results[:5]:
50
+ primary_results.append(item.primary_result)
51
+ # Add up to 2 related results per primary item
52
+ if item.related_results:
53
+ related_results.extend(item.related_results[:2])
54
+
43
55
  prompt_context = format_prompt_context(
44
56
  PromptContext(
45
57
  topic=f"Recent Activity from ({timeframe})",
46
58
  timeframe=timeframe,
47
59
  results=[
48
60
  PromptContextItem(
49
- primary_results=recent.primary_results[:5],
50
- related_results=recent.related_results[:2],
61
+ primary_results=primary_results,
62
+ related_results=related_results[:10], # Limit total related results
51
63
  )
52
64
  ],
53
65
  )
54
66
  )
55
67
 
56
68
  # Add suggestions for summarizing recent activity
69
+ first_title = "Recent Topic"
70
+ if primary_results and len(primary_results) > 0:
71
+ first_title = primary_results[0].title
72
+
57
73
  capture_suggestions = f"""
58
74
  ## Opportunity to Capture Activity Summary
59
75
 
@@ -76,7 +92,7 @@ async def recent_activity_prompt(
76
92
  - [insight] [Connection between different activities]
77
93
 
78
94
  ## Relations
79
- - summarizes [[{recent.primary_results[0].title if recent.primary_results else "Recent Topic"}]]
95
+ - summarizes [[{first_title}]]
80
96
  - relates_to [[Project Overview]]
81
97
  '''
82
98
  )
@@ -3,16 +3,17 @@
3
3
  These prompts help users search and explore their knowledge base.
4
4
  """
5
5
 
6
- from textwrap import dedent
7
6
  from typing import Annotated, Optional
8
7
 
9
8
  from loguru import logger
10
9
  from pydantic import Field
11
10
 
11
+ from basic_memory.config import get_project_config
12
+ from basic_memory.mcp.async_client import client
12
13
  from basic_memory.mcp.server import mcp
13
- from basic_memory.mcp.tools.search import search_notes as search_tool
14
+ from basic_memory.mcp.tools.utils import call_post
14
15
  from basic_memory.schemas.base import TimeFrame
15
- from basic_memory.schemas.search import SearchResponse
16
+ from basic_memory.schemas.prompt import SearchPromptRequest
16
17
 
17
18
 
18
19
  @mcp.prompt(
@@ -40,143 +41,16 @@ async def search_prompt(
40
41
  """
41
42
  logger.info(f"Searching knowledge base, query: {query}, timeframe: {timeframe}")
42
43
 
43
- search_results = await search_tool(query=query, after_date=timeframe)
44
- return format_search_results(query, search_results, timeframe)
44
+ # Create request model
45
+ request = SearchPromptRequest(query=query, timeframe=timeframe)
45
46
 
47
+ project_url = get_project_config().project_url
46
48
 
47
- def format_search_results(
48
- query: str, results: SearchResponse, timeframe: Optional[TimeFrame] = None
49
- ) -> str:
50
- """Format search results into a helpful summary.
51
-
52
- Args:
53
- query: The search query
54
- results: Search results object
55
- timeframe: How far back results were searched
56
-
57
- Returns:
58
- Formatted search results summary
59
- """
60
- if not results.results:
61
- return dedent(f"""
62
- # Search Results for: "{query}"
63
-
64
- I couldn't find any results for this query.
65
-
66
- ## Opportunity to Capture Knowledge!
67
-
68
- This is an excellent opportunity to create new knowledge on this topic. Consider:
69
-
70
- ```python
71
- await write_note(
72
- title="{query.capitalize()}",
73
- content=f'''
74
- # {query.capitalize()}
75
-
76
- ## Overview
77
- [Summary of what we've discussed about {query}]
78
-
79
- ## Observations
80
- - [category] [First observation about {query}]
81
- - [category] [Second observation about {query}]
82
-
83
- ## Relations
84
- - relates_to [[Other Relevant Topic]]
85
- '''
86
- )
87
- ```
88
-
89
- ## Other Suggestions
90
- - Try a different search term
91
- - Broaden your search criteria
92
- - Check recent activity with `recent_activity(timeframe="1w")`
93
- """)
94
-
95
- # Start building our summary with header
96
- time_info = f" (after {timeframe})" if timeframe else ""
97
- summary = dedent(f"""
98
- # Search Results for: "{query}"{time_info}
99
-
100
- This is a memory search session.
101
- Please use the available basic-memory tools to gather relevant context before responding.
102
- I found {len(results.results)} results that match your query.
103
-
104
- Here are the most relevant results:
105
- """)
106
-
107
- # Add each search result
108
- for i, result in enumerate(results.results[:5]): # Limit to top 5 results
109
- summary += dedent(f"""
110
- ## {i + 1}. {result.title}
111
- - **Type**: {result.type.value}
112
- """)
113
-
114
- # Add creation date if available in metadata
115
- if result.metadata and "created_at" in result.metadata:
116
- created_at = result.metadata["created_at"]
117
- if hasattr(created_at, "strftime"):
118
- summary += (
119
- f"- **Created**: {created_at.strftime('%Y-%m-%d %H:%M')}\n" # pragma: no cover
120
- )
121
- elif isinstance(created_at, str):
122
- summary += f"- **Created**: {created_at}\n"
123
-
124
- # Add score and excerpt
125
- summary += f"- **Relevance Score**: {result.score:.2f}\n"
126
-
127
- # Add excerpt if available in metadata
128
- if result.content:
129
- summary += f"- **Excerpt**:\n{result.content}\n"
130
-
131
- # Add permalink for retrieving content
132
- if result.permalink:
133
- summary += dedent(f"""
134
- You can view this content with: `read_note("{result.permalink}")`
135
- Or explore its context with: `build_context("memory://{result.permalink}")`
136
- """)
137
- else:
138
- summary += dedent(f"""
139
- You can view this file with: `read_file("{result.file_path}")`
140
- """) # pragma: no cover
141
-
142
- # Add next steps with strong write encouragement
143
- summary += dedent(f"""
144
- ## Next Steps
145
-
146
- You can:
147
- - Refine your search: `search_notes("{query} AND additional_term")`
148
- - Exclude terms: `search_notes("{query} NOT exclude_term")`
149
- - View more results: `search_notes("{query}", after_date=None)`
150
- - Check recent activity: `recent_activity()`
151
-
152
- ## Synthesize and Capture Knowledge
153
-
154
- Consider creating a new note that synthesizes what you've learned:
155
-
156
- ```python
157
- await write_note(
158
- title="Synthesis of {query.capitalize()} Information",
159
- content='''
160
- # Synthesis of {query.capitalize()} Information
161
-
162
- ## Overview
163
- [Synthesis of the search results and your conversation]
164
-
165
- ## Key Insights
166
- [Summary of main points learned from these results]
167
-
168
- ## Observations
169
- - [insight] [Important observation from search results]
170
- - [connection] [How this connects to other topics]
171
-
172
- ## Relations
173
- - relates_to [[{results.results[0].title if results.results else "Related Topic"}]]
174
- - extends [[Another Relevant Topic]]
175
- '''
176
- )
177
- ```
178
-
179
- Remember that capturing synthesized knowledge is one of the most valuable features of Basic Memory.
180
- """)
49
+ # Call the prompt API endpoint
50
+ response = await call_post(
51
+ client, f"{project_url}/prompt/search", json=request.model_dump(exclude_none=True)
52
+ )
181
53
 
182
- return summary
54
+ # Extract the rendered prompt from the response
55
+ result = response.json()
56
+ return result["prompt"]
@@ -35,7 +35,7 @@ def format_prompt_context(context: PromptContext) -> str:
35
35
  Returns:
36
36
  Formatted continuation summary
37
37
  """
38
- if not context.results:
38
+ if not context.results: # pragma: no cover
39
39
  return dedent(f"""
40
40
  # Continuing conversation on: {context.topic}
41
41
 
@@ -138,11 +138,11 @@ def format_prompt_context(context: PromptContext) -> str:
138
138
  - type: **{related.type}**
139
139
  - title: {related.title}
140
140
  """)
141
- if related.permalink:
141
+ if related.permalink: # pragma: no cover
142
142
  section_content += (
143
143
  f'You can view this document with: `read_note("{related.permalink}")`'
144
144
  )
145
- else:
145
+ else: # pragma: no cover
146
146
  section_content += (
147
147
  f'You can view this file with: `read_file("{related.file_path}")`'
148
148
  )
@@ -2,13 +2,15 @@
2
2
 
3
3
  from loguru import logger
4
4
 
5
+ from basic_memory.mcp.project_session import get_active_project
5
6
  from basic_memory.mcp.async_client import client
6
7
  from basic_memory.mcp.server import mcp
7
8
  from basic_memory.mcp.tools.utils import call_get
8
9
  from basic_memory.schemas import ProjectInfoResponse
9
10
 
10
11
 
11
- @mcp.tool(
12
+ @mcp.resource(
13
+ uri="memory://project_info",
12
14
  description="Get information and statistics about the current Basic Memory project.",
13
15
  )
14
16
  async def project_info() -> ProjectInfoResponse:
@@ -43,9 +45,11 @@ async def project_info() -> ProjectInfoResponse:
43
45
  print(f"Basic Memory version: {info.system.version}")
44
46
  """
45
47
  logger.info("Getting project info")
48
+ project_config = get_active_project()
49
+ project_url = project_config.project_url
46
50
 
47
51
  # Call the API endpoint
48
- response = await call_get(client, "/stats/project-info")
52
+ response = await call_get(client, f"{project_url}/project/info")
49
53
 
50
54
  # Convert response to ProjectInfoResponse
51
55
  return ProjectInfoResponse.model_validate(response.json())
@@ -1,19 +1,32 @@
1
- """Enhanced FastMCP server instance for Basic Memory."""
1
+ """
2
+ Basic Memory FastMCP server.
3
+ """
2
4
 
3
5
  import asyncio
4
6
  from contextlib import asynccontextmanager
5
- from typing import AsyncIterator, Optional
6
-
7
- from mcp.server.fastmcp import FastMCP
8
- from mcp.server.fastmcp.utilities.logging import configure_logging as mcp_configure_logging
9
7
  from dataclasses import dataclass
8
+ from typing import AsyncIterator, Optional, Any
9
+
10
+ from dotenv import load_dotenv
11
+ from fastmcp import FastMCP
12
+ from fastmcp.utilities.logging import configure_logging as mcp_configure_logging
13
+ from mcp.server.auth.settings import AuthSettings
10
14
 
11
- from basic_memory.config import config as project_config
15
+ from basic_memory.config import app_config
12
16
  from basic_memory.services.initialization import initialize_app
17
+ from basic_memory.mcp.auth_provider import BasicMemoryOAuthProvider
18
+ from basic_memory.mcp.project_session import session
19
+ from basic_memory.mcp.external_auth_provider import (
20
+ create_github_provider,
21
+ create_google_provider,
22
+ )
23
+ from basic_memory.mcp.supabase_auth_provider import SupabaseOAuthProvider
13
24
 
14
25
  # mcp console logging
15
26
  mcp_configure_logging(level="ERROR")
16
27
 
28
+ load_dotenv()
29
+
17
30
 
18
31
  @dataclass
19
32
  class AppContext:
@@ -24,7 +37,11 @@ class AppContext:
24
37
  async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: # pragma: no cover
25
38
  """Manage application lifecycle with type-safe context"""
26
39
  # Initialize on startup
27
- watch_task = await initialize_app(project_config)
40
+ watch_task = await initialize_app(app_config)
41
+
42
+ # Initialize project session with default project
43
+ session.initialize(app_config.default_project)
44
+
28
45
  try:
29
46
  yield AppContext(watch_task=watch_task)
30
47
  finally:
@@ -33,5 +50,62 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: # pragma:
33
50
  watch_task.cancel()
34
51
 
35
52
 
53
+ # OAuth configuration function
54
+ def create_auth_config() -> tuple[AuthSettings | None, Any | None]:
55
+ """Create OAuth configuration if enabled."""
56
+ # Check if OAuth is enabled via environment variable
57
+ import os
58
+
59
+ if os.getenv("FASTMCP_AUTH_ENABLED", "false").lower() == "true":
60
+ from pydantic import AnyHttpUrl
61
+
62
+ # Configure OAuth settings
63
+ issuer_url = os.getenv("FASTMCP_AUTH_ISSUER_URL", "http://localhost:8000")
64
+ required_scopes = os.getenv("FASTMCP_AUTH_REQUIRED_SCOPES", "read,write")
65
+ docs_url = os.getenv("FASTMCP_AUTH_DOCS_URL") or "http://localhost:8000/docs/oauth"
66
+
67
+ auth_settings = AuthSettings(
68
+ issuer_url=AnyHttpUrl(issuer_url),
69
+ service_documentation_url=AnyHttpUrl(docs_url),
70
+ required_scopes=required_scopes.split(",") if required_scopes else ["read", "write"],
71
+ )
72
+
73
+ # Create OAuth provider based on type
74
+ provider_type = os.getenv("FASTMCP_AUTH_PROVIDER", "basic").lower()
75
+
76
+ if provider_type == "github":
77
+ auth_provider = create_github_provider()
78
+ elif provider_type == "google":
79
+ auth_provider = create_google_provider()
80
+ elif provider_type == "supabase":
81
+ supabase_url = os.getenv("SUPABASE_URL")
82
+ supabase_anon_key = os.getenv("SUPABASE_ANON_KEY")
83
+ supabase_service_key = os.getenv("SUPABASE_SERVICE_KEY")
84
+
85
+ if not supabase_url or not supabase_anon_key:
86
+ raise ValueError("SUPABASE_URL and SUPABASE_ANON_KEY must be set for Supabase auth")
87
+
88
+ auth_provider = SupabaseOAuthProvider(
89
+ supabase_url=supabase_url,
90
+ supabase_anon_key=supabase_anon_key,
91
+ supabase_service_key=supabase_service_key,
92
+ issuer_url=issuer_url,
93
+ )
94
+ else: # default to "basic"
95
+ auth_provider = BasicMemoryOAuthProvider(issuer_url=issuer_url)
96
+
97
+ return auth_settings, auth_provider
98
+
99
+ return None, None
100
+
101
+
102
+ # Create auth configuration
103
+ auth_settings, auth_provider = create_auth_config()
104
+
36
105
  # Create the shared server instance
37
- mcp = FastMCP("Basic Memory", log_level="ERROR", lifespan=app_lifespan)
106
+ mcp = FastMCP(
107
+ name="Basic Memory",
108
+ log_level="DEBUG",
109
+ auth_server_provider=auth_provider,
110
+ auth=auth_settings,
111
+ )