brainmemory-mcp 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 R&D ICWR
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.4
2
+ Name: brainmemory-mcp
3
+ Version: 0.1.0
4
+ Summary: BrainMemory-MCP — a Model Context Protocol server exposing Cognitive memory tools over HTTP + SSE.
5
+ Author: BrainMemory-MCP maintainers
6
+ Maintainer: BrainMemory-MCP maintainers
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/venturo/BrainMemory-MCP
9
+ Project-URL: Repository, https://github.com/venturo/BrainMemory-MCP
10
+ Project-URL: Issues, https://github.com/venturo/BrainMemory-MCP/issues
11
+ Project-URL: Changelog, https://github.com/venturo/BrainMemory-MCP/tree/main/docs/changelog
12
+ Keywords: mcp,model-context-protocol,memory,sse,cognitive,llm,agent
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Web Environment
15
+ Classifier: Framework :: AsyncIO
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
25
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.11
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Requires-Dist: mcp>=1.2.0
31
+ Requires-Dist: starlette>=0.37.0
32
+ Requires-Dist: uvicorn>=0.30.0
33
+ Provides-Extra: dev
34
+ Requires-Dist: ruff>=0.5.0; extra == "dev"
35
+ Requires-Dist: black>=24.0.0; extra == "dev"
36
+ Provides-Extra: build
37
+ Requires-Dist: build>=1.2.0; extra == "build"
38
+ Requires-Dist: twine>=5.0.0; extra == "build"
39
+ Dynamic: license-file
40
+
41
+ # BrainMemory-MCP
42
+
43
+ A **Model Context Protocol (MCP)** server that gives AI/LLM agents a durable
44
+ **brain memory** — the ability to store, recall, search, summarize, and forget
45
+ information across sessions through standardized MCP tool calls.
46
+
47
+ The server speaks MCP over **HTTP + Server-Sent Events (SSE)**, so remote
48
+ MCP-capable clients (Claude, IDE agents, etc.) can connect over the network.
49
+ Memory is persisted locally under **`~/.brainmemory-mcp`** (a SQLite database).
50
+
51
+ ## Cognitive Tools
52
+
53
+ | Tool | Description |
54
+ |------|-------------|
55
+ | `store_memory` | Persist a new memory (content, category, tags, importance). |
56
+ | `recall_memory` | Fetch a single memory by its id. |
57
+ | `search_memory` | Find memories by free text, category, tags and/or importance. |
58
+ | `list_memories` | List stored memories (most important & recent first). |
59
+ | `update_memory` | Modify an existing memory (only supplied fields change). |
60
+ | `forget_memory` | Delete a memory by id. |
61
+ | `summarize_memories` | Summary statistics: totals, categories, top tags. |
62
+
63
+ A read-only resource `brainmemory://stats` exposes the same summary as JSON.
64
+
65
+ ## Install
66
+
67
+ From PyPI (once published):
68
+
69
+ ```bash
70
+ python3 -m pip install brainmemory-mcp
71
+ ```
72
+
73
+ From a local checkout:
74
+
75
+ ```bash
76
+ python3 -m pip install .
77
+ ```
78
+
79
+ Both install the package and a console script named `brainmemory-mcp`.
80
+
81
+ For development (editable install):
82
+
83
+ ```bash
84
+ python3 -m pip install -e ".[dev]"
85
+ ```
86
+
87
+ To build/publish a release, see [`docs/RELEASING.md`](docs/RELEASING.md).
88
+
89
+ ## Run
90
+
91
+ ```bash
92
+ # Defaults: 127.0.0.1:8765, memory in ~/.brainmemory-mcp
93
+ brainmemory-mcp
94
+
95
+ # Custom host/port and memory location
96
+ brainmemory-mcp --host 0.0.0.0 --port 9000 --data-dir /path/to/memory
97
+
98
+ # Or without the console script
99
+ python3 -m brainmemory_mcp
100
+ ```
101
+
102
+ Endpoints once running:
103
+
104
+ - SSE stream: `http://<host>:<port>/sse`
105
+ - Message POST: `http://<host>:<port>/messages/`
106
+
107
+ ### Configuration
108
+
109
+ | Option | Env var | Default |
110
+ |--------|---------|---------|
111
+ | `--host` | `BRAINMEMORY_HOST` | `127.0.0.1` |
112
+ | `--port` | `BRAINMEMORY_PORT` | `8765` |
113
+ | `--data-dir` | `BRAINMEMORY_HOME` | `~/.brainmemory-mcp` |
114
+
115
+ ## Connect a client
116
+
117
+ Point any MCP client that supports SSE at the `/sse` endpoint. Example client
118
+ configuration (URL-based transport):
119
+
120
+ ```json
121
+ {
122
+ "mcpServers": {
123
+ "brainmemory": {
124
+ "url": "http://127.0.0.1:8765/sse"
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## How memory is stored
131
+
132
+ Memories live in `~/.brainmemory-mcp/memory.db` (SQLite, WAL mode). Each memory
133
+ has: `id`, `content`, `category`, `tags`, `importance` (1–5), `created_at`,
134
+ `updated_at`. Nothing is ever silently deleted — removal only happens through
135
+ `forget_memory`.
136
+
137
+ ## License
138
+
139
+ MIT
@@ -0,0 +1,99 @@
1
+ # BrainMemory-MCP
2
+
3
+ A **Model Context Protocol (MCP)** server that gives AI/LLM agents a durable
4
+ **brain memory** — the ability to store, recall, search, summarize, and forget
5
+ information across sessions through standardized MCP tool calls.
6
+
7
+ The server speaks MCP over **HTTP + Server-Sent Events (SSE)**, so remote
8
+ MCP-capable clients (Claude, IDE agents, etc.) can connect over the network.
9
+ Memory is persisted locally under **`~/.brainmemory-mcp`** (a SQLite database).
10
+
11
+ ## Cognitive Tools
12
+
13
+ | Tool | Description |
14
+ |------|-------------|
15
+ | `store_memory` | Persist a new memory (content, category, tags, importance). |
16
+ | `recall_memory` | Fetch a single memory by its id. |
17
+ | `search_memory` | Find memories by free text, category, tags and/or importance. |
18
+ | `list_memories` | List stored memories (most important & recent first). |
19
+ | `update_memory` | Modify an existing memory (only supplied fields change). |
20
+ | `forget_memory` | Delete a memory by id. |
21
+ | `summarize_memories` | Summary statistics: totals, categories, top tags. |
22
+
23
+ A read-only resource `brainmemory://stats` exposes the same summary as JSON.
24
+
25
+ ## Install
26
+
27
+ From PyPI (once published):
28
+
29
+ ```bash
30
+ python3 -m pip install brainmemory-mcp
31
+ ```
32
+
33
+ From a local checkout:
34
+
35
+ ```bash
36
+ python3 -m pip install .
37
+ ```
38
+
39
+ Both install the package and a console script named `brainmemory-mcp`.
40
+
41
+ For development (editable install):
42
+
43
+ ```bash
44
+ python3 -m pip install -e ".[dev]"
45
+ ```
46
+
47
+ To build/publish a release, see [`docs/RELEASING.md`](docs/RELEASING.md).
48
+
49
+ ## Run
50
+
51
+ ```bash
52
+ # Defaults: 127.0.0.1:8765, memory in ~/.brainmemory-mcp
53
+ brainmemory-mcp
54
+
55
+ # Custom host/port and memory location
56
+ brainmemory-mcp --host 0.0.0.0 --port 9000 --data-dir /path/to/memory
57
+
58
+ # Or without the console script
59
+ python3 -m brainmemory_mcp
60
+ ```
61
+
62
+ Endpoints once running:
63
+
64
+ - SSE stream: `http://<host>:<port>/sse`
65
+ - Message POST: `http://<host>:<port>/messages/`
66
+
67
+ ### Configuration
68
+
69
+ | Option | Env var | Default |
70
+ |--------|---------|---------|
71
+ | `--host` | `BRAINMEMORY_HOST` | `127.0.0.1` |
72
+ | `--port` | `BRAINMEMORY_PORT` | `8765` |
73
+ | `--data-dir` | `BRAINMEMORY_HOME` | `~/.brainmemory-mcp` |
74
+
75
+ ## Connect a client
76
+
77
+ Point any MCP client that supports SSE at the `/sse` endpoint. Example client
78
+ configuration (URL-based transport):
79
+
80
+ ```json
81
+ {
82
+ "mcpServers": {
83
+ "brainmemory": {
84
+ "url": "http://127.0.0.1:8765/sse"
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ ## How memory is stored
91
+
92
+ Memories live in `~/.brainmemory-mcp/memory.db` (SQLite, WAL mode). Each memory
93
+ has: `id`, `content`, `category`, `tags`, `importance` (1–5), `created_at`,
94
+ `updated_at`. Nothing is ever silently deleted — removal only happens through
95
+ `forget_memory`.
96
+
97
+ ## License
98
+
99
+ MIT
@@ -0,0 +1,62 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "brainmemory-mcp"
7
+ dynamic = ["version"]
8
+ description = "BrainMemory-MCP — a Model Context Protocol server exposing Cognitive memory tools over HTTP + SSE."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "BrainMemory-MCP maintainers" }]
13
+ maintainers = [{ name = "BrainMemory-MCP maintainers" }]
14
+ keywords = ["mcp", "model-context-protocol", "memory", "sse", "cognitive", "llm", "agent"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Environment :: Web Environment",
18
+ "Framework :: AsyncIO",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3 :: Only",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
28
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
29
+ "Typing :: Typed",
30
+ ]
31
+ dependencies = [
32
+ "mcp>=1.2.0",
33
+ "starlette>=0.37.0",
34
+ "uvicorn>=0.30.0",
35
+ ]
36
+
37
+ [project.optional-dependencies]
38
+ dev = ["ruff>=0.5.0", "black>=24.0.0"]
39
+ build = ["build>=1.2.0", "twine>=5.0.0"]
40
+
41
+ [project.scripts]
42
+ brainmemory-mcp = "brainmemory_mcp.__main__:main"
43
+
44
+ [project.urls]
45
+ Homepage = "https://github.com/venturo/BrainMemory-MCP"
46
+ Repository = "https://github.com/venturo/BrainMemory-MCP"
47
+ Issues = "https://github.com/venturo/BrainMemory-MCP/issues"
48
+ Changelog = "https://github.com/venturo/BrainMemory-MCP/tree/main/docs/changelog"
49
+
50
+ [tool.setuptools.dynamic]
51
+ version = { attr = "brainmemory_mcp.__version__" }
52
+
53
+ [tool.setuptools.packages.find]
54
+ where = ["src"]
55
+
56
+ [tool.ruff]
57
+ line-length = 100
58
+ target-version = "py311"
59
+
60
+ [tool.black]
61
+ line-length = 100
62
+ target-version = ["py311"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,16 @@
1
+ """BrainMemory-MCP — Cognitive memory tools served over HTTP + SSE.
2
+
3
+ A Model Context Protocol (MCP) server that gives AI/LLM agents a durable
4
+ "brain memory": the ability to store, recall, search, update, summarize, and
5
+ forget information across sessions through standardized MCP tool calls.
6
+
7
+ Memory is persisted locally under ``~/.brainmemory-mcp``.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ __version__ = "0.1.0"
13
+
14
+ from .memory import MemoryStore, Memory, default_data_dir
15
+
16
+ __all__ = ["MemoryStore", "Memory", "default_data_dir", "__version__"]
@@ -0,0 +1,63 @@
1
+ """Command-line entry point for BrainMemory-MCP.
2
+
3
+ Usage:
4
+ brainmemory-mcp --host 0.0.0.0 --port 8765
5
+ python3 -m brainmemory_mcp
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import os
12
+
13
+ from . import __version__
14
+ from .memory import DATA_DIR_ENV, default_data_dir
15
+ from .server import run
16
+
17
+
18
+ def build_parser() -> argparse.ArgumentParser:
19
+ parser = argparse.ArgumentParser(
20
+ prog="brainmemory-mcp",
21
+ description=(
22
+ "BrainMemory-MCP — Cognitive memory tools for AI agents, served over "
23
+ "the Model Context Protocol using HTTP + SSE."
24
+ ),
25
+ )
26
+ parser.add_argument(
27
+ "--host",
28
+ default=os.environ.get("BRAINMEMORY_HOST", "127.0.0.1"),
29
+ help="Host/interface to bind (default: 127.0.0.1).",
30
+ )
31
+ parser.add_argument(
32
+ "--port",
33
+ type=int,
34
+ default=int(os.environ.get("BRAINMEMORY_PORT", "8765")),
35
+ help="TCP port to listen on (default: 8765).",
36
+ )
37
+ parser.add_argument(
38
+ "--data-dir",
39
+ default=None,
40
+ help=(
41
+ "Directory to store memories in. "
42
+ f"Overrides ${DATA_DIR_ENV}. Default: {default_data_dir()}"
43
+ ),
44
+ )
45
+ parser.add_argument(
46
+ "--version",
47
+ action="version",
48
+ version=f"%(prog)s {__version__}",
49
+ )
50
+ return parser
51
+
52
+
53
+ def main(argv: list[str] | None = None) -> int:
54
+ args = build_parser().parse_args(argv)
55
+ try:
56
+ run(host=args.host, port=args.port, data_dir=args.data_dir)
57
+ except KeyboardInterrupt: # pragma: no cover - interactive shutdown
58
+ print("\nBrainMemory-MCP stopped.", flush=True)
59
+ return 0
60
+
61
+
62
+ if __name__ == "__main__": # pragma: no cover
63
+ raise SystemExit(main())
@@ -0,0 +1,318 @@
1
+ """Persistence layer for BrainMemory-MCP.
2
+
3
+ Memories are stored in a local SQLite database under ``~/.brainmemory-mcp``.
4
+ The store is intentionally small, deterministic, and dependency-free (stdlib
5
+ ``sqlite3`` only) so that the "brain memory" is safe and never silently loses
6
+ data.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import os
13
+ import sqlite3
14
+ import uuid
15
+ from dataclasses import dataclass, asdict
16
+ from datetime import datetime, timezone
17
+ from pathlib import Path
18
+ from typing import Any, Iterable
19
+
20
+ # --------------------------------------------------------------------------- #
21
+ # Paths
22
+ # --------------------------------------------------------------------------- #
23
+
24
+ DATA_DIR_ENV = "BRAINMEMORY_HOME"
25
+
26
+
27
+ def default_data_dir() -> Path:
28
+ """Return the directory where memories are persisted.
29
+
30
+ Defaults to ``~/.brainmemory-mcp`` but can be overridden with the
31
+ ``BRAINMEMORY_HOME`` environment variable.
32
+ """
33
+ override = os.environ.get(DATA_DIR_ENV)
34
+ if override:
35
+ return Path(override).expanduser().resolve()
36
+ return Path.home() / ".brainmemory-mcp"
37
+
38
+
39
+ def _utcnow() -> str:
40
+ return datetime.now(timezone.utc).isoformat()
41
+
42
+
43
+ # --------------------------------------------------------------------------- #
44
+ # Model
45
+ # --------------------------------------------------------------------------- #
46
+
47
+
48
+ @dataclass
49
+ class Memory:
50
+ """A single stored memory."""
51
+
52
+ id: str
53
+ content: str
54
+ category: str
55
+ tags: list[str]
56
+ importance: int
57
+ created_at: str
58
+ updated_at: str
59
+
60
+ def to_dict(self) -> dict[str, Any]:
61
+ return asdict(self)
62
+
63
+
64
+ def _row_to_memory(row: sqlite3.Row) -> Memory:
65
+ return Memory(
66
+ id=row["id"],
67
+ content=row["content"],
68
+ category=row["category"],
69
+ tags=json.loads(row["tags"]) if row["tags"] else [],
70
+ importance=row["importance"],
71
+ created_at=row["created_at"],
72
+ updated_at=row["updated_at"],
73
+ )
74
+
75
+
76
+ # --------------------------------------------------------------------------- #
77
+ # Store
78
+ # --------------------------------------------------------------------------- #
79
+
80
+
81
+ class MemoryStore:
82
+ """SQLite-backed memory store.
83
+
84
+ Thread/async note: each public method opens a short-lived connection, so the
85
+ store is safe to use from the MCP server's request handlers.
86
+ """
87
+
88
+ def __init__(self, data_dir: Path | None = None) -> None:
89
+ self.data_dir = Path(data_dir) if data_dir else default_data_dir()
90
+ self.data_dir.mkdir(parents=True, exist_ok=True)
91
+ self.db_path = self.data_dir / "memory.db"
92
+ self._init_db()
93
+
94
+ # -- internal ----------------------------------------------------------- #
95
+
96
+ def _connect(self) -> sqlite3.Connection:
97
+ conn = sqlite3.connect(self.db_path)
98
+ conn.row_factory = sqlite3.Row
99
+ conn.execute("PRAGMA journal_mode=WAL;")
100
+ conn.execute("PRAGMA foreign_keys=ON;")
101
+ return conn
102
+
103
+ def _init_db(self) -> None:
104
+ with self._connect() as conn:
105
+ conn.execute(
106
+ """
107
+ CREATE TABLE IF NOT EXISTS memories (
108
+ id TEXT PRIMARY KEY,
109
+ content TEXT NOT NULL,
110
+ category TEXT NOT NULL DEFAULT 'general',
111
+ tags TEXT NOT NULL DEFAULT '[]',
112
+ importance INTEGER NOT NULL DEFAULT 3,
113
+ created_at TEXT NOT NULL,
114
+ updated_at TEXT NOT NULL
115
+ );
116
+ """
117
+ )
118
+ conn.execute(
119
+ "CREATE INDEX IF NOT EXISTS idx_memories_category ON memories(category);"
120
+ )
121
+ conn.execute(
122
+ "CREATE INDEX IF NOT EXISTS idx_memories_importance ON memories(importance);"
123
+ )
124
+ conn.commit()
125
+
126
+ # -- public API --------------------------------------------------------- #
127
+
128
+ def store(
129
+ self,
130
+ content: str,
131
+ *,
132
+ category: str = "general",
133
+ tags: Iterable[str] | None = None,
134
+ importance: int = 3,
135
+ ) -> Memory:
136
+ """Persist a new memory and return it."""
137
+ if not content or not content.strip():
138
+ raise ValueError("content must not be empty")
139
+ importance = max(1, min(5, int(importance)))
140
+ now = _utcnow()
141
+ mem = Memory(
142
+ id=uuid.uuid4().hex,
143
+ content=content.strip(),
144
+ category=(category or "general").strip() or "general",
145
+ tags=sorted({t.strip() for t in (tags or []) if t and t.strip()}),
146
+ importance=importance,
147
+ created_at=now,
148
+ updated_at=now,
149
+ )
150
+ with self._connect() as conn:
151
+ conn.execute(
152
+ """
153
+ INSERT INTO memories
154
+ (id, content, category, tags, importance, created_at, updated_at)
155
+ VALUES (?, ?, ?, ?, ?, ?, ?)
156
+ """,
157
+ (
158
+ mem.id,
159
+ mem.content,
160
+ mem.category,
161
+ json.dumps(mem.tags),
162
+ mem.importance,
163
+ mem.created_at,
164
+ mem.updated_at,
165
+ ),
166
+ )
167
+ conn.commit()
168
+ return mem
169
+
170
+ def get(self, memory_id: str) -> Memory | None:
171
+ """Return a single memory by id, or ``None`` if it does not exist."""
172
+ with self._connect() as conn:
173
+ row = conn.execute(
174
+ "SELECT * FROM memories WHERE id = ?", (memory_id,)
175
+ ).fetchone()
176
+ return _row_to_memory(row) if row else None
177
+
178
+ def search(
179
+ self,
180
+ query: str | None = None,
181
+ *,
182
+ category: str | None = None,
183
+ tags: Iterable[str] | None = None,
184
+ min_importance: int | None = None,
185
+ limit: int = 20,
186
+ ) -> list[Memory]:
187
+ """Search memories by free text, category, tags and/or importance."""
188
+ clauses: list[str] = []
189
+ params: list[Any] = []
190
+
191
+ if query and query.strip():
192
+ clauses.append("(content LIKE ? OR tags LIKE ? OR category LIKE ?)")
193
+ like = f"%{query.strip()}%"
194
+ params.extend([like, like, like])
195
+
196
+ if category and category.strip():
197
+ clauses.append("category = ?")
198
+ params.append(category.strip())
199
+
200
+ if min_importance is not None:
201
+ clauses.append("importance >= ?")
202
+ params.append(max(1, min(5, int(min_importance))))
203
+
204
+ wanted_tags = [t.strip() for t in (tags or []) if t and t.strip()]
205
+ for tag in wanted_tags:
206
+ clauses.append("tags LIKE ?")
207
+ params.append(f'%"{tag}"%')
208
+
209
+ where = f"WHERE {' AND '.join(clauses)}" if clauses else ""
210
+ sql = (
211
+ f"SELECT * FROM memories {where} "
212
+ "ORDER BY importance DESC, updated_at DESC LIMIT ?"
213
+ )
214
+ params.append(max(1, int(limit)))
215
+
216
+ with self._connect() as conn:
217
+ rows = conn.execute(sql, params).fetchall()
218
+ return [_row_to_memory(r) for r in rows]
219
+
220
+ def list_all(self, *, limit: int = 100, offset: int = 0) -> list[Memory]:
221
+ """Return memories ordered by importance then recency."""
222
+ with self._connect() as conn:
223
+ rows = conn.execute(
224
+ "SELECT * FROM memories "
225
+ "ORDER BY importance DESC, updated_at DESC "
226
+ "LIMIT ? OFFSET ?",
227
+ (max(1, int(limit)), max(0, int(offset))),
228
+ ).fetchall()
229
+ return [_row_to_memory(r) for r in rows]
230
+
231
+ def update(
232
+ self,
233
+ memory_id: str,
234
+ *,
235
+ content: str | None = None,
236
+ category: str | None = None,
237
+ tags: Iterable[str] | None = None,
238
+ importance: int | None = None,
239
+ ) -> Memory | None:
240
+ """Update fields of an existing memory. Returns the updated memory."""
241
+ current = self.get(memory_id)
242
+ if current is None:
243
+ return None
244
+
245
+ if content is not None and content.strip():
246
+ current.content = content.strip()
247
+ if category is not None and category.strip():
248
+ current.category = category.strip()
249
+ if tags is not None:
250
+ current.tags = sorted({t.strip() for t in tags if t and t.strip()})
251
+ if importance is not None:
252
+ current.importance = max(1, min(5, int(importance)))
253
+ current.updated_at = _utcnow()
254
+
255
+ with self._connect() as conn:
256
+ conn.execute(
257
+ """
258
+ UPDATE memories
259
+ SET content = ?, category = ?, tags = ?, importance = ?, updated_at = ?
260
+ WHERE id = ?
261
+ """,
262
+ (
263
+ current.content,
264
+ current.category,
265
+ json.dumps(current.tags),
266
+ current.importance,
267
+ current.updated_at,
268
+ current.id,
269
+ ),
270
+ )
271
+ conn.commit()
272
+ return current
273
+
274
+ def forget(self, memory_id: str) -> bool:
275
+ """Delete a memory by id. Returns True if a row was removed."""
276
+ with self._connect() as conn:
277
+ cur = conn.execute("DELETE FROM memories WHERE id = ?", (memory_id,))
278
+ conn.commit()
279
+ return cur.rowcount > 0
280
+
281
+ def clear(self) -> int:
282
+ """Delete ALL memories. Returns the number of rows removed."""
283
+ with self._connect() as conn:
284
+ cur = conn.execute("DELETE FROM memories")
285
+ conn.commit()
286
+ return cur.rowcount
287
+
288
+ def stats(self) -> dict[str, Any]:
289
+ """Return summary statistics about the stored memories."""
290
+ with self._connect() as conn:
291
+ total = conn.execute("SELECT COUNT(*) AS c FROM memories").fetchone()["c"]
292
+ by_category = {
293
+ row["category"]: row["c"]
294
+ for row in conn.execute(
295
+ "SELECT category, COUNT(*) AS c FROM memories "
296
+ "GROUP BY category ORDER BY c DESC"
297
+ ).fetchall()
298
+ }
299
+ avg_importance_row = conn.execute(
300
+ "SELECT AVG(importance) AS a FROM memories"
301
+ ).fetchone()
302
+ avg_importance = round(avg_importance_row["a"], 2) if avg_importance_row["a"] else 0
303
+
304
+ all_tags: dict[str, int] = {}
305
+ for row in conn.execute("SELECT tags FROM memories").fetchall():
306
+ for tag in json.loads(row["tags"]) if row["tags"] else []:
307
+ all_tags[tag] = all_tags.get(tag, 0) + 1
308
+
309
+ return {
310
+ "total": total,
311
+ "by_category": by_category,
312
+ "top_tags": dict(
313
+ sorted(all_tags.items(), key=lambda kv: kv[1], reverse=True)[:10]
314
+ ),
315
+ "average_importance": avg_importance,
316
+ "data_dir": str(self.data_dir),
317
+ "db_path": str(self.db_path),
318
+ }
@@ -0,0 +1,214 @@
1
+ """BrainMemory-MCP server.
2
+
3
+ Exposes Cognitive memory tools over the Model Context Protocol using the
4
+ HTTP + Server-Sent Events (SSE) transport. Built on the official ``mcp`` SDK
5
+ (``FastMCP``).
6
+
7
+ Cognitive tools:
8
+ - store_memory : persist a new memory
9
+ - recall_memory : fetch a single memory by id
10
+ - search_memory : find memories by text / category / tags / importance
11
+ - list_memories : list stored memories (most important first)
12
+ - update_memory : modify an existing memory
13
+ - forget_memory : delete a memory
14
+ - summarize_memories : summary statistics over stored memories
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import Any
20
+
21
+ from mcp.server.fastmcp import FastMCP
22
+
23
+ from .memory import MemoryStore, default_data_dir
24
+
25
+ # --------------------------------------------------------------------------- #
26
+ # Factory
27
+ # --------------------------------------------------------------------------- #
28
+
29
+
30
+ def create_server(
31
+ *,
32
+ host: str = "127.0.0.1",
33
+ port: int = 8765,
34
+ data_dir: str | None = None,
35
+ ) -> FastMCP:
36
+ """Create and configure the BrainMemory-MCP :class:`FastMCP` server."""
37
+
38
+ store = MemoryStore(data_dir=data_dir) if data_dir else MemoryStore()
39
+
40
+ mcp = FastMCP(
41
+ name="BrainMemory-MCP",
42
+ instructions=(
43
+ "Cognitive brain-memory for AI agents. Use these tools to persist "
44
+ "durable memories across sessions, then recall/search them later. "
45
+ "Store concise, self-contained facts; tag them and set an "
46
+ "importance from 1 (trivial) to 5 (critical). Search before "
47
+ "storing to avoid duplicates."
48
+ ),
49
+ host=host,
50
+ port=port,
51
+ )
52
+
53
+ # ----------------------------------------------------------------- tools #
54
+
55
+ @mcp.tool()
56
+ def store_memory(
57
+ content: str,
58
+ category: str = "general",
59
+ tags: list[str] | None = None,
60
+ importance: int = 3,
61
+ ) -> dict[str, Any]:
62
+ """Persist a new memory in the brain.
63
+
64
+ Args:
65
+ content: The information to remember (a concise, self-contained fact).
66
+ category: A grouping label, e.g. "preferences", "facts", "tasks".
67
+ tags: Optional keywords to make the memory easier to find later.
68
+ importance: 1 (trivial) .. 5 (critical). Defaults to 3.
69
+
70
+ Returns:
71
+ The stored memory including its generated ``id``.
72
+ """
73
+ mem = store.store(
74
+ content,
75
+ category=category,
76
+ tags=tags or [],
77
+ importance=importance,
78
+ )
79
+ return {"status": "stored", "memory": mem.to_dict()}
80
+
81
+ @mcp.tool()
82
+ def recall_memory(memory_id: str) -> dict[str, Any]:
83
+ """Recall a single memory by its id.
84
+
85
+ Args:
86
+ memory_id: The id returned when the memory was stored.
87
+ """
88
+ mem = store.get(memory_id)
89
+ if mem is None:
90
+ return {"status": "not_found", "memory_id": memory_id}
91
+ return {"status": "ok", "memory": mem.to_dict()}
92
+
93
+ @mcp.tool()
94
+ def search_memory(
95
+ query: str = "",
96
+ category: str | None = None,
97
+ tags: list[str] | None = None,
98
+ min_importance: int | None = None,
99
+ limit: int = 20,
100
+ ) -> dict[str, Any]:
101
+ """Search memories by free text, category, tags and/or importance.
102
+
103
+ Args:
104
+ query: Free-text matched against content, tags and category.
105
+ category: Restrict to a single category.
106
+ tags: Only memories containing ALL of these tags.
107
+ min_importance: Only memories with importance >= this value (1..5).
108
+ limit: Maximum number of results (default 20).
109
+
110
+ Returns:
111
+ A list of matching memories, most important & most recent first.
112
+ """
113
+ results = store.search(
114
+ query=query or None,
115
+ category=category,
116
+ tags=tags or None,
117
+ min_importance=min_importance,
118
+ limit=limit,
119
+ )
120
+ return {
121
+ "status": "ok",
122
+ "count": len(results),
123
+ "memories": [m.to_dict() for m in results],
124
+ }
125
+
126
+ @mcp.tool()
127
+ def list_memories(limit: int = 100, offset: int = 0) -> dict[str, Any]:
128
+ """List stored memories, most important and most recent first.
129
+
130
+ Args:
131
+ limit: Maximum number of memories to return (default 100).
132
+ offset: Number of memories to skip (for pagination).
133
+ """
134
+ results = store.list_all(limit=limit, offset=offset)
135
+ return {
136
+ "status": "ok",
137
+ "count": len(results),
138
+ "memories": [m.to_dict() for m in results],
139
+ }
140
+
141
+ @mcp.tool()
142
+ def update_memory(
143
+ memory_id: str,
144
+ content: str | None = None,
145
+ category: str | None = None,
146
+ tags: list[str] | None = None,
147
+ importance: int | None = None,
148
+ ) -> dict[str, Any]:
149
+ """Update fields of an existing memory (only provided fields change).
150
+
151
+ Args:
152
+ memory_id: The id of the memory to update.
153
+ content: New content (optional).
154
+ category: New category (optional).
155
+ tags: Replacement tag list (optional).
156
+ importance: New importance 1..5 (optional).
157
+ """
158
+ mem = store.update(
159
+ memory_id,
160
+ content=content,
161
+ category=category,
162
+ tags=tags,
163
+ importance=importance,
164
+ )
165
+ if mem is None:
166
+ return {"status": "not_found", "memory_id": memory_id}
167
+ return {"status": "updated", "memory": mem.to_dict()}
168
+
169
+ @mcp.tool()
170
+ def forget_memory(memory_id: str) -> dict[str, Any]:
171
+ """Forget (delete) a memory by its id.
172
+
173
+ Args:
174
+ memory_id: The id of the memory to delete.
175
+ """
176
+ removed = store.forget(memory_id)
177
+ return {
178
+ "status": "forgotten" if removed else "not_found",
179
+ "memory_id": memory_id,
180
+ }
181
+
182
+ @mcp.tool()
183
+ def summarize_memories() -> dict[str, Any]:
184
+ """Summarize the brain memory: totals, categories, top tags, etc."""
185
+ return {"status": "ok", "summary": store.stats()}
186
+
187
+ # ------------------------------------------------------------- resources #
188
+
189
+ @mcp.resource("brainmemory://stats")
190
+ def stats_resource() -> str:
191
+ """Live summary statistics of the stored memories (JSON)."""
192
+ import json
193
+
194
+ return json.dumps(store.stats(), indent=2)
195
+
196
+ return mcp
197
+
198
+
199
+ def run(
200
+ *,
201
+ host: str = "127.0.0.1",
202
+ port: int = 8765,
203
+ data_dir: str | None = None,
204
+ ) -> None:
205
+ """Create the server and run it over the SSE transport."""
206
+ server = create_server(host=host, port=port, data_dir=data_dir)
207
+ resolved_dir = data_dir or str(default_data_dir())
208
+ print(
209
+ f"BrainMemory-MCP (SSE) listening on http://{host}:{port}/sse\n"
210
+ f" message endpoint : http://{host}:{port}/messages/\n"
211
+ f" memory directory : {resolved_dir}",
212
+ flush=True,
213
+ )
214
+ server.run(transport="sse")
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.4
2
+ Name: brainmemory-mcp
3
+ Version: 0.1.0
4
+ Summary: BrainMemory-MCP — a Model Context Protocol server exposing Cognitive memory tools over HTTP + SSE.
5
+ Author: BrainMemory-MCP maintainers
6
+ Maintainer: BrainMemory-MCP maintainers
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/venturo/BrainMemory-MCP
9
+ Project-URL: Repository, https://github.com/venturo/BrainMemory-MCP
10
+ Project-URL: Issues, https://github.com/venturo/BrainMemory-MCP/issues
11
+ Project-URL: Changelog, https://github.com/venturo/BrainMemory-MCP/tree/main/docs/changelog
12
+ Keywords: mcp,model-context-protocol,memory,sse,cognitive,llm,agent
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Web Environment
15
+ Classifier: Framework :: AsyncIO
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
25
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.11
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Requires-Dist: mcp>=1.2.0
31
+ Requires-Dist: starlette>=0.37.0
32
+ Requires-Dist: uvicorn>=0.30.0
33
+ Provides-Extra: dev
34
+ Requires-Dist: ruff>=0.5.0; extra == "dev"
35
+ Requires-Dist: black>=24.0.0; extra == "dev"
36
+ Provides-Extra: build
37
+ Requires-Dist: build>=1.2.0; extra == "build"
38
+ Requires-Dist: twine>=5.0.0; extra == "build"
39
+ Dynamic: license-file
40
+
41
+ # BrainMemory-MCP
42
+
43
+ A **Model Context Protocol (MCP)** server that gives AI/LLM agents a durable
44
+ **brain memory** — the ability to store, recall, search, summarize, and forget
45
+ information across sessions through standardized MCP tool calls.
46
+
47
+ The server speaks MCP over **HTTP + Server-Sent Events (SSE)**, so remote
48
+ MCP-capable clients (Claude, IDE agents, etc.) can connect over the network.
49
+ Memory is persisted locally under **`~/.brainmemory-mcp`** (a SQLite database).
50
+
51
+ ## Cognitive Tools
52
+
53
+ | Tool | Description |
54
+ |------|-------------|
55
+ | `store_memory` | Persist a new memory (content, category, tags, importance). |
56
+ | `recall_memory` | Fetch a single memory by its id. |
57
+ | `search_memory` | Find memories by free text, category, tags and/or importance. |
58
+ | `list_memories` | List stored memories (most important & recent first). |
59
+ | `update_memory` | Modify an existing memory (only supplied fields change). |
60
+ | `forget_memory` | Delete a memory by id. |
61
+ | `summarize_memories` | Summary statistics: totals, categories, top tags. |
62
+
63
+ A read-only resource `brainmemory://stats` exposes the same summary as JSON.
64
+
65
+ ## Install
66
+
67
+ From PyPI (once published):
68
+
69
+ ```bash
70
+ python3 -m pip install brainmemory-mcp
71
+ ```
72
+
73
+ From a local checkout:
74
+
75
+ ```bash
76
+ python3 -m pip install .
77
+ ```
78
+
79
+ Both install the package and a console script named `brainmemory-mcp`.
80
+
81
+ For development (editable install):
82
+
83
+ ```bash
84
+ python3 -m pip install -e ".[dev]"
85
+ ```
86
+
87
+ To build/publish a release, see [`docs/RELEASING.md`](docs/RELEASING.md).
88
+
89
+ ## Run
90
+
91
+ ```bash
92
+ # Defaults: 127.0.0.1:8765, memory in ~/.brainmemory-mcp
93
+ brainmemory-mcp
94
+
95
+ # Custom host/port and memory location
96
+ brainmemory-mcp --host 0.0.0.0 --port 9000 --data-dir /path/to/memory
97
+
98
+ # Or without the console script
99
+ python3 -m brainmemory_mcp
100
+ ```
101
+
102
+ Endpoints once running:
103
+
104
+ - SSE stream: `http://<host>:<port>/sse`
105
+ - Message POST: `http://<host>:<port>/messages/`
106
+
107
+ ### Configuration
108
+
109
+ | Option | Env var | Default |
110
+ |--------|---------|---------|
111
+ | `--host` | `BRAINMEMORY_HOST` | `127.0.0.1` |
112
+ | `--port` | `BRAINMEMORY_PORT` | `8765` |
113
+ | `--data-dir` | `BRAINMEMORY_HOME` | `~/.brainmemory-mcp` |
114
+
115
+ ## Connect a client
116
+
117
+ Point any MCP client that supports SSE at the `/sse` endpoint. Example client
118
+ configuration (URL-based transport):
119
+
120
+ ```json
121
+ {
122
+ "mcpServers": {
123
+ "brainmemory": {
124
+ "url": "http://127.0.0.1:8765/sse"
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## How memory is stored
131
+
132
+ Memories live in `~/.brainmemory-mcp/memory.db` (SQLite, WAL mode). Each memory
133
+ has: `id`, `content`, `category`, `tags`, `importance` (1–5), `created_at`,
134
+ `updated_at`. Nothing is ever silently deleted — removal only happens through
135
+ `forget_memory`.
136
+
137
+ ## License
138
+
139
+ MIT
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/brainmemory_mcp/__init__.py
5
+ src/brainmemory_mcp/__main__.py
6
+ src/brainmemory_mcp/memory.py
7
+ src/brainmemory_mcp/server.py
8
+ src/brainmemory_mcp.egg-info/PKG-INFO
9
+ src/brainmemory_mcp.egg-info/SOURCES.txt
10
+ src/brainmemory_mcp.egg-info/dependency_links.txt
11
+ src/brainmemory_mcp.egg-info/entry_points.txt
12
+ src/brainmemory_mcp.egg-info/requires.txt
13
+ src/brainmemory_mcp.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ brainmemory-mcp = brainmemory_mcp.__main__:main
@@ -0,0 +1,11 @@
1
+ mcp>=1.2.0
2
+ starlette>=0.37.0
3
+ uvicorn>=0.30.0
4
+
5
+ [build]
6
+ build>=1.2.0
7
+ twine>=5.0.0
8
+
9
+ [dev]
10
+ ruff>=0.5.0
11
+ black>=24.0.0
@@ -0,0 +1 @@
1
+ brainmemory_mcp