mag-memory 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.
- mag_memory-0.1.0/.gitignore +18 -0
- mag_memory-0.1.0/PKG-INFO +66 -0
- mag_memory-0.1.0/README.md +37 -0
- mag_memory-0.1.0/mag_memory/__init__.py +75 -0
- mag_memory-0.1.0/mag_memory/_download.py +186 -0
- mag_memory-0.1.0/pyproject.toml +42 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/target
|
|
2
|
+
.cargo-cache/
|
|
3
|
+
|
|
4
|
+
.env
|
|
5
|
+
.env.local
|
|
6
|
+
# Local provenance / legal notes (not published)
|
|
7
|
+
PROVENANCE.local.md
|
|
8
|
+
|
|
9
|
+
# Claude Code worktrees (agent isolation)
|
|
10
|
+
.claude/worktrees/
|
|
11
|
+
|
|
12
|
+
# Local MCP config (machine-specific paths)
|
|
13
|
+
.mcp.json
|
|
14
|
+
|
|
15
|
+
# Benchmark datasets (large, downloaded separately)
|
|
16
|
+
data/*
|
|
17
|
+
# But track the local benchmark definition
|
|
18
|
+
!data/local_benchmark.json
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mag-memory
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PyPI wrapper for the mag MCP memory server (Rust binary)
|
|
5
|
+
Project-URL: Homepage, https://github.com/George-RD/mag
|
|
6
|
+
Project-URL: Repository, https://github.com/George-RD/mag
|
|
7
|
+
Project-URL: Issues, https://github.com/George-RD/mag/issues
|
|
8
|
+
Author: George-RD
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: ai,llm,mcp,memory,semantic-search,sqlite
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Rust
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# mag-memory
|
|
31
|
+
|
|
32
|
+
PyPI wrapper for [mag](https://github.com/George-RD/mag), a Rust-based MCP memory server.
|
|
33
|
+
|
|
34
|
+
mag stores memories in SQLite with ONNX embeddings for semantic search, exposing 16 MCP tools via stdio protocol. No external services required.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install mag-memory
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Start the MCP server
|
|
46
|
+
mag serve
|
|
47
|
+
|
|
48
|
+
# The native binary is downloaded automatically on first run.
|
|
49
|
+
# All CLI arguments are passed through to the Rust binary.
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## How it works
|
|
53
|
+
|
|
54
|
+
This package does not bundle the native binary. On first run, it detects your platform (Linux/macOS/Windows, x86_64/ARM64), downloads the correct prebuilt binary from [GitHub Releases](https://github.com/George-RD/mag/releases), and caches it locally. Subsequent runs use the cached binary with zero overhead (Unix `exec`).
|
|
55
|
+
|
|
56
|
+
## Supported platforms
|
|
57
|
+
|
|
58
|
+
| OS | Architecture |
|
|
59
|
+
|---------|-------------|
|
|
60
|
+
| Linux | x86_64, aarch64 |
|
|
61
|
+
| macOS | x86_64, Apple Silicon (aarch64) |
|
|
62
|
+
| Windows | x86_64 |
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
MIT
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# mag-memory
|
|
2
|
+
|
|
3
|
+
PyPI wrapper for [mag](https://github.com/George-RD/mag), a Rust-based MCP memory server.
|
|
4
|
+
|
|
5
|
+
mag stores memories in SQLite with ONNX embeddings for semantic search, exposing 16 MCP tools via stdio protocol. No external services required.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install mag-memory
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Start the MCP server
|
|
17
|
+
mag serve
|
|
18
|
+
|
|
19
|
+
# The native binary is downloaded automatically on first run.
|
|
20
|
+
# All CLI arguments are passed through to the Rust binary.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## How it works
|
|
24
|
+
|
|
25
|
+
This package does not bundle the native binary. On first run, it detects your platform (Linux/macOS/Windows, x86_64/ARM64), downloads the correct prebuilt binary from [GitHub Releases](https://github.com/George-RD/mag/releases), and caches it locally. Subsequent runs use the cached binary with zero overhead (Unix `exec`).
|
|
26
|
+
|
|
27
|
+
## Supported platforms
|
|
28
|
+
|
|
29
|
+
| OS | Architecture |
|
|
30
|
+
|---------|-------------|
|
|
31
|
+
| Linux | x86_64, aarch64 |
|
|
32
|
+
| macOS | x86_64, Apple Silicon (aarch64) |
|
|
33
|
+
| Windows | x86_64 |
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
MIT
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mag-memory: PyPI wrapper for the mag MCP memory server.
|
|
3
|
+
|
|
4
|
+
On first run, the native binary is downloaded from GitHub Releases
|
|
5
|
+
for the current platform. Subsequent runs use the cached binary.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
# Version of the Rust binary to download (kept in sync with __version__)
|
|
17
|
+
_BINARY_VERSION = "0.1.0"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _binary_dir():
|
|
21
|
+
# type: () -> str
|
|
22
|
+
"""Return the directory where the mag binary is stored."""
|
|
23
|
+
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "bin")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _binary_name():
|
|
27
|
+
# type: () -> str
|
|
28
|
+
"""Return the platform-appropriate binary filename."""
|
|
29
|
+
if sys.platform == "win32":
|
|
30
|
+
return "mag.exe"
|
|
31
|
+
return "mag"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _find_binary():
|
|
35
|
+
# type: () -> str | None
|
|
36
|
+
"""Locate the mag binary: package dir first, then PATH."""
|
|
37
|
+
# 1. Check package bin directory
|
|
38
|
+
packaged = os.path.join(_binary_dir(), _binary_name())
|
|
39
|
+
if os.path.isfile(packaged) and os.access(packaged, os.X_OK):
|
|
40
|
+
return packaged
|
|
41
|
+
|
|
42
|
+
# 2. Fall back to PATH
|
|
43
|
+
found = shutil.which("mag")
|
|
44
|
+
return found
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def main():
|
|
48
|
+
# type: () -> None
|
|
49
|
+
"""Entry point: find (or download) the mag binary and exec it."""
|
|
50
|
+
binary = _find_binary()
|
|
51
|
+
|
|
52
|
+
if binary is None:
|
|
53
|
+
# Download on first run
|
|
54
|
+
sys.stderr.write("mag: binary not found, downloading for this platform...\n")
|
|
55
|
+
try:
|
|
56
|
+
from mag_memory._download import download_binary
|
|
57
|
+
|
|
58
|
+
binary = download_binary(_BINARY_VERSION)
|
|
59
|
+
except Exception as exc:
|
|
60
|
+
sys.stderr.write("mag: failed to download binary: {}\n".format(exc))
|
|
61
|
+
sys.stderr.write(
|
|
62
|
+
"mag: install manually from "
|
|
63
|
+
"https://github.com/George-RD/mag/releases\n"
|
|
64
|
+
)
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
|
|
67
|
+
# Replace this process with the binary (Unix) or subprocess (Windows)
|
|
68
|
+
args = [binary] + sys.argv[1:]
|
|
69
|
+
|
|
70
|
+
if sys.platform != "win32":
|
|
71
|
+
os.execvp(binary, args)
|
|
72
|
+
else:
|
|
73
|
+
# os.execvp is unreliable on Windows; use subprocess instead
|
|
74
|
+
result = subprocess.run(args)
|
|
75
|
+
sys.exit(result.returncode)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Download the correct mag binary for the current platform from GitHub Releases.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
import os
|
|
7
|
+
import platform
|
|
8
|
+
import stat
|
|
9
|
+
import sys
|
|
10
|
+
import tarfile
|
|
11
|
+
import zipfile
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from urllib.request import urlopen, Request
|
|
15
|
+
from urllib.error import URLError, HTTPError
|
|
16
|
+
except ImportError:
|
|
17
|
+
# Python 2 fallback (shouldn't happen with >=3.8, but defensive)
|
|
18
|
+
from urllib2 import urlopen, Request, URLError, HTTPError # type: ignore[no-redef]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
_GITHUB_RELEASE_URL = (
|
|
22
|
+
"https://github.com/George-RD/mag/releases/download/"
|
|
23
|
+
"v{version}/mag-{target}.{ext}"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Mapping: (sys.platform, platform.machine()) -> Rust target triple
|
|
27
|
+
_TARGET_MAP = {
|
|
28
|
+
("linux", "x86_64"): "x86_64-unknown-linux-gnu",
|
|
29
|
+
("linux", "aarch64"): "aarch64-unknown-linux-gnu",
|
|
30
|
+
("linux", "arm64"): "aarch64-unknown-linux-gnu",
|
|
31
|
+
("darwin", "x86_64"): "x86_64-apple-darwin",
|
|
32
|
+
("darwin", "arm64"): "aarch64-apple-darwin",
|
|
33
|
+
("darwin", "aarch64"): "aarch64-apple-darwin",
|
|
34
|
+
("win32", "AMD64"): "x86_64-pc-windows-msvc",
|
|
35
|
+
("win32", "x86_64"): "x86_64-pc-windows-msvc",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _detect_target():
|
|
40
|
+
# type: () -> tuple[str, str]
|
|
41
|
+
"""Detect the Rust target triple and archive extension for this platform.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
(target_triple, archive_extension)
|
|
45
|
+
"""
|
|
46
|
+
plat = sys.platform
|
|
47
|
+
# Normalize platform string
|
|
48
|
+
if plat.startswith("linux"):
|
|
49
|
+
plat = "linux"
|
|
50
|
+
|
|
51
|
+
machine = platform.machine()
|
|
52
|
+
|
|
53
|
+
key = (plat, machine)
|
|
54
|
+
target = _TARGET_MAP.get(key)
|
|
55
|
+
if target is None:
|
|
56
|
+
raise RuntimeError(
|
|
57
|
+
"Unsupported platform: {} / {} (machine={})".format(
|
|
58
|
+
sys.platform, platform.system(), machine
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
ext = "zip" if plat == "win32" else "tar.gz"
|
|
63
|
+
return target, ext
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _download_url(url):
|
|
67
|
+
# type: (str) -> bytes
|
|
68
|
+
"""Download a URL and return its content as bytes."""
|
|
69
|
+
req = Request(url, headers={"User-Agent": "mag-memory-pypi-installer"})
|
|
70
|
+
try:
|
|
71
|
+
resp = urlopen(req, timeout=120)
|
|
72
|
+
return resp.read()
|
|
73
|
+
except HTTPError as exc:
|
|
74
|
+
raise RuntimeError(
|
|
75
|
+
"HTTP {} downloading {}: {}".format(exc.code, url, exc.reason)
|
|
76
|
+
)
|
|
77
|
+
except URLError as exc:
|
|
78
|
+
raise RuntimeError("Failed to download {}: {}".format(url, exc.reason))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _extract_tar_gz(data, dest_dir):
|
|
82
|
+
# type: (bytes, str) -> str
|
|
83
|
+
"""Extract a .tar.gz archive, find the mag binary, place it in dest_dir."""
|
|
84
|
+
binary_name = "mag"
|
|
85
|
+
with tarfile.open(fileobj=io.BytesIO(data), mode="r:gz") as tar:
|
|
86
|
+
# Find the mag binary in the archive
|
|
87
|
+
members = tar.getnames()
|
|
88
|
+
binary_member = None
|
|
89
|
+
for name in members:
|
|
90
|
+
basename = os.path.basename(name)
|
|
91
|
+
if basename == binary_name:
|
|
92
|
+
binary_member = name
|
|
93
|
+
break
|
|
94
|
+
|
|
95
|
+
if binary_member is None:
|
|
96
|
+
raise RuntimeError(
|
|
97
|
+
"Could not find '{}' in archive. Contents: {}".format(
|
|
98
|
+
binary_name, members
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Extract just the binary
|
|
103
|
+
member = tar.getmember(binary_member)
|
|
104
|
+
fileobj = tar.extractfile(member)
|
|
105
|
+
if fileobj is None:
|
|
106
|
+
raise RuntimeError("Could not extract '{}'".format(binary_member))
|
|
107
|
+
|
|
108
|
+
dest_path = os.path.join(dest_dir, binary_name)
|
|
109
|
+
with open(dest_path, "wb") as f:
|
|
110
|
+
f.write(fileobj.read())
|
|
111
|
+
|
|
112
|
+
return dest_path
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _extract_zip(data, dest_dir):
|
|
116
|
+
# type: (bytes, str) -> str
|
|
117
|
+
"""Extract a .zip archive, find the mag binary, place it in dest_dir."""
|
|
118
|
+
binary_name = "mag.exe"
|
|
119
|
+
with zipfile.ZipFile(io.BytesIO(data)) as zf:
|
|
120
|
+
names = zf.namelist()
|
|
121
|
+
binary_member = None
|
|
122
|
+
for name in names:
|
|
123
|
+
basename = os.path.basename(name)
|
|
124
|
+
if basename == binary_name:
|
|
125
|
+
binary_member = name
|
|
126
|
+
break
|
|
127
|
+
|
|
128
|
+
if binary_member is None:
|
|
129
|
+
raise RuntimeError(
|
|
130
|
+
"Could not find '{}' in archive. Contents: {}".format(
|
|
131
|
+
binary_name, names
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
dest_path = os.path.join(dest_dir, binary_name)
|
|
136
|
+
with open(dest_path, "wb") as f:
|
|
137
|
+
f.write(zf.read(binary_member))
|
|
138
|
+
|
|
139
|
+
return dest_path
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def download_binary(version):
|
|
143
|
+
# type: (str) -> str
|
|
144
|
+
"""Download the mag binary for this platform and return its path.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
version: The version string (e.g. "0.1.0")
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Absolute path to the downloaded binary.
|
|
151
|
+
"""
|
|
152
|
+
target, ext = _detect_target()
|
|
153
|
+
url = _GITHUB_RELEASE_URL.format(version=version, target=target, ext=ext)
|
|
154
|
+
|
|
155
|
+
sys.stderr.write("mag: downloading {} ...\n".format(url))
|
|
156
|
+
data = _download_url(url)
|
|
157
|
+
sys.stderr.write(
|
|
158
|
+
"mag: downloaded {:.1f} MB\n".format(len(data) / (1024.0 * 1024.0))
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Ensure destination directory exists
|
|
162
|
+
from mag_memory import _binary_dir
|
|
163
|
+
|
|
164
|
+
dest_dir = _binary_dir()
|
|
165
|
+
os.makedirs(dest_dir, exist_ok=True)
|
|
166
|
+
|
|
167
|
+
# Extract
|
|
168
|
+
if ext == "zip":
|
|
169
|
+
binary_path = _extract_zip(data, dest_dir)
|
|
170
|
+
else:
|
|
171
|
+
binary_path = _extract_tar_gz(data, dest_dir)
|
|
172
|
+
|
|
173
|
+
# Make executable (Unix)
|
|
174
|
+
if sys.platform != "win32":
|
|
175
|
+
st = os.stat(binary_path)
|
|
176
|
+
os.chmod(binary_path, st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
177
|
+
|
|
178
|
+
sys.stderr.write("mag: installed to {}\n".format(binary_path))
|
|
179
|
+
return binary_path
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if __name__ == "__main__":
|
|
183
|
+
# Allow running directly: python -m mag_memory._download [version]
|
|
184
|
+
ver = sys.argv[1] if len(sys.argv) > 1 else "0.1.0"
|
|
185
|
+
path = download_binary(ver)
|
|
186
|
+
print("Downloaded: {}".format(path))
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mag-memory"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "PyPI wrapper for the mag MCP memory server (Rust binary)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
authors = [{ name = "George-RD" }]
|
|
13
|
+
keywords = ["mcp", "memory", "ai", "llm", "semantic-search", "sqlite"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: MacOS",
|
|
20
|
+
"Operating System :: Microsoft :: Windows",
|
|
21
|
+
"Operating System :: POSIX :: Linux",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.8",
|
|
24
|
+
"Programming Language :: Python :: 3.9",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Programming Language :: Rust",
|
|
30
|
+
"Topic :: Software Development :: Libraries",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/George-RD/mag"
|
|
35
|
+
Repository = "https://github.com/George-RD/mag"
|
|
36
|
+
Issues = "https://github.com/George-RD/mag/issues"
|
|
37
|
+
|
|
38
|
+
[project.scripts]
|
|
39
|
+
mag = "mag_memory:main"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.wheel]
|
|
42
|
+
packages = ["mag_memory"]
|