mem0-open-mcp 0.1.2__py3-none-any.whl → 0.1.3__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.
- {mem0_open_mcp-0.1.2.dist-info → mem0_open_mcp-0.1.3.dist-info}/METADATA +9 -1
- {mem0_open_mcp-0.1.2.dist-info → mem0_open_mcp-0.1.3.dist-info}/RECORD +7 -7
- mem0_server/__init__.py +6 -1
- mem0_server/cli.py +199 -1
- mem0_server/config/schema.py +2 -0
- {mem0_open_mcp-0.1.2.dist-info → mem0_open_mcp-0.1.3.dist-info}/WHEEL +0 -0
- {mem0_open_mcp-0.1.2.dist-info → mem0_open_mcp-0.1.3.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mem0-open-mcp
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Open-source MCP server for mem0 - local LLMs, self-hosted, Docker-free
|
|
5
5
|
Author: Alex
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -83,10 +83,18 @@ mem0-open-mcp configure
|
|
|
83
83
|
# Start the server
|
|
84
84
|
mem0-open-mcp serve
|
|
85
85
|
|
|
86
|
+
# Test configuration before starting (recommended for initial setup)
|
|
87
|
+
mem0-open-mcp serve --test
|
|
88
|
+
|
|
86
89
|
# With options
|
|
87
90
|
mem0-open-mcp serve --port 8765 --user-id alice
|
|
88
91
|
```
|
|
89
92
|
|
|
93
|
+
The `--test` flag runs connectivity and memory tests before starting the server:
|
|
94
|
+
- Checks Vector Store, LLM, and Embedder connections
|
|
95
|
+
- Performs actual memory add/search operations
|
|
96
|
+
- Cleans up test data automatically
|
|
97
|
+
|
|
90
98
|
## Configuration
|
|
91
99
|
|
|
92
100
|
Create `mem0-open-mcp.yaml`:
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
mem0_server/__init__.py,sha256=
|
|
2
|
-
mem0_server/cli.py,sha256=
|
|
1
|
+
mem0_server/__init__.py,sha256=FrcCqmcA_VSR57xjgdQ1obSrsSzxDsDsXdEGSdVhUq0,379
|
|
2
|
+
mem0_server/cli.py,sha256=TVjbm-pSj4-yS0uZ1qZcqMlGtL__JyZm6S5gBkkqvdU,24363
|
|
3
3
|
mem0_server/server.py,sha256=Obc2eZaAYuZdUgYpOY8qWwbOXgc3w1vRmmEF3tc0gdI,12494
|
|
4
4
|
mem0_server/api/__init__.py,sha256=JOOnrq3v7yym3xotMkOUAoZj1zjhI8C_a1wEdbpCakQ,122
|
|
5
5
|
mem0_server/api/routes.py,sha256=fBOeQ1kckEa9Nr57cutCunhG9D1J5zYrv9sFiYcZ1yQ,21743
|
|
6
6
|
mem0_server/config/__init__.py,sha256=Z0GWnr8I7VbrUfVLeA37K9qHOsK7HJOHiGELbWp8YcI,733
|
|
7
7
|
mem0_server/config/loader.py,sha256=LPobv8BSyPShJ_27d_hwsx9EhAJPDdzWJMrUGJ4Qdt8,8373
|
|
8
|
-
mem0_server/config/schema.py,sha256=
|
|
8
|
+
mem0_server/config/schema.py,sha256=HnFWUR4KxwHkyukFw_ptMCHt7K26Zl7AU02-5jdtjL4,12731
|
|
9
9
|
mem0_server/mcp/__init__.py,sha256=8BWWjlJj5_jdeUuQFN4i5hm5Zd3Pk0cCR2Vf0G4eUB4,127
|
|
10
10
|
mem0_server/mcp/server.py,sha256=Y_f8upEtvjr0JjZaUQ4YWLRrof9btR2ds8FL2DucCZI,184
|
|
11
11
|
mem0_server/utils/__init__.py,sha256=GvVR3Tz4OmU2gRGCbTyQg5Xip5cAx7KW6ToIh2sKA4w,41
|
|
12
|
-
mem0_open_mcp-0.1.
|
|
13
|
-
mem0_open_mcp-0.1.
|
|
14
|
-
mem0_open_mcp-0.1.
|
|
15
|
-
mem0_open_mcp-0.1.
|
|
12
|
+
mem0_open_mcp-0.1.3.dist-info/METADATA,sha256=2P1mwFuVOFhugcDwJ7KmF_nRLszRkJDAbCvO1eveuBg,4937
|
|
13
|
+
mem0_open_mcp-0.1.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
14
|
+
mem0_open_mcp-0.1.3.dist-info/entry_points.txt,sha256=WXqVdvwhFvMkzAmNtdlHlRZV23yikM43BtB8S4F9ByE,54
|
|
15
|
+
mem0_open_mcp-0.1.3.dist-info/RECORD,,
|
mem0_server/__init__.py
CHANGED
|
@@ -5,4 +5,9 @@ This package provides a CLI tool to run mem0 as an MCP server without Docker,
|
|
|
5
5
|
with optional web UI for configuration management.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
__version__ = version("mem0-open-mcp")
|
|
12
|
+
except PackageNotFoundError:
|
|
13
|
+
__version__ = "0.0.0-dev"
|
mem0_server/cli.py
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
import logging
|
|
7
|
+
import time
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
from typing import Annotated
|
|
8
10
|
|
|
@@ -29,6 +31,190 @@ app = typer.Typer(
|
|
|
29
31
|
|
|
30
32
|
console = Console()
|
|
31
33
|
|
|
34
|
+
UPDATE_CHECK_CACHE = Path.home() / ".cache" / "mem0-open-mcp" / "update_check.json"
|
|
35
|
+
UPDATE_CHECK_INTERVAL = 86400 # 24 hours
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _check_for_updates() -> None:
|
|
39
|
+
"""Check PyPI for newer version (once per day)."""
|
|
40
|
+
try:
|
|
41
|
+
UPDATE_CHECK_CACHE.parent.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
|
|
43
|
+
now = time.time()
|
|
44
|
+
if UPDATE_CHECK_CACHE.exists():
|
|
45
|
+
cache = json.loads(UPDATE_CHECK_CACHE.read_text())
|
|
46
|
+
if now - cache.get("last_check", 0) < UPDATE_CHECK_INTERVAL:
|
|
47
|
+
if cache.get("latest") and cache["latest"] != __version__:
|
|
48
|
+
console.print(
|
|
49
|
+
f"[yellow]Update available: {__version__} → {cache['latest']}[/yellow]\n"
|
|
50
|
+
f"[dim] pip install --upgrade mem0-open-mcp[/dim]\n"
|
|
51
|
+
)
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
import httpx
|
|
55
|
+
resp = httpx.get("https://pypi.org/pypi/mem0-open-mcp/json", timeout=3)
|
|
56
|
+
resp.raise_for_status()
|
|
57
|
+
latest = resp.json()["info"]["version"]
|
|
58
|
+
|
|
59
|
+
UPDATE_CHECK_CACHE.write_text(json.dumps({"last_check": now, "latest": latest}))
|
|
60
|
+
|
|
61
|
+
if latest != __version__:
|
|
62
|
+
console.print(
|
|
63
|
+
f"[yellow]Update available: {__version__} → {latest}[/yellow]\n"
|
|
64
|
+
f"[dim] pip install --upgrade mem0-open-mcp[/dim]\n"
|
|
65
|
+
)
|
|
66
|
+
except Exception:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _run_connectivity_tests(config: Mem0ServerConfig) -> bool:
|
|
71
|
+
"""Run connectivity tests for LLM, Embedder, and Vector Store."""
|
|
72
|
+
console.print("[bold]Running connectivity tests...[/bold]\n")
|
|
73
|
+
|
|
74
|
+
all_passed = True
|
|
75
|
+
|
|
76
|
+
# Test Vector Store
|
|
77
|
+
console.print(" [dim]Vector Store...[/dim]", end=" ")
|
|
78
|
+
try:
|
|
79
|
+
vs_config = config.vector_store
|
|
80
|
+
if vs_config.provider.value == "qdrant":
|
|
81
|
+
from qdrant_client import QdrantClient
|
|
82
|
+
host = vs_config.config.host or "localhost"
|
|
83
|
+
port = vs_config.config.port or 6333
|
|
84
|
+
client = QdrantClient(host=host, port=port, timeout=5)
|
|
85
|
+
client.get_collections()
|
|
86
|
+
console.print("[green]✓ Connected[/green]")
|
|
87
|
+
elif vs_config.provider.value == "chroma":
|
|
88
|
+
import chromadb
|
|
89
|
+
if vs_config.config.host:
|
|
90
|
+
client = chromadb.HttpClient(host=vs_config.config.host, port=vs_config.config.port or 8000)
|
|
91
|
+
else:
|
|
92
|
+
client = chromadb.Client()
|
|
93
|
+
client.heartbeat()
|
|
94
|
+
console.print("[green]✓ Connected[/green]")
|
|
95
|
+
else:
|
|
96
|
+
console.print(f"[yellow]⚠ Skip (no test for {vs_config.provider.value})[/yellow]")
|
|
97
|
+
except Exception as e:
|
|
98
|
+
console.print(f"[red]✗ Failed: {e}[/red]")
|
|
99
|
+
all_passed = False
|
|
100
|
+
|
|
101
|
+
# Test LLM
|
|
102
|
+
console.print(" [dim]LLM...[/dim]", end=" ")
|
|
103
|
+
try:
|
|
104
|
+
llm_config = config.llm
|
|
105
|
+
if llm_config.provider.value == "ollama":
|
|
106
|
+
import httpx
|
|
107
|
+
base_url = llm_config.config.base_url or "http://localhost:11434"
|
|
108
|
+
resp = httpx.get(f"{base_url}/api/tags", timeout=5)
|
|
109
|
+
resp.raise_for_status()
|
|
110
|
+
models = [m["name"] for m in resp.json().get("models", [])]
|
|
111
|
+
if llm_config.config.model in models or any(llm_config.config.model in m for m in models):
|
|
112
|
+
console.print(f"[green]✓ Connected ({llm_config.config.model})[/green]")
|
|
113
|
+
else:
|
|
114
|
+
console.print(f"[yellow]⚠ Connected but model '{llm_config.config.model}' not found[/yellow]")
|
|
115
|
+
console.print(f" [dim]Available: {', '.join(models[:5])}{'...' if len(models) > 5 else ''}[/dim]")
|
|
116
|
+
elif llm_config.provider.value in ("openai", "lmstudio"):
|
|
117
|
+
import httpx
|
|
118
|
+
base_url = llm_config.config.base_url or "https://api.openai.com/v1"
|
|
119
|
+
headers = {}
|
|
120
|
+
if llm_config.config.api_key:
|
|
121
|
+
headers["Authorization"] = f"Bearer {llm_config.config.api_key}"
|
|
122
|
+
resp = httpx.get(f"{base_url}/models", headers=headers, timeout=5)
|
|
123
|
+
resp.raise_for_status()
|
|
124
|
+
console.print(f"[green]✓ Connected ({llm_config.config.model})[/green]")
|
|
125
|
+
else:
|
|
126
|
+
console.print(f"[yellow]⚠ Skip (no test for {llm_config.provider.value})[/yellow]")
|
|
127
|
+
except Exception as e:
|
|
128
|
+
console.print(f"[red]✗ Failed: {e}[/red]")
|
|
129
|
+
all_passed = False
|
|
130
|
+
|
|
131
|
+
# Test Embedder
|
|
132
|
+
console.print(" [dim]Embedder...[/dim]", end=" ")
|
|
133
|
+
try:
|
|
134
|
+
emb_config = config.embedder
|
|
135
|
+
if emb_config.provider.value == "ollama":
|
|
136
|
+
import httpx
|
|
137
|
+
base_url = emb_config.config.base_url or "http://localhost:11434"
|
|
138
|
+
resp = httpx.get(f"{base_url}/api/tags", timeout=5)
|
|
139
|
+
resp.raise_for_status()
|
|
140
|
+
models = [m["name"] for m in resp.json().get("models", [])]
|
|
141
|
+
if emb_config.config.model in models or any(emb_config.config.model in m for m in models):
|
|
142
|
+
console.print(f"[green]✓ Connected ({emb_config.config.model})[/green]")
|
|
143
|
+
else:
|
|
144
|
+
console.print(f"[yellow]⚠ Connected but model '{emb_config.config.model}' not found[/yellow]")
|
|
145
|
+
console.print(f" [dim]Available: {', '.join(models[:5])}{'...' if len(models) > 5 else ''}[/dim]")
|
|
146
|
+
elif emb_config.provider.value in ("openai", "lmstudio"):
|
|
147
|
+
import httpx
|
|
148
|
+
base_url = emb_config.config.base_url or "https://api.openai.com/v1"
|
|
149
|
+
headers = {}
|
|
150
|
+
if emb_config.config.api_key:
|
|
151
|
+
headers["Authorization"] = f"Bearer {emb_config.config.api_key}"
|
|
152
|
+
resp = httpx.get(f"{base_url}/models", headers=headers, timeout=5)
|
|
153
|
+
resp.raise_for_status()
|
|
154
|
+
console.print(f"[green]✓ Connected ({emb_config.config.model})[/green]")
|
|
155
|
+
else:
|
|
156
|
+
console.print(f"[yellow]⚠ Skip (no test for {emb_config.provider.value})[/yellow]")
|
|
157
|
+
except Exception as e:
|
|
158
|
+
console.print(f"[red]✗ Failed: {e}[/red]")
|
|
159
|
+
all_passed = False
|
|
160
|
+
|
|
161
|
+
console.print()
|
|
162
|
+
if all_passed:
|
|
163
|
+
console.print("[bold green]All connectivity tests passed![/bold green]\n")
|
|
164
|
+
else:
|
|
165
|
+
console.print("[bold red]Some connectivity tests failed. Please check your configuration.[/bold red]\n")
|
|
166
|
+
|
|
167
|
+
return all_passed
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _run_memory_tests(config: Mem0ServerConfig) -> bool:
|
|
171
|
+
"""Run actual mem0 memory add/search tests."""
|
|
172
|
+
import uuid
|
|
173
|
+
console.print("[bold]Running memory tests...[/bold]\n")
|
|
174
|
+
|
|
175
|
+
test_user_id = f"__test_user_{uuid.uuid4().hex[:8]}"
|
|
176
|
+
test_memory_text = "This is a test memory for connectivity verification."
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
console.print(" [dim]Initializing mem0 client...[/dim]", end=" ")
|
|
180
|
+
from mem0 import Memory
|
|
181
|
+
mem0_config = config.to_mem0_config()
|
|
182
|
+
memory = Memory.from_config(mem0_config)
|
|
183
|
+
console.print("[green]✓[/green]")
|
|
184
|
+
|
|
185
|
+
console.print(" [dim]Adding test memory...[/dim]", end=" ")
|
|
186
|
+
add_result = memory.add(test_memory_text, user_id=test_user_id)
|
|
187
|
+
if add_result and add_result.get("results"):
|
|
188
|
+
first_result = add_result["results"][0]
|
|
189
|
+
memory_id = first_result.get("id") if first_result else None
|
|
190
|
+
if memory_id:
|
|
191
|
+
console.print(f"[green]✓ Added (id: {memory_id[:8]}...)[/green]")
|
|
192
|
+
else:
|
|
193
|
+
console.print("[green]✓ Added[/green]")
|
|
194
|
+
else:
|
|
195
|
+
console.print("[green]✓ Added[/green]")
|
|
196
|
+
|
|
197
|
+
console.print(" [dim]Searching memories...[/dim]", end=" ")
|
|
198
|
+
search_result = memory.search("test memory verification", user_id=test_user_id, limit=5)
|
|
199
|
+
if search_result and search_result.get("results"):
|
|
200
|
+
console.print(f"[green]✓ Found {len(search_result['results'])} result(s)[/green]")
|
|
201
|
+
else:
|
|
202
|
+
console.print("[yellow]⚠ No results (may be expected for new setup)[/yellow]")
|
|
203
|
+
|
|
204
|
+
console.print(" [dim]Cleaning up test data...[/dim]", end=" ")
|
|
205
|
+
memory.delete_all(user_id=test_user_id)
|
|
206
|
+
console.print("[green]✓ Cleaned[/green]")
|
|
207
|
+
|
|
208
|
+
console.print()
|
|
209
|
+
console.print("[bold green]All memory tests passed![/bold green]\n")
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
console.print(f"[red]✗ Failed: {e}[/red]")
|
|
214
|
+
console.print()
|
|
215
|
+
console.print("[bold red]Memory test failed. Check your LLM/Embedder/VectorStore configuration.[/bold red]\n")
|
|
216
|
+
return False
|
|
217
|
+
|
|
32
218
|
|
|
33
219
|
def version_callback(value: bool) -> None:
|
|
34
220
|
"""Show version and exit."""
|
|
@@ -83,6 +269,10 @@ def serve(
|
|
|
83
269
|
str,
|
|
84
270
|
typer.Option("--log-level", "-l", help="Logging level."),
|
|
85
271
|
] = "info",
|
|
272
|
+
test: Annotated[
|
|
273
|
+
bool,
|
|
274
|
+
typer.Option("--test", "-t", help="Run connectivity tests before starting server."),
|
|
275
|
+
] = False,
|
|
86
276
|
) -> None:
|
|
87
277
|
"""Start the MCP server.
|
|
88
278
|
|
|
@@ -120,7 +310,9 @@ def serve(
|
|
|
120
310
|
border_style="green",
|
|
121
311
|
))
|
|
122
312
|
|
|
123
|
-
|
|
313
|
+
_check_for_updates()
|
|
314
|
+
|
|
315
|
+
console.print("[bold]Configuration:[/bold]")
|
|
124
316
|
console.print(f" Host: [cyan]{config.server.host}[/cyan]")
|
|
125
317
|
console.print(f" Port: [cyan]{config.server.port}[/cyan]")
|
|
126
318
|
console.print(f" User ID: [cyan]{config.server.user_id}[/cyan]")
|
|
@@ -129,6 +321,12 @@ def serve(
|
|
|
129
321
|
console.print(f" Vector Store: [cyan]{config.vector_store.provider.value}[/cyan]")
|
|
130
322
|
console.print()
|
|
131
323
|
|
|
324
|
+
if test:
|
|
325
|
+
if not _run_connectivity_tests(config):
|
|
326
|
+
raise typer.Exit(1)
|
|
327
|
+
if not _run_memory_tests(config):
|
|
328
|
+
raise typer.Exit(1)
|
|
329
|
+
|
|
132
330
|
# Start the server
|
|
133
331
|
try:
|
|
134
332
|
from mem0_server.server import run_server
|
mem0_server/config/schema.py
CHANGED
|
@@ -323,6 +323,7 @@ def get_default_config() -> Mem0ServerConfig:
|
|
|
323
323
|
config=EmbedderConfig(
|
|
324
324
|
model="text-embedding-3-small",
|
|
325
325
|
api_key="env:OPENAI_API_KEY",
|
|
326
|
+
embedding_dims=1536,
|
|
326
327
|
),
|
|
327
328
|
),
|
|
328
329
|
vector_store=VectorStoreProvider(
|
|
@@ -331,6 +332,7 @@ def get_default_config() -> Mem0ServerConfig:
|
|
|
331
332
|
collection_name="mem0_memories",
|
|
332
333
|
host="localhost",
|
|
333
334
|
port=6333,
|
|
335
|
+
embedding_model_dims=1536,
|
|
334
336
|
),
|
|
335
337
|
),
|
|
336
338
|
)
|
|
File without changes
|
|
File without changes
|