unibaseio-membase 0.3.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.
- unibaseio_membase-0.3.0/.gitignore +54 -0
- unibaseio_membase-0.3.0/LICENSE +33 -0
- unibaseio_membase-0.3.0/PKG-INFO +154 -0
- unibaseio_membase-0.3.0/README.md +128 -0
- unibaseio_membase-0.3.0/membase/__init__.py +38 -0
- unibaseio_membase-0.3.0/membase/__main__.py +3 -0
- unibaseio_membase-0.3.0/membase/_version.py +1 -0
- unibaseio_membase-0.3.0/membase/cli.py +246 -0
- unibaseio_membase-0.3.0/membase/client.py +243 -0
- unibaseio_membase-0.3.0/membase/errors.py +95 -0
- unibaseio_membase-0.3.0/membase/local/__init__.py +34 -0
- unibaseio_membase-0.3.0/membase/local/backend.py +486 -0
- unibaseio_membase-0.3.0/membase/local/routes.py +89 -0
- unibaseio_membase-0.3.0/membase/local/server.py +79 -0
- unibaseio_membase-0.3.0/membase/mcp/__init__.py +126 -0
- unibaseio_membase-0.3.0/membase/mcp/automem.py +214 -0
- unibaseio_membase-0.3.0/membase/mcp/automem_tools.py +157 -0
- unibaseio_membase-0.3.0/membase/py.typed +0 -0
- unibaseio_membase-0.3.0/membase/requirements.py +53 -0
- unibaseio_membase-0.3.0/pyproject.toml +60 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Virtual Environment
|
|
24
|
+
venv/
|
|
25
|
+
ENV/
|
|
26
|
+
env/
|
|
27
|
+
.env/
|
|
28
|
+
.venv/
|
|
29
|
+
.env
|
|
30
|
+
|
|
31
|
+
# IDE
|
|
32
|
+
.idea/
|
|
33
|
+
.vscode/
|
|
34
|
+
*.swp
|
|
35
|
+
*.swo
|
|
36
|
+
|
|
37
|
+
# Logs
|
|
38
|
+
*.log
|
|
39
|
+
logs/
|
|
40
|
+
log/
|
|
41
|
+
|
|
42
|
+
# OS specific
|
|
43
|
+
.DS_Store
|
|
44
|
+
Thumbs.db
|
|
45
|
+
|
|
46
|
+
chroma_db/
|
|
47
|
+
|
|
48
|
+
out/.pytest_cache/
|
|
49
|
+
.ruff_cache/
|
|
50
|
+
*.db
|
|
51
|
+
|
|
52
|
+
typescript/dist/
|
|
53
|
+
node_modules/
|
|
54
|
+
cli/dist/
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Membase SDK — Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Unibase. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software, including its source code, packages, documentation and assets (the "Software"),
|
|
6
|
+
is proprietary to Unibase and is licensed, not sold.
|
|
7
|
+
|
|
8
|
+
1. Use. Unibase grants you a non-exclusive, non-transferable, revocable license to install and
|
|
9
|
+
use the Software as published by Unibase (the `unibaseio-membase` package on PyPI and the
|
|
10
|
+
`@unibaseio/membase` and `@unibaseio/membase-cli` packages on npm), and to
|
|
11
|
+
include it as a dependency of your own applications, to access Membase services and the
|
|
12
|
+
Membase engine.
|
|
13
|
+
|
|
14
|
+
2. Restrictions. Except as expressly permitted by this license or required by applicable law,
|
|
15
|
+
you may not: (a) redistribute, sublicense, sell, rent, lease, or otherwise make the Software
|
|
16
|
+
available to third parties on its own; (b) modify or create derivative works of the Software
|
|
17
|
+
other than for your own internal use; or (c) remove or alter any proprietary notices.
|
|
18
|
+
|
|
19
|
+
3. Third-party components. Third-party components included with the Software are licensed
|
|
20
|
+
under their own terms, which govern those components.
|
|
21
|
+
|
|
22
|
+
4. No warranty. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
24
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
25
|
+
|
|
26
|
+
5. Limitation of liability. IN NO EVENT SHALL UNIBASE BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
27
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
28
|
+
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
29
|
+
|
|
30
|
+
6. Termination. This license terminates automatically if you breach it. On termination you
|
|
31
|
+
must stop using and delete all copies of the Software.
|
|
32
|
+
|
|
33
|
+
For other licensing, contact Unibase.
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: unibaseio-membase
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Membase SDK: drop-in memory infrastructure for AI agents and apps, hosted or on your machine. Python client, CLI and MCP server.
|
|
5
|
+
Project-URL: Homepage, https://www.unibase.com/memory
|
|
6
|
+
Project-URL: Documentation, https://docs.membase.ai
|
|
7
|
+
Project-URL: Repository, https://github.com/unibaseio/membase-ai
|
|
8
|
+
Author: Unibase
|
|
9
|
+
License: Proprietary
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent-memory,agents,ai,llm,llm-memory,long-term-memory,mcp,membase,memory
|
|
12
|
+
Classifier: License :: Other/Proprietary License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: httpx<1,>=0.25
|
|
22
|
+
Provides-Extra: local
|
|
23
|
+
Requires-Dist: mcp<2,>=1.0; extra == 'local'
|
|
24
|
+
Requires-Dist: unibaseio-membase-core[all]>=0.3.0; (python_version >= '3.12') and extra == 'local'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
<p align="center">
|
|
28
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/membase-logo.png" width="72" alt="Membase">
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
<h1 align="center">Membase</h1>
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<b>One memory. Every AI.</b><br>
|
|
35
|
+
Teach it once. Every AI remembers.
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<a href="https://pypi.org/project/unibaseio-membase/"><img src="https://img.shields.io/pypi/v/unibaseio-membase?label=pypi" alt="PyPI"></a>
|
|
40
|
+
<a href="https://www.npmjs.com/package/@unibaseio/membase"><img src="https://img.shields.io/npm/v/@unibaseio/membase?label=npm" alt="npm"></a>
|
|
41
|
+
<a href="https://github.com/unibaseio/membase-ai/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-proprietary-blue" alt="Proprietary"></a>
|
|
42
|
+
</p>
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<a href="https://www.unibase.com/memory">Website</a> ·
|
|
46
|
+
<a href="https://docs.membase.ai">Docs</a> ·
|
|
47
|
+
<a href="https://www.app.membase.ai">Web app</a> ·
|
|
48
|
+
<a href="https://discord.gg/nB9EfPGsSf">Discord</a> ·
|
|
49
|
+
<a href="https://x.com/Unibase_AI">X</a>
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/hero.svg" width="840" alt="Add sources, manage one memory, use it from every AI and app">
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
Membase keeps one memory for everything you and your agents know, and every AI you use reads it.
|
|
57
|
+
You stop re-explaining yourself each time you switch models or tools.
|
|
58
|
+
|
|
59
|
+
- **Add.** Facts, documents, web pages and chat exports from Claude or ChatGPT go into one memory.
|
|
60
|
+
- **Manage.** Containers keep projects apart; search it, ask it questions, forget what should go.
|
|
61
|
+
- **Use.** Your code, Claude Code, Cursor, Codex and ChatGPT read the same memory through the SDK or MCP.
|
|
62
|
+
|
|
63
|
+
This repository is the developer side: Python and TypeScript clients, the `membase` command line
|
|
64
|
+
and an MCP server. The memory lives in your Membase account, which the
|
|
65
|
+
[web app](https://www.app.membase.ai) and browser extension also fill, or on your machine with
|
|
66
|
+
`unibaseio-membase[local]`.
|
|
67
|
+
|
|
68
|
+
## Write once, read from any AI
|
|
69
|
+
|
|
70
|
+
<p align="center">
|
|
71
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/in-action.svg" width="840" alt="Your app writes a memory; any AI reads it, here Claude Code">
|
|
72
|
+
</p>
|
|
73
|
+
|
|
74
|
+
Recordings: [Python SDK](https://github.com/unibaseio/membase-ai/blob/main/assets/videos/sdk.mp4) (57 s) ·
|
|
75
|
+
[Claude Code](https://github.com/unibaseio/membase-ai/blob/main/assets/videos/switch-ai.mp4) (1 min 33 s). The answer above is illustrative.
|
|
76
|
+
|
|
77
|
+
## Accuracy
|
|
78
|
+
|
|
79
|
+
<p align="center">
|
|
80
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/results.svg" width="840" alt="LoCoMo 93.1%, LongMemEval_S 92.6%, DMR 92.2%">
|
|
81
|
+
</p>
|
|
82
|
+
|
|
83
|
+
On three public long-term memory benchmarks, with a few thousand tokens of context per question.
|
|
84
|
+
|
|
85
|
+
## Quickstart
|
|
86
|
+
|
|
87
|
+
<p align="center">
|
|
88
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/architecture.svg" width="840" alt="Hosted, local in process, or local over HTTP">
|
|
89
|
+
</p>
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install unibaseio-membase # hosted · Python 3.10+ · needs MEMBASE_API_KEY
|
|
93
|
+
pip install 'unibaseio-membase[local]' # local · Python 3.12 or 3.13 · needs OPENAI_API_KEY
|
|
94
|
+
npm install @unibaseio/membase # TypeScript · Node 18+
|
|
95
|
+
npm install -g @unibaseio/membase-cli # the `membase` command (hosted) without Python
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from membase import Membase
|
|
100
|
+
|
|
101
|
+
m = Membase() # hosted
|
|
102
|
+
# m = Membase(local=True) # local, in ~/.membase
|
|
103
|
+
|
|
104
|
+
m.memories.add("We picked Postgres for the ledger service.", container="Engineering")
|
|
105
|
+
m.search("what database is the ledger on?")
|
|
106
|
+
m.add("Design notes …", container="Engineering", custom_id="design-1") # a document
|
|
107
|
+
m.ask("Which database did we choose for the ledger?")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Every method, its REST route and MCP tool, and the access it needs: [SDK reference](https://docs.membase.ai/build/reference/sdk-quickstart/).
|
|
111
|
+
The Python package is `unibaseio-membase` (import `membase`); `membase` on PyPI is unrelated.
|
|
112
|
+
|
|
113
|
+
## Command line
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
membase --local add "We picked Postgres for the ledger" --container Engineering
|
|
117
|
+
membase --local search "what did we pick for the ledger?"
|
|
118
|
+
membase --local ask "Which database is the ledger on?"
|
|
119
|
+
membase --local import ~/Downloads/claude-export.json # Claude, ChatGPT, markdown or JSON chats
|
|
120
|
+
membase --local agent ingest trace.json --agent coder
|
|
121
|
+
membase --local agent search "fix flaky deploy" --agent coder
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Without `--local` (or `--store DIR`, both before the command) commands use the hosted API; `agent`,
|
|
125
|
+
`import` and `serve` need `unibaseio-membase[local]`.
|
|
126
|
+
|
|
127
|
+
## MCP
|
|
128
|
+
|
|
129
|
+
<p align="center">
|
|
130
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/mcp.svg" width="840" alt="MCP clients, hosted or local server, same tools">
|
|
131
|
+
</p>
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
claude mcp add membase -- membase --local mcp # local
|
|
135
|
+
claude mcp add --transport http membase https://api.app.membase.io/mcp-http \
|
|
136
|
+
--header "Authorization: Bearer $MEMBASE_API_KEY" # hosted
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Hosted without the header, the connection signs you in and is read-only. Setup for Claude, ChatGPT,
|
|
140
|
+
Cursor, Codex and other clients: [Connect your AI](https://docs.membase.ai/connect/).
|
|
141
|
+
|
|
142
|
+
## Docs
|
|
143
|
+
|
|
144
|
+
- [SDK reference](https://docs.membase.ai/build/reference/sdk-quickstart/): methods, REST routes, MCP tools, access levels
|
|
145
|
+
- [Memory operations](https://docs.membase.ai/build/guides/memory-operations/): containers, documents, memories, profile, ask
|
|
146
|
+
- [Authentication](https://docs.membase.ai/build/reference/authentication/): keys and what each one can reach
|
|
147
|
+
- [Connect your AI](https://docs.membase.ai/connect/): per-client MCP setup
|
|
148
|
+
- [Local engine](https://github.com/unibaseio/membase-ai/blob/main/docs/reference.md): what it stores, `membase serve`, settings
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
Copyright © 2026 Unibase. All rights reserved. Proprietary; see
|
|
153
|
+
[LICENSE](https://github.com/unibaseio/membase-ai/blob/main/LICENSE). `unibaseio-membase[local]` also
|
|
154
|
+
installs unibaseio-membase-core, the compiled engine, under its own Unibase license.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/membase-logo.png" width="72" alt="Membase">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Membase</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<b>One memory. Every AI.</b><br>
|
|
9
|
+
Teach it once. Every AI remembers.
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://pypi.org/project/unibaseio-membase/"><img src="https://img.shields.io/pypi/v/unibaseio-membase?label=pypi" alt="PyPI"></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/@unibaseio/membase"><img src="https://img.shields.io/npm/v/@unibaseio/membase?label=npm" alt="npm"></a>
|
|
15
|
+
<a href="https://github.com/unibaseio/membase-ai/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-proprietary-blue" alt="Proprietary"></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="https://www.unibase.com/memory">Website</a> ·
|
|
20
|
+
<a href="https://docs.membase.ai">Docs</a> ·
|
|
21
|
+
<a href="https://www.app.membase.ai">Web app</a> ·
|
|
22
|
+
<a href="https://discord.gg/nB9EfPGsSf">Discord</a> ·
|
|
23
|
+
<a href="https://x.com/Unibase_AI">X</a>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/hero.svg" width="840" alt="Add sources, manage one memory, use it from every AI and app">
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
Membase keeps one memory for everything you and your agents know, and every AI you use reads it.
|
|
31
|
+
You stop re-explaining yourself each time you switch models or tools.
|
|
32
|
+
|
|
33
|
+
- **Add.** Facts, documents, web pages and chat exports from Claude or ChatGPT go into one memory.
|
|
34
|
+
- **Manage.** Containers keep projects apart; search it, ask it questions, forget what should go.
|
|
35
|
+
- **Use.** Your code, Claude Code, Cursor, Codex and ChatGPT read the same memory through the SDK or MCP.
|
|
36
|
+
|
|
37
|
+
This repository is the developer side: Python and TypeScript clients, the `membase` command line
|
|
38
|
+
and an MCP server. The memory lives in your Membase account, which the
|
|
39
|
+
[web app](https://www.app.membase.ai) and browser extension also fill, or on your machine with
|
|
40
|
+
`unibaseio-membase[local]`.
|
|
41
|
+
|
|
42
|
+
## Write once, read from any AI
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/in-action.svg" width="840" alt="Your app writes a memory; any AI reads it, here Claude Code">
|
|
46
|
+
</p>
|
|
47
|
+
|
|
48
|
+
Recordings: [Python SDK](https://github.com/unibaseio/membase-ai/blob/main/assets/videos/sdk.mp4) (57 s) ·
|
|
49
|
+
[Claude Code](https://github.com/unibaseio/membase-ai/blob/main/assets/videos/switch-ai.mp4) (1 min 33 s). The answer above is illustrative.
|
|
50
|
+
|
|
51
|
+
## Accuracy
|
|
52
|
+
|
|
53
|
+
<p align="center">
|
|
54
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/results.svg" width="840" alt="LoCoMo 93.1%, LongMemEval_S 92.6%, DMR 92.2%">
|
|
55
|
+
</p>
|
|
56
|
+
|
|
57
|
+
On three public long-term memory benchmarks, with a few thousand tokens of context per question.
|
|
58
|
+
|
|
59
|
+
## Quickstart
|
|
60
|
+
|
|
61
|
+
<p align="center">
|
|
62
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/architecture.svg" width="840" alt="Hosted, local in process, or local over HTTP">
|
|
63
|
+
</p>
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install unibaseio-membase # hosted · Python 3.10+ · needs MEMBASE_API_KEY
|
|
67
|
+
pip install 'unibaseio-membase[local]' # local · Python 3.12 or 3.13 · needs OPENAI_API_KEY
|
|
68
|
+
npm install @unibaseio/membase # TypeScript · Node 18+
|
|
69
|
+
npm install -g @unibaseio/membase-cli # the `membase` command (hosted) without Python
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from membase import Membase
|
|
74
|
+
|
|
75
|
+
m = Membase() # hosted
|
|
76
|
+
# m = Membase(local=True) # local, in ~/.membase
|
|
77
|
+
|
|
78
|
+
m.memories.add("We picked Postgres for the ledger service.", container="Engineering")
|
|
79
|
+
m.search("what database is the ledger on?")
|
|
80
|
+
m.add("Design notes …", container="Engineering", custom_id="design-1") # a document
|
|
81
|
+
m.ask("Which database did we choose for the ledger?")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Every method, its REST route and MCP tool, and the access it needs: [SDK reference](https://docs.membase.ai/build/reference/sdk-quickstart/).
|
|
85
|
+
The Python package is `unibaseio-membase` (import `membase`); `membase` on PyPI is unrelated.
|
|
86
|
+
|
|
87
|
+
## Command line
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
membase --local add "We picked Postgres for the ledger" --container Engineering
|
|
91
|
+
membase --local search "what did we pick for the ledger?"
|
|
92
|
+
membase --local ask "Which database is the ledger on?"
|
|
93
|
+
membase --local import ~/Downloads/claude-export.json # Claude, ChatGPT, markdown or JSON chats
|
|
94
|
+
membase --local agent ingest trace.json --agent coder
|
|
95
|
+
membase --local agent search "fix flaky deploy" --agent coder
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Without `--local` (or `--store DIR`, both before the command) commands use the hosted API; `agent`,
|
|
99
|
+
`import` and `serve` need `unibaseio-membase[local]`.
|
|
100
|
+
|
|
101
|
+
## MCP
|
|
102
|
+
|
|
103
|
+
<p align="center">
|
|
104
|
+
<img src="https://raw.githubusercontent.com/unibaseio/membase-ai/main/assets/mcp.svg" width="840" alt="MCP clients, hosted or local server, same tools">
|
|
105
|
+
</p>
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
claude mcp add membase -- membase --local mcp # local
|
|
109
|
+
claude mcp add --transport http membase https://api.app.membase.io/mcp-http \
|
|
110
|
+
--header "Authorization: Bearer $MEMBASE_API_KEY" # hosted
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Hosted without the header, the connection signs you in and is read-only. Setup for Claude, ChatGPT,
|
|
114
|
+
Cursor, Codex and other clients: [Connect your AI](https://docs.membase.ai/connect/).
|
|
115
|
+
|
|
116
|
+
## Docs
|
|
117
|
+
|
|
118
|
+
- [SDK reference](https://docs.membase.ai/build/reference/sdk-quickstart/): methods, REST routes, MCP tools, access levels
|
|
119
|
+
- [Memory operations](https://docs.membase.ai/build/guides/memory-operations/): containers, documents, memories, profile, ask
|
|
120
|
+
- [Authentication](https://docs.membase.ai/build/reference/authentication/): keys and what each one can reach
|
|
121
|
+
- [Connect your AI](https://docs.membase.ai/connect/): per-client MCP setup
|
|
122
|
+
- [Local engine](https://github.com/unibaseio/membase-ai/blob/main/docs/reference.md): what it stores, `membase serve`, settings
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
Copyright © 2026 Unibase. All rights reserved. Proprietary; see
|
|
127
|
+
[LICENSE](https://github.com/unibaseio/membase-ai/blob/main/LICENSE). `unibaseio-membase[local]` also
|
|
128
|
+
installs unibaseio-membase-core, the compiled engine, under its own Unibase license.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Membase — long-term memory for AI."""
|
|
2
|
+
|
|
3
|
+
from ._version import __version__
|
|
4
|
+
from .client import DEFAULT_BASE_URL, Membase
|
|
5
|
+
from .requirements import MissingDependencyError
|
|
6
|
+
from .errors import (
|
|
7
|
+
APIConnectionError,
|
|
8
|
+
APIStatusError,
|
|
9
|
+
APITimeoutError,
|
|
10
|
+
AuthenticationError,
|
|
11
|
+
BadRequestError,
|
|
12
|
+
ConflictError,
|
|
13
|
+
InternalServerError,
|
|
14
|
+
MembaseError,
|
|
15
|
+
NotFoundError,
|
|
16
|
+
PermissionDeniedError,
|
|
17
|
+
RateLimitError,
|
|
18
|
+
UnprocessableEntityError,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"DEFAULT_BASE_URL",
|
|
23
|
+
"Membase",
|
|
24
|
+
"MembaseError",
|
|
25
|
+
"MissingDependencyError",
|
|
26
|
+
"APIConnectionError",
|
|
27
|
+
"APITimeoutError",
|
|
28
|
+
"APIStatusError",
|
|
29
|
+
"BadRequestError",
|
|
30
|
+
"AuthenticationError",
|
|
31
|
+
"PermissionDeniedError",
|
|
32
|
+
"NotFoundError",
|
|
33
|
+
"ConflictError",
|
|
34
|
+
"UnprocessableEntityError",
|
|
35
|
+
"RateLimitError",
|
|
36
|
+
"InternalServerError",
|
|
37
|
+
"__version__",
|
|
38
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""``membase``: the Membase command line, hosted or local."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from ._version import __version__
|
|
12
|
+
from .client import Membase
|
|
13
|
+
from .errors import MembaseError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _client(args: argparse.Namespace) -> Membase:
|
|
17
|
+
if args.local or args.store:
|
|
18
|
+
return Membase(local=args.store or True)
|
|
19
|
+
try:
|
|
20
|
+
return Membase(api_key=args.api_key, base_url=args.base_url)
|
|
21
|
+
except ValueError:
|
|
22
|
+
raise SystemExit("no API key: pass --api-key or set MEMBASE_API_KEY (Connect › Developer keys), "
|
|
23
|
+
"or use --local") from None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _print(obj: Any) -> None:
|
|
27
|
+
print(json.dumps(obj, ensure_ascii=False, indent=2, default=str))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
_IMPORTABLE = {".json", ".md", ".markdown", ".txt"}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _import_files(paths: list[str]) -> list[Path]:
|
|
34
|
+
out: list[Path] = []
|
|
35
|
+
for raw in paths:
|
|
36
|
+
p = Path(raw).expanduser()
|
|
37
|
+
if p.is_dir():
|
|
38
|
+
out += sorted(f for f in p.rglob("*") if f.is_file() and f.suffix.lower() in _IMPORTABLE)
|
|
39
|
+
elif p.is_file():
|
|
40
|
+
out.append(p)
|
|
41
|
+
else:
|
|
42
|
+
raise SystemExit(f"no such file or directory: {raw}")
|
|
43
|
+
return out
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _cmd_import(args: argparse.Namespace, m: Membase) -> Any:
|
|
47
|
+
try:
|
|
48
|
+
from membase_core.sources import detect_format, load_sessions
|
|
49
|
+
except ImportError:
|
|
50
|
+
raise SystemExit("membase import reads chat exports with the local engine: pip install 'unibaseio-membase[local]'")
|
|
51
|
+
sessions: list[dict] = []
|
|
52
|
+
found: list[dict] = []
|
|
53
|
+
for f in _import_files(args.files):
|
|
54
|
+
got = load_sessions(f)
|
|
55
|
+
sessions += got
|
|
56
|
+
found.append({"file": str(f), "format": detect_format(f), "sessions": len(got)})
|
|
57
|
+
if not sessions:
|
|
58
|
+
raise SystemExit("no sessions found")
|
|
59
|
+
if args.dry_run:
|
|
60
|
+
return {"would_import": len(sessions), "files": found}
|
|
61
|
+
transport = m._http._transport # noqa: SLF001 - the local transport, when local
|
|
62
|
+
backend = getattr(transport, "backend", None)
|
|
63
|
+
if backend is not None:
|
|
64
|
+
return backend.import_sessions(sessions, args.container)
|
|
65
|
+
out = []
|
|
66
|
+
for s in sessions:
|
|
67
|
+
text = "\n".join(f"{t['role']}: {t['content']}" for t in s["turns"])
|
|
68
|
+
out.append(m.add(text, container=args.container, title=s["session_id"],
|
|
69
|
+
custom_id=f"session:{s['session_id']}"))
|
|
70
|
+
return {"sessions": len(out), "documents": out}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _load_trace(path: str) -> tuple[list[dict], dict]:
|
|
74
|
+
raw = sys.stdin.read() if path == "-" else Path(path).expanduser().read_text(encoding="utf-8")
|
|
75
|
+
data = json.loads(raw)
|
|
76
|
+
if isinstance(data, list):
|
|
77
|
+
return data, {}
|
|
78
|
+
if isinstance(data, dict) and isinstance(data.get("messages"), list):
|
|
79
|
+
return data["messages"], {k: data[k] for k in ("agent_id", "session_id") if data.get(k)}
|
|
80
|
+
raise SystemExit("a trace is a JSON list of messages or an object with a 'messages' list")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _cmd_agent(args: argparse.Namespace, m: Membase) -> Any:
|
|
84
|
+
backend = getattr(m._http._transport, "backend", None) # noqa: SLF001
|
|
85
|
+
if backend is None:
|
|
86
|
+
raise SystemExit("agent memory is local: use membase --local agent ...")
|
|
87
|
+
if args.agent_cmd == "ingest":
|
|
88
|
+
messages, meta = _load_trace(args.trace)
|
|
89
|
+
session = args.session or meta.get("session_id") or (Path(args.trace).stem if args.trace != "-" else None)
|
|
90
|
+
return backend.agent_ingest(messages, agent_id=args.agent, session_id=session, container=args.container)
|
|
91
|
+
if args.agent_cmd == "search":
|
|
92
|
+
return backend.agent_search(args.q, agent_id=args.agent, kind=args.kind, limit=args.limit,
|
|
93
|
+
container=args.container)
|
|
94
|
+
return backend.agent_skills(args.agent, container=args.container)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _parser() -> argparse.ArgumentParser:
|
|
98
|
+
p = argparse.ArgumentParser(prog="membase", description="Membase — long-term memory for AI.")
|
|
99
|
+
p.add_argument("--version", action="version", version=__version__)
|
|
100
|
+
p.add_argument("--local", action="store_true",
|
|
101
|
+
help="use local memory (store ~/.membase) instead of the hosted API")
|
|
102
|
+
p.add_argument("--store", default=None, metavar="DIR",
|
|
103
|
+
help="local store directory (default ~/.membase); implies --local")
|
|
104
|
+
p.add_argument("--api-key", default=None, help="hosted API key (default MEMBASE_API_KEY)")
|
|
105
|
+
p.add_argument("--base-url", default=None, help="hosted API base URL (default MEMBASE_BASE_URL)")
|
|
106
|
+
sub = p.add_subparsers(dest="cmd", required=True)
|
|
107
|
+
|
|
108
|
+
sub.add_parser("containers", help="list the containers in reach")
|
|
109
|
+
|
|
110
|
+
s = sub.add_parser("search", help="search memory")
|
|
111
|
+
s.add_argument("q")
|
|
112
|
+
s.add_argument("--container", help="one container id (default: every container in reach)")
|
|
113
|
+
s.add_argument("--limit", type=int, help="passages in all, 1-50")
|
|
114
|
+
|
|
115
|
+
a = sub.add_parser("add", help="remember one fact or a short exchange")
|
|
116
|
+
a.add_argument("content")
|
|
117
|
+
a.add_argument("--container", help="container id (may be omitted when one is in reach)")
|
|
118
|
+
a.add_argument("--static", action="store_true", help="a standing fact about the user (profile)")
|
|
119
|
+
a.add_argument("--title", default="")
|
|
120
|
+
|
|
121
|
+
d = sub.add_parser("documents", help="documents: add / list / get / delete")
|
|
122
|
+
dsub = d.add_subparsers(dest="doc_cmd", required=True)
|
|
123
|
+
da = dsub.add_parser("add", help="give a container raw material: a file, --url or --text")
|
|
124
|
+
da.add_argument("path", nargs="?")
|
|
125
|
+
da.add_argument("--url", help="a public web address to fetch")
|
|
126
|
+
da.add_argument("--text", help="the document's text")
|
|
127
|
+
da.add_argument("--container", default=None, help="container id (default: default)")
|
|
128
|
+
da.add_argument("--title", default="")
|
|
129
|
+
da.add_argument("--custom-id", default="", help="your own id; the same id again is a no-op")
|
|
130
|
+
dl = dsub.add_parser("list", help="documents, newest first")
|
|
131
|
+
dl.add_argument("--container")
|
|
132
|
+
dg = dsub.add_parser("get", help="one document, with whether it is learned")
|
|
133
|
+
dg.add_argument("document_id")
|
|
134
|
+
dd = dsub.add_parser("delete", help="remove a document")
|
|
135
|
+
dd.add_argument("document_id")
|
|
136
|
+
dd.add_argument("--confirm", action="store_true", help="remove it; without this, only show what would go")
|
|
137
|
+
|
|
138
|
+
f = sub.add_parser("forget", help="forget one memory by id")
|
|
139
|
+
f.add_argument("memory_id")
|
|
140
|
+
f.add_argument("--container")
|
|
141
|
+
f.add_argument("--confirm", action="store_true", help="forget it; without this, only show what would go")
|
|
142
|
+
|
|
143
|
+
pr = sub.add_parser("profile", help="who the user is")
|
|
144
|
+
pr.add_argument("q", nargs="?")
|
|
145
|
+
|
|
146
|
+
k = sub.add_parser("ask", help="an answer composed from memory")
|
|
147
|
+
k.add_argument("message")
|
|
148
|
+
|
|
149
|
+
i = sub.add_parser("import", help="import chat exports (Claude, ChatGPT, markdown, JSON); directories are scanned")
|
|
150
|
+
i.add_argument("files", nargs="+")
|
|
151
|
+
i.add_argument("--container")
|
|
152
|
+
i.add_argument("--dry-run", action="store_true", help="only report what would be imported")
|
|
153
|
+
|
|
154
|
+
g = sub.add_parser("agent", help="agent memory (local): traces to cases and skills")
|
|
155
|
+
gsub = g.add_subparsers(dest="agent_cmd", required=True)
|
|
156
|
+
gi = gsub.add_parser("ingest", help="learn from an agent trace (chat-completions JSON, or '-' for stdin)")
|
|
157
|
+
gi.add_argument("trace")
|
|
158
|
+
gi.add_argument("--agent", required=True, help="the agent's id (owner of its cases and skills)")
|
|
159
|
+
gi.add_argument("--session", default=None)
|
|
160
|
+
gi.add_argument("--container")
|
|
161
|
+
gs = gsub.add_parser("search", help="search an agent's cases and skills")
|
|
162
|
+
gs.add_argument("q")
|
|
163
|
+
gs.add_argument("--agent", required=True)
|
|
164
|
+
gs.add_argument("--kind", choices=("cases", "skills", "both"), default="both")
|
|
165
|
+
gs.add_argument("--limit", type=int, default=10)
|
|
166
|
+
gs.add_argument("--container")
|
|
167
|
+
gk = gsub.add_parser("skills", help="list an agent's skills")
|
|
168
|
+
gk.add_argument("--agent", required=True)
|
|
169
|
+
gk.add_argument("--container")
|
|
170
|
+
|
|
171
|
+
sv = sub.add_parser("serve", help="serve local memory over HTTP on the /v1 routes (always local; "
|
|
172
|
+
"MEMBASE_LOCAL_TOKEN requires a bearer)")
|
|
173
|
+
sv.add_argument("--host", default="127.0.0.1", help="default 127.0.0.1")
|
|
174
|
+
sv.add_argument("--port", type=int, default=8787, help="default 8787")
|
|
175
|
+
|
|
176
|
+
mc = sub.add_parser("mcp", help="run the MCP server (hosted, or local with --local)")
|
|
177
|
+
mc.add_argument("--transport", choices=("stdio", "sse", "streamable-http"), default="stdio")
|
|
178
|
+
mc.add_argument("--host", default="127.0.0.1", help="default 127.0.0.1 (sse / streamable-http)")
|
|
179
|
+
mc.add_argument("--port", type=int, default=8765, help="default 8765 (sse / streamable-http)")
|
|
180
|
+
return p
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def main(argv: list[str] | None = None) -> int:
|
|
184
|
+
args = _parser().parse_args(argv)
|
|
185
|
+
try:
|
|
186
|
+
if args.cmd == "serve":
|
|
187
|
+
from .local.server import serve
|
|
188
|
+
|
|
189
|
+
serve(args.host, args.port, root=args.store)
|
|
190
|
+
return 0
|
|
191
|
+
if args.cmd == "mcp":
|
|
192
|
+
from .requirements import require_mcp
|
|
193
|
+
|
|
194
|
+
require_mcp()
|
|
195
|
+
m = _client(args)
|
|
196
|
+
if args.cmd == "mcp":
|
|
197
|
+
from .mcp import run
|
|
198
|
+
|
|
199
|
+
run(m, transport=args.transport, host=args.host, port=args.port)
|
|
200
|
+
return 0
|
|
201
|
+
if args.cmd == "containers":
|
|
202
|
+
out = m.containers.list()
|
|
203
|
+
elif args.cmd == "search":
|
|
204
|
+
out = m.search(args.q, container=args.container, limit=args.limit)
|
|
205
|
+
elif args.cmd == "add":
|
|
206
|
+
out = m.memories.add(args.content, container=args.container, static=args.static, title=args.title)
|
|
207
|
+
elif args.cmd == "documents":
|
|
208
|
+
if args.doc_cmd == "add":
|
|
209
|
+
if args.path:
|
|
210
|
+
content = Path(args.path).read_text()
|
|
211
|
+
title = args.title or Path(args.path).stem
|
|
212
|
+
else:
|
|
213
|
+
content, title = args.text, args.title
|
|
214
|
+
if (content is None) == (args.url is None):
|
|
215
|
+
raise SystemExit("documents add needs exactly one of PATH, --text or --url")
|
|
216
|
+
out = m.add(content, container=args.container or "default", url=args.url,
|
|
217
|
+
title=title, custom_id=args.custom_id)
|
|
218
|
+
elif args.doc_cmd == "list":
|
|
219
|
+
out = m.documents.list(container=args.container)
|
|
220
|
+
elif args.doc_cmd == "get":
|
|
221
|
+
out = m.documents.get(args.document_id)
|
|
222
|
+
else:
|
|
223
|
+
out = m.documents.delete(args.document_id, confirm=args.confirm)
|
|
224
|
+
elif args.cmd == "forget":
|
|
225
|
+
out = m.memories.forget(args.memory_id, container=args.container, confirm=args.confirm)
|
|
226
|
+
elif args.cmd == "profile":
|
|
227
|
+
out = m.profile(args.q)
|
|
228
|
+
elif args.cmd == "ask":
|
|
229
|
+
out = m.ask(args.message)
|
|
230
|
+
elif args.cmd == "agent":
|
|
231
|
+
out = _cmd_agent(args, m)
|
|
232
|
+
else:
|
|
233
|
+
out = _cmd_import(args, m)
|
|
234
|
+
_print(out)
|
|
235
|
+
m.close()
|
|
236
|
+
return 0
|
|
237
|
+
except MembaseError as e:
|
|
238
|
+
print(f"error: {e}", file=sys.stderr)
|
|
239
|
+
return 1
|
|
240
|
+
except ValueError as e:
|
|
241
|
+
print(f"error: {e}", file=sys.stderr)
|
|
242
|
+
return 2
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
if __name__ == "__main__":
|
|
246
|
+
sys.exit(main())
|