Skip to content

Commit 8527232

Browse files
committed
fix: optimize Neo4j Community Edition support and enhance MCP environment loading
- Update default Neo4j DB name to 'neo4j' for Community Edition compatibility - Add neo4j_auto_create and use_multi_db configuration options - Enhance MCP server to load all relevant environment variables from .env - Add defensive error handling in Neo4j driver for administrative commands on Community Edition - Update .env.example with detailed Neo4j setup instructions
1 parent 42c3d9d commit 8527232

File tree

4 files changed

+109
-21
lines changed

4 files changed

+109
-21
lines changed

docker/.env.example

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ NEO4J_URI=bolt://localhost:7687 # required when backend=neo4j*
9090
NEO4J_USER=neo4j # required when backend=neo4j*
9191
NEO4J_PASSWORD=12345678 # required when backend=neo4j*
9292
NEO4J_DB_NAME=neo4j # required for shared-db mode
93-
MOS_NEO4J_SHARED_DB=false
93+
MOS_NEO4J_SHARED_DB=true # if true, all users share one DB; if false, each user gets their own DB
94+
NEO4J_AUTO_CREATE=false # [IMPORTANT] set to false for Neo4j Community Edition
95+
NEO4J_USE_MULTI_DB=false # alternative to MOS_NEO4J_SHARED_DB (logic is inverse)
9496
QDRANT_HOST=localhost
9597
QDRANT_PORT=6333
9698
MILVUS_URI=http://localhost:19530 # required when ENABLE_PREFERENCE_MEMORY=true

src/memos/api/mcp_serve.py

Lines changed: 88 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,88 @@
1616

1717

1818
def load_default_config(user_id="default_user"):
19+
"""
20+
Load MOS configuration from environment variables.
21+
22+
IMPORTANT for Neo4j Community Edition:
23+
Community Edition does not support administrative commands like 'CREATE DATABASE'.
24+
To avoid errors, ensure the following environment variables are set correctly:
25+
- NEO4J_DB_NAME=neo4j (Must use the default database)
26+
- NEO4J_AUTO_CREATE=false (Disable automatic database creation)
27+
- NEO4J_USE_MULTI_DB=false (Disable multi-tenant database mode)
28+
"""
29+
# Define mapping between environment variables and configuration parameters
30+
# We support both clean names and MOS_ prefixed names for compatibility
31+
env_mapping = {
32+
"OPENAI_API_KEY": "openai_api_key",
33+
"OPENAI_API_BASE": "openai_api_base",
34+
"MOS_TEXT_MEM_TYPE": "text_mem_type",
35+
"NEO4J_URI": "neo4j_uri",
36+
"NEO4J_USER": "neo4j_user",
37+
"NEO4J_PASSWORD": "neo4j_password",
38+
"NEO4J_DB_NAME": "neo4j_db_name",
39+
"NEO4J_AUTO_CREATE": "neo4j_auto_create",
40+
"NEO4J_USE_MULTI_DB": "use_multi_db",
41+
"MOS_NEO4J_SHARED_DB": "mos_shared_db", # Special handle later
42+
"MODEL_NAME": "model_name",
43+
"MOS_CHAT_MODEL": "model_name",
44+
"EMBEDDER_MODEL": "embedder_model",
45+
"MOS_EMBEDDER_MODEL": "embedder_model",
46+
"CHUNK_SIZE": "chunk_size",
47+
"CHUNK_OVERLAP": "chunk_overlap",
48+
"ENABLE_MEM_SCHEDULER": "enable_mem_scheduler",
49+
"MOS_ENABLE_SCHEDULER": "enable_mem_scheduler",
50+
"ENABLE_ACTIVATION_MEMORY": "enable_activation_memory",
51+
"TEMPERATURE": "temperature",
52+
"MOS_CHAT_TEMPERATURE": "temperature",
53+
"MAX_TOKENS": "max_tokens",
54+
"MOS_MAX_TOKENS": "max_tokens",
55+
"TOP_P": "top_p",
56+
"MOS_TOP_P": "top_p",
57+
"TOP_K": "top_k",
58+
"MOS_TOP_K": "top_k",
59+
"SCHEDULER_TOP_K": "scheduler_top_k",
60+
"MOS_SCHEDULER_TOP_K": "scheduler_top_k",
61+
"SCHEDULER_TOP_N": "scheduler_top_n",
62+
}
63+
64+
kwargs = {"user_id": user_id}
65+
for env_key, param_key in env_mapping.items():
66+
val = os.getenv(env_key)
67+
if val is not None:
68+
# Strip quotes if they exist (sometimes happens with .env)
69+
if (val.startswith('"') and val.endswith('"')) or (
70+
val.startswith("'") and val.endswith("'")
71+
):
72+
val = val[1:-1]
73+
74+
# Handle boolean conversions
75+
if val.lower() in ("true", "false"):
76+
kwargs[param_key] = val.lower() == "true"
77+
else:
78+
# Try numeric conversions (int first, then float)
79+
try:
80+
if "." in val:
81+
kwargs[param_key] = float(val)
82+
else:
83+
kwargs[param_key] = int(val)
84+
except ValueError:
85+
kwargs[param_key] = val
86+
87+
# Logic handle for MOS_NEO4J_SHARED_DB vs use_multi_db
88+
if "mos_shared_db" in kwargs:
89+
kwargs["use_multi_db"] = not kwargs.pop("mos_shared_db")
90+
91+
# Extract mandatory or special params
92+
openai_api_key = kwargs.pop("openai_api_key", os.getenv("OPENAI_API_KEY"))
93+
openai_api_base = kwargs.pop("openai_api_base", "https://api.openai.com/v1")
94+
text_mem_type = kwargs.pop("text_mem_type", "tree_text")
95+
1996
config, cube = get_default(
20-
openai_api_key=os.getenv("OPENAI_API_KEY"),
21-
openai_api_base=os.getenv("OPENAI_API_BASE"),
22-
text_mem_type=os.getenv("MOS_TEXT_MEM_TYPE"),
23-
user_id=user_id,
24-
neo4j_uri=os.getenv("NEO4J_URI"),
25-
neo4j_user=os.getenv("NEO4J_USER"),
26-
neo4j_password=os.getenv("NEO4J_PASSWORD"),
97+
openai_api_key=openai_api_key,
98+
openai_api_base=openai_api_base,
99+
text_mem_type=text_mem_type,
100+
**kwargs,
27101
)
28102
return config, cube
29103

@@ -33,6 +107,7 @@ def __init__(self):
33107
self.mcp = FastMCP("MOS Memory System")
34108
config, cube = load_default_config()
35109
self.mos_core = MOS(config=config)
110+
self.mos_core.register_mem_cube(cube)
36111
self._setup_tools()
37112

38113
def _setup_tools(self):
@@ -132,11 +207,14 @@ async def register_cube(
132207
"""
133208
try:
134209
if not os.path.exists(cube_name_or_path):
135-
mos_config, cube_name_or_path = load_default_config(user_id=user_id)
210+
_, cube = load_default_config(user_id=user_id)
211+
cube_to_register = cube
212+
else:
213+
cube_to_register = cube_name_or_path
136214
self.mos_core.register_mem_cube(
137-
cube_name_or_path, mem_cube_id=cube_id, user_id=user_id
215+
cube_to_register, mem_cube_id=cube_id, user_id=user_id
138216
)
139-
return f"Cube registered successfully: {cube_id or cube_name_or_path}"
217+
return f"Cube registered successfully: {cube_id or cube_to_register}"
140218
except Exception as e:
141219
return f"Error registering cube: {e!s}"
142220

@@ -489,14 +567,6 @@ def run(self, transport: str = "stdio", **kwargs):
489567

490568
args = parser.parse_args()
491569

492-
# Set environment variables
493-
os.environ["OPENAI_API_BASE"] = os.getenv("OPENAI_API_BASE")
494-
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
495-
os.environ["MOS_TEXT_MEM_TYPE"] = "tree_text" # "tree_text" need set neo4j
496-
os.environ["NEO4J_URI"] = os.getenv("NEO4J_URI")
497-
os.environ["NEO4J_USER"] = os.getenv("NEO4J_USER")
498-
os.environ["NEO4J_PASSWORD"] = os.getenv("NEO4J_PASSWORD")
499-
500570
# Create and run MCP server
501571
server = MOSMCPStdioServer()
502572
server.run(transport=args.transport, host=args.host, port=args.port)

src/memos/graph_dbs/neo4j.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,15 @@ def _ensure_database_exists(self):
10861086
with self.driver.session(database="system") as session:
10871087
session.run(f"CREATE DATABASE `{self.db_name}` IF NOT EXISTS")
10881088
except ClientError as e:
1089+
if "Unsupported administration command" in str(
1090+
e
1091+
) or "Unsupported administration" in str(e):
1092+
logger.warning(
1093+
f"Could not create database '{self.db_name}' because this Neo4j instance "
1094+
"(likely Community Edition) does not support administrative commands. "
1095+
"Please ensure the database exists manually or use the default 'neo4j' database."
1096+
)
1097+
return
10891098
if "ExistingDatabaseFound" in str(e):
10901099
pass # Ignore, database already exists
10911100
else:

src/memos/mem_os/utils/default_config.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,22 @@ def get_default_cube_config(
181181
# Configure text memory based on type
182182
if text_mem_type == "tree_text":
183183
# Tree text memory requires Neo4j configuration
184+
# NOTE: Neo4j Community Edition does NOT support multiple databases.
185+
# It only has one default database named 'neo4j'.
186+
# If you are using Community Edition:
187+
# 1. Set 'use_multi_db' to False (default)
188+
# 2. Set 'db_name' to 'neo4j' (default)
189+
# 3. Set 'auto_create' to False to avoid 'CREATE DATABASE' permission errors.
184190
db_name = f"memos{user_id.replace('-', '').replace('_', '')}"
185191
if not kwargs.get("use_multi_db", False):
186-
db_name = kwargs.get("neo4j_db_name", "defaultdb")
192+
db_name = kwargs.get("neo4j_db_name", "neo4j")
193+
187194
neo4j_config = {
188195
"uri": kwargs.get("neo4j_uri", "bolt://localhost:7687"),
189196
"user": kwargs.get("neo4j_user", "neo4j"),
190197
"db_name": db_name,
191198
"password": kwargs.get("neo4j_password", "12345678"),
192-
"auto_create": True,
199+
"auto_create": kwargs.get("neo4j_auto_create", True),
193200
"use_multi_db": kwargs.get("use_multi_db", False),
194201
"embedding_dimension": kwargs.get("embedding_dimension", 3072),
195202
}

0 commit comments

Comments
 (0)