Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
run: |
python -m venv /tmp/clean
/tmp/clean/bin/pip install dist/*.whl
for cmd in sgm-index sgm-ask sgm-mcp sgm-turnstate-hook sgm-turnstate-show; do
for cmd in sgm-index sgm-ask sgm-mcp sgm-turnstate-hook sgm-turnstate-show sqlite-graph-memory; do
test -x "/tmp/clean/bin/$cmd" || { echo "::error::$cmd missing from wheel"; exit 1; }
done
- uses: actions/upload-artifact@v4
Expand All @@ -66,3 +66,37 @@ jobs:
- uses: pypa/gh-action-pypi-publish@release/v1
with:
skip-existing: true # the package may already be uploaded before the GitHub release object exists

# List the server in the official MCP Registry once PyPI has the release.
# Auth is GitHub OIDC (no stored secret): the registry grants the
# io.github.tonydzi/* namespace to workflows running in tonydzi's repos.
# Ownership is proven by the `mcp-name:` line in README.md, which PyPI shows
# as the package description, so this job must run AFTER the PyPI upload.
mcp-registry:
needs: publish
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: server.json must describe the version being released
run: |
pkg=$(python3 -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
srv=$(python3 -c "import json;d=json.load(open('server.json'));p=d['packages'][0];print(d['version'], p['version'], p['runtimeArguments'][0]['value'])")
[ "$srv" = "$pkg $pkg sqlite-graph-memory[embeddings]==$pkg" ] || { echo "::error::server.json says '$srv', pyproject says $pkg"; exit 1; }
grep -q "mcp-name: $(python3 -c "import json;print(json.load(open('server.json'))['name'])")" README.md \
|| { echo "::error::README.md lacks the mcp-name line the registry verifies"; exit 1; }
- name: Wait until PyPI serves this version
run: |
v=$(python3 -c "import json;print(json.load(open('server.json'))['version'])")
for i in $(seq 1 30); do
curl -sf "https://pypi.org/pypi/sqlite-graph-memory/$v/json" >/dev/null && exit 0
sleep 20
done
echo "::error::PyPI never served $v"; exit 1
- name: Install mcp-publisher
run: |
curl -sL "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz" | tar xz mcp-publisher
- run: ./mcp-publisher login github-oidc
- run: ./mcp-publisher publish
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
/tmp/clean/bin/pip install dist/*.whl
- name: every advertised command must start
run: |
for cmd in sgm-index sgm-ask sgm-turnstate-hook sgm-turnstate-show; do
for cmd in sgm-index sgm-ask sgm-mcp sgm-turnstate-hook sgm-turnstate-show sqlite-graph-memory; do
test -x "/tmp/clean/bin/$cmd" || { echo "::error::$cmd was not installed"; exit 1; }
done
/tmp/clean/bin/sgm-turnstate-show --help > /dev/null
Expand Down
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,31 @@ What shipped, in plain words. Entries below v0.1.3 were written on 2026-09-05 fr
release notes that already existed — the file itself did not exist until then, so those three are
recorded after the fact rather than backdated to look contemporaneous.

## Unreleased
## v0.3.1 — unreleased

**Credit where it is due.** The MCP server that ships as `sgm-mcp` was written by Vardhman Gupta
([@Kaap10](https://github.com/Kaap10)) in PR #8: the server, its tests and its docs. His
follow-up fixes are the shared answer-file race (commit 3ce7061), the stdout fallback (PR #10)
and the swallowed read errors (PR #14).
He is now listed in the `authors` field of `pyproject.toml`, which is the attribution he asked for.

**Indexing works on Intel Macs again.** A fresh `pip install 'sqlite-graph-memory[embeddings]'`
on an x86_64 Mac with Python 3.12 crashed `sgm-index` with `NameError: name 'nn' is not
defined`. Torch stops at 2.2 there, transformers 5.x needs 2.6 and quietly disables torch,
and sentence-transformers 6.x requires transformers 5.x. The embeddings extra now caps
transformers below 4.50 and sentence-transformers below 5 on that one platform. Measured
both ways on the same machine: 0.3.0 from PyPI crashes, this build indexes the 202-note
bench vault cleanly (204 chunks).

**Listed in the official MCP Registry.** `server.json` describes the server as
`io.github.tonydzi/sqlite-graph-memory`, the README carries the `mcp-name` line the registry
checks, and the publish workflow lists the release after PyPI has it. The package also
installs the MCP server under its own name, `sqlite-graph-memory`, so registry clients can
start it with `uvx`.

**The README opens with three commands and their real output** on the bench vault, including
the honest caveat that vector-only ranks ahead of graph mode on that vault overall.

## bench-v0.1 — 2026-10-09

**A benchmark other people can run.** Until now every retrieval number in this repo came from
Expand Down
53 changes: 50 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,60 @@
# sqlite-graph-memory

<!-- mcp-name: io.github.tonydzi/sqlite-graph-memory -->

**Your agent forgets everything between sessions, and plain vector search gives it back
flat, unrelated chunks. This gives it associative recall over your own markdown notes —
one SQLite file, no graph database, no ETL pass, no server.**
flat, unrelated chunks. This gives it associative recall over your own markdown notes:
vector search finds the entry points, the `[[wikilinks]]` you already wrote are the graph.
One SQLite file, no graph database, no ETL pass, no server.**

## Three commands

```bash
pip install sqlite-graph-memory
pip install 'sqlite-graph-memory[embeddings]'
sgm-index ./notes
sgm-ask --ab "In which programming language is the tool behind Project Ouzel written?"
```

`./notes` is any folder of markdown notes. To get exactly the output below, use the
202-note synthetic vault from this repo (it is not inside the pip package):

```bash
git clone --depth 1 https://github.com/tonydzi/sqlite-graph-memory && cp -R sqlite-graph-memory/bench/vault ./notes
```

Real output, top 5 of 12:

```
--- DIRECT memory (vector) ---
1. [ 1.66] project-ouzel
2. [ -1.33] project-avocet
3. [ -1.79] tool-cobbleset
4. [ -2.44] meeting-2025-10-21-lighthouse-sync
5. [ -2.70] person-severin-ibarrondo

--- ASSOCIATIVE memory (vector+graph) ---
1. [ 1.66] project-ouzel
2. [ 1.14] tool-lanternfish <== +graph
3. [ -1.33] project-avocet
4. [ -1.79] tool-cobbleset
5. [ -2.44] meeting-2025-10-21-lighthouse-sync
```

The answer lives in `tool-lanternfish` ("Lanternfish is written in Elixir"). The question
never names Lanternfish, so the vector pass does not find it in the top 12 at all.
`project-ouzel` links to it, so one graph hop puts it second.

That is the idea in one query, and it is not a blanket win. On this same vault vector-only
ranks ahead of graph mode overall; the numbers are in [`bench/`](bench/README.md). `--ab`
exists so you can measure which one wins on your own notes.

**From any MCP client** (Claude Desktop, Cursor, Zed, Windsurf): `sgm-mcp` after the install
above, or `uvx --with 'sqlite-graph-memory[embeddings]' sqlite-graph-memory`. Set
`BRAIN_INDEX_DIR` to the `index` folder `sgm-index` wrote. Config:
[`examples/mcp-config.json`](examples/mcp-config.json).

## What it is

Graph RAG on SQLite for AI agents — a working pilot, not a framework. It is the extracted
memory layer of a personal "second brain" agent setup: vector search finds the entry
points, the `[[wikilinks]]` you already wrote by hand are the graph, and a cross-encoder
Expand Down
12 changes: 11 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "sqlite-graph-memory"
version = "0.3.0"
version = "0.3.1"
Comment thread
cursor[bot] marked this conversation as resolved.
description = "Associative memory for LLM agents: Graph RAG over a folder of markdown notes, in one SQLite file, with no server to run."
readme = "README.md"
requires-python = ">=3.9"
Expand Down Expand Up @@ -47,9 +47,16 @@ dependencies = ["numpy>=1.24"]
# Intel Macs top out at torch 2.2.x, which is built against NumPy 1.x and dies on
# NumPy 2 with "RuntimeError: Numpy is not available" (measured 2026-10-15 on an
# x86_64 Mac, Python 3.9: sgm-index crashed; with numpy<2 it indexed cleanly).
# The same ceiling bites one layer up: transformers 5.x needs torch >= 2.6, so on
# torch 2.2 it disables torch and sgm-index dies with "NameError: name 'nn' is not
# defined" inside transformers/integrations/accelerate.py (measured 2026-10-15 on
# an x86_64 Mac, Python 3.12, fresh `pip install` from PyPI). sentence-transformers
# 6.x in turn requires transformers 5.x, so both are capped there.
embeddings = [
"sentence-transformers>=2.6",
"numpy<2; sys_platform == 'darwin' and platform_machine == 'x86_64'",
"transformers<4.50; sys_platform == 'darwin' and platform_machine == 'x86_64'",
"sentence-transformers<5; sys_platform == 'darwin' and platform_machine == 'x86_64'",
]

[project.urls]
Expand All @@ -64,6 +71,9 @@ sgm-ask = "sqlite_graph_memory.brain_ask:main"
sgm-turnstate-hook = "sqlite_graph_memory.turnstate_hook:main"
sgm-turnstate-show = "sqlite_graph_memory.turnstate_show:main"
sgm-mcp = "sqlite_graph_memory.mcp_server:main"
# Same server under the package's own name, so `uvx sqlite-graph-memory` (what MCP
# registry clients run for a PyPI package) starts it without a --from flag.
sqlite-graph-memory = "sqlite_graph_memory.mcp_server:main"

[tool.hatch.build.targets.wheel]
packages = ["src/sqlite_graph_memory"]
Expand Down
45 changes: 45 additions & 0 deletions server.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "io.github.tonydzi/sqlite-graph-memory",
"title": "sqlite-graph-memory",
"description": "Recall over your markdown notes: vector search plus your [[wikilinks]] as a graph, in SQLite.",
"repository": {
"url": "https://github.com/tonydzi/sqlite-graph-memory",
"source": "github"
},
"version": "0.3.1",
"packages": [
{
"registryType": "pypi",
"registryBaseUrl": "https://pypi.org",
"identifier": "sqlite-graph-memory",
"version": "0.3.1",
"runtimeHint": "uvx",
"runtimeArguments": [
{
"type": "named",
"name": "--with",
"value": "sqlite-graph-memory[embeddings]==0.3.1",
"description": "Recall needs the embedding and reranker models; the base package leaves them out to stay small."
}
],
"transport": {
"type": "stdio"
},
"environmentVariables": [
{
"name": "BRAIN_INDEX_DIR",
"description": "Directory holding the index. Build it once with `sgm-index /path/to/notes`.",
"format": "filepath",
"isRequired": true
},
{
"name": "TURNSTATE_DB",
"description": "SQLite file for recall telemetry.",
"format": "filepath",
"default": "./turnstate.db"
}
]
}
]
}
2 changes: 1 addition & 1 deletion src/sqlite_graph_memory/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
on an ``ImportError`` at startup.
"""

__version__ = "0.3.0"
__version__ = "0.3.1"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the MCP handshake version in sync

When an MCP client initializes the newly released 0.3.1 server, SERVER_INFO in src/sqlite_graph_memory/mcp_server.py still returns "version": "0.3.0". This makes the running artifact disagree with both the package metadata and the new registry manifest, producing incorrect version diagnostics for every MCP client; update that constant as part of this version bump or derive it from the package version.

Useful? React with 👍 / 👎.

Comment thread
cursor[bot] marked this conversation as resolved.

__all__ = ["__version__"]
4 changes: 3 additions & 1 deletion src/sqlite_graph_memory/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import sys, os, json, subprocess, tempfile
from pathlib import Path

from sqlite_graph_memory import __version__

# Ensure UTF-8 for stdio communication on all platforms (including Windows console)
try:
sys.stdin.reconfigure(encoding='utf-8')
Expand All @@ -43,7 +45,7 @@ def _brain_ask_cmd():

SERVER_INFO = {
"name": "sqlite-graph-memory",
"version": "0.3.0"
"version": __version__
}

TOOLS = [
Expand Down
5 changes: 5 additions & 0 deletions tests/test_mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@



def test_server_info_version_tracks_package():
from sqlite_graph_memory import __version__
assert SERVER_INFO["version"] == __version__


def test_mcp_initialize():
req = {
"jsonrpc": "2.0",
Expand Down
Loading