Mneme: a self-hosted catalog of callables (OpenAPI operations, library symbols) and persistent memory for AI agents.
Project description
Mneme
A self-hosted catalog of callables and persistent memory for AI agents.
Mneme is a small, local-first indexer and search service. An LLM agent consults it to find the right HTTP API operation, library symbol, or saved note, and then pulls a minimal slice instead of dumping entire docs into context. Published on PyPI as mneme-server; the CLI is mneme.
The searchable unit is a callable — not "an API" or "a library." That can be:
- an OpenAPI operation (e.g.,
POST /v1/refunds), - a Python library symbol (e.g.,
matplotlib.axes._axes.Axes.plot), - a JavaScript/TypeScript library symbol (e.g.,
axios.create), - a saved note in the agent's persistent memory.
All four live in the same SQLite + FTS5 index and are searchable from one MCP tool: search_callables.
Quickstart
pip install mneme-server
mneme demo # indexes a bundled spec, runs a search
mneme mcp-config --client cursor # prints ready-to-paste MCP config
mneme doctor # environment diagnostics
The default index lives in a per-user directory (XDG-aware), so subsequent commands work without --db.
Want it on real APIs? mneme crawl-seeds examples/seeds.popular.txt ingests GitHub, Stripe, Slack, DigitalOcean, Twilio, and others.
Live demo: build a strategy across two libraries
This is what library indexing looks like end to end. Given yfinance (finance) and matplotlib (charting), an agent asks Mneme three small questions, then assembles a working SMA crossover chart for AAPL.
pip install 'mneme-server[pylib]' yfinance matplotlib
mneme add-pylib yfinance # 472 symbols in ~0.3s
mneme add-pylib matplotlib # 6.6k symbols in ~3s
mneme search-callables "download daily price history" --package yfinance --limit 3
# → yfinance.scrapers.history.PriceHistory.history(period, interval, start, end, ...) -> pd.DataFrame
# yfinance.download(tickers, start, end, auto_adjust, ...) -> pd.DataFrame
# yfinance.tickers.Tickers.download(...)
mneme search-callables "plot two line series on the same axes" --package matplotlib --limit 3
# → matplotlib.axes._axes.Axes.plot(*args, scalex, scaley, data, **kwargs) -> list[Line2D]
# matplotlib.axes._axes.Axes.axvline(x, ymin, ymax, **kwargs) -> Line2D
# matplotlib.axes._axes.Axes.axhline(y, xmin, xmax, **kwargs) -> Line2D
mneme search-callables "shade a region between two values" --package matplotlib --limit 3
# → matplotlib.axes._axes.Axes.fill_between(x, y1, y2=0, where=None, ...) -> PolyCollection
# matplotlib.axes._axes.Axes.fill_betweenx(y, x1, x2=0, where=None, ...) -> PolyCollection
# matplotlib.collections.FillBetweenPolyCollection
Each hit is a compact card with the signature, docstring summary, and a symbol_id the agent can pass to get_library_symbol for the full slice. Using those three hits, an agent can write:
import yfinance as yf
import matplotlib.pyplot as plt
prices = yf.download("AAPL", start="2023-01-01", end="2024-06-01",
auto_adjust=True, progress=False)
close = prices["Close"].squeeze()
sma_fast = close.rolling(20).mean()
sma_slow = close.rolling(50).mean()
long_signal = sma_fast > sma_slow
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(close.index, close, label="AAPL close", color="black")
ax.plot(sma_fast.index, sma_fast, label="SMA(20)", color="tab:blue")
ax.plot(sma_slow.index, sma_slow, label="SMA(50)", color="tab:orange")
ax.fill_between(close.index, close.min(), close.max(),
where=long_signal, alpha=0.08, color="tab:green", label="long")
ax.set_title("AAPL — 20/50 SMA crossover")
ax.legend(loc="upper left")
fig.savefig("aapl_sma.png", dpi=110)
The complete script is in examples/sma_strategy.py. The point: the agent didn't need yfinance's or matplotlib's full docs in its context — just three targeted hits.
Why callable-level search?
An agent rarely needs the entire Stripe, GitHub, or matplotlib API. It needs to find callables that match a task:
POST /repos/{owner}/{repo}/issuesPOST /v1/refundsmatplotlib.axes._axes.Axes.errorbarthe note I saved about our auth flow
Mneme indexes each callable with a compact agent-facing summary, required inputs, auth metadata, response fields (for HTTP), signatures and docstrings (for library symbols), provenance, and a minimal usage slice. Search returns a small list; the agent then pulls only the slice it actually needs.
Architecture
flowchart LR
oas[OpenAPI: URL, file, APIs.guru, crawler] --> normalize_oas
pkg[Python package or local source] --> normalize_py
dts[.d.ts file] --> normalize_ts
normalize_oas[normalize operations] --> sqlite[(SQLite + FTS5)]
normalize_py[normalize Python symbols] --> sqlite
normalize_ts[normalize JS/TS symbols] --> sqlite
notes[agent notes / workspace] --> notesdb[(notes.db)]
sqlite --> httpapi[FastAPI search service]
sqlite --> mcp[Local MCP server]
notesdb --> mcp
httpapi --> agent[Agents / clients]
mcp --> agent
Features
- HTTP / OpenAPI: conservative discovery crawler, direct URL/file ingestion, APIs.guru bulk import, normalized operation cards, auth-aware request preparation, and guarded HTTP execution.
- Python library indexing via
griffe— static analysis, never executes user code. - JS/TS library indexing via
tree-sitter-typescriptparsing of.d.tsfiles — no Node runtime required. - Unified search across HTTP operations and library symbols (
search_callables). - Agent memory: FTS5-backed notebook + opt-in scoped file workspace, stored in a separate
notes.db. - Local MCP server with auth profile redaction and dry-run-by-default HTTP execution.
- Self-hostable: SQLite-only, no Postgres or vector DB required. Docker Compose, systemd, cron, and GitHub Actions examples included.
Install
From PyPI:
pip install mneme-server # base
pip install 'mneme-server[mcp]' # with MCP server
pip install 'mneme-server[libraries]' # with Python + JS/TS indexers
pip install 'mneme-server[mcp,libraries]' # everything
From source:
git clone https://github.com/Joshwani/mneme.git
cd mneme
python -m venv .venv
source .venv/bin/activate
python -m pip install -e '.[dev,mcp,libraries]'
CLI cheatsheet
# one-command demo
mneme demo
# index sources
mneme add-file examples/specs/todo.yaml
mneme add-spec https://example.com/openapi.yaml
mneme discover example.com --ingest
mneme crawl-seeds examples/seeds.popular.txt
mneme ingest-apis-guru --limit 25
# library indexing
mneme add-pylib httpx # an installed package
mneme add-pylib mymod --source-dir ./src # a local source tree
mneme add-jslib --package axios --file ./axios.d.ts
mneme list-libraries
# unified search
mneme search "create a todo with a due date" # HTTP-only shortcut
mneme search-callables "create a request" # all kinds
mneme search-callables "DataFrame" --language python # Python lib only
mneme search-callables "post" --package httpx # one package
mneme search-callables "create" --kind pylib_symbol --kind jslib_symbol
# memory: notebook
mneme notes-add --title "T" --body "B" --tag x
mneme notes-search "query"
mneme notes-list
# memory: scoped file workspace (must enable first)
mneme workspace-enable --scope notes --max-mb 10
mneme workspace-write --scope notes --path a.md --content "..."
mneme workspace-ls --scope notes
# inspect
mneme stats
mneme doctor
# serve / run as MCP
mneme serve --host 127.0.0.1 --port 8080
mneme mcp-server
mneme mcp-config --client cursor
All commands accept --db <path> to override the per-user default.
Library indexing in depth
Library symbols (functions, classes, methods, interfaces, type aliases, enums) live in the same SQLite + FTS5 index as HTTP operations. They surface in search_callables with kind="pylib_symbol" or kind="jslib_symbol".
Python via griffe
pip install 'mneme-server[pylib]'
mneme add-pylib httpx # an installed package
mneme add-pylib mymod --source-dir ./src # a local source tree
mneme search-callables "create a request" --language python
Notes:
- Static analysis only — Mneme never imports or executes user code.
- The package must be installed in the current Python environment, or
--source-dirmust be provided. - Private names (
_foo,_Bar) and dunder names (__init__,__repr__) are skipped. - Class re-exports are deduplicated by canonical path so methods appear once.
JavaScript / TypeScript via .d.ts
pip install 'mneme-server[jslib]'
mneme add-jslib --package axios --file ./node_modules/axios/index.d.ts
mneme add-jslib --package @types/node --file ./node_modules/@types/node/fs.d.ts
mneme search-callables "axios.create"
Notes:
tree-sitter-typescriptparses.d.tsfiles; no Node/npm required at runtime.- Currently you supply a local
.d.tsfile (pulling tarballs from the npm registry is on the roadmap). - Captured kinds:
function,class(+ itsmethods),interface,type,enum. - JSDoc comments immediately preceding a declaration become the docstring.
Memory: notebook + workspace
Two memory primitives, both stored in a separate notes.db so you can back up, sync, or wipe agent memory without touching the API catalog.
Notebook
A persistent, FTS5-searchable scratch pad — design decisions, gotchas, "here's the call that works."
mneme notes-add --title "Stripe refund flow" \
--body "POST /v1/refunds needs payment_id, amount. 25h refund window." \
--tag stripe --tag payments
mneme notes-search refund
mneme notes-list --scope finops
mneme notes-get note_<id>
mneme notes-update note_<id> --body "New body"
mneme notes-delete note_<id>
Notes have optional tags, an optional free-text scope (project name or task ID), and microsecond-resolution timestamps for stable ordering.
Scoped file workspace (off by default)
A small, opt-in directory the agent can read and write within. The workspace is OFF until you explicitly enable a scope, and the agent cannot create new scopes via MCP.
mneme workspace-enable --scope notes --max-mb 10
mneme workspace-write --scope notes --path daily.md --content "## 2026-05-24"
mneme workspace-ls --scope notes
mneme workspace-read --scope notes --path daily.md
mneme workspace-rm --scope notes --path daily.md
mneme workspace-disable --scope notes # keeps files on disk
mneme workspace-disable --scope notes --remove-files
Safety invariants:
- scopes must match
[a-zA-Z0-9_][a-zA-Z0-9_.\-]{0,63}; - paths cannot escape the scope directory (
..segments and symlinks are rejected); - per-file size limit (1 MiB default);
- per-scope quota (10 MiB default, configurable via
--max-mb); - the agent cannot enable or disable scopes through MCP — only the human operator can.
Local MCP server
Mneme runs as a local MCP server. API credentials and notes stay on the user's machine.
python -m pip install -e '.[mcp]'
mneme mcp-server # stdio (default)
mneme mcp-server --transport streamable-http
Tools exposed:
# Unified callable search (HTTP + library symbols)
search_callables # unified search; restrict via kinds=[...]
get_library_symbol # full symbol card for a symbol_id
list_libraries # list indexed library packages
# OpenAPI / HTTP
index_openapi_url # fetch and index one remote OpenAPI spec URL
search_operations # HTTP-only operation search
get_operation # full normalized operation card
get_spec_slice # minimal OpenAPI-style operation slice
get_call_template # non-executing request template
list_local_auth_profiles # local auth profiles without secrets
prepare_http_call # redacted prepared request, no network traffic
execute_http_call # dry-run by default; real calls require confirm=true
mneme_stats # local index stats
# Memory: notebook
notes_search # full-text search the agent's notebook
notes_get # fetch a note by ID
notes_list # list recent notes, optionally by scope/tag
notes_add # add a new note
notes_update # update an existing note
notes_delete # delete a note
# Memory: scoped file workspace (off by default)
workspace_status # list enabled scopes and current usage
workspace_ls # list files in a scope
workspace_read # read a file in a scope
workspace_write # write a file in a scope (must be enabled first)
workspace_rm # remove a file in a scope
Print a paste-ready config for your client:
mneme mcp-config --client cursor # ~/.cursor/mcp.json or repo .cursor/mcp.json
mneme mcp-config --client claude # Claude Desktop
mneme mcp-config --client continue # Continue.dev
mneme mcp-config --client generic # bare mcpServers snippet
mneme mcp-config --client cursor --auth-config ~/.config/mneme/auth.json
Local auth profiles
Auth profiles are JSON files mapping a friendly profile name to credentials stored in environment variables. Agents see profile names and redacted previews, never raw secrets.
Default path: ~/.config/mneme/auth.json.
Example:
{
"profiles": {
"github": {
"provider_domain": "api.github.com",
"base_url": "https://api.github.com",
"allow_methods": ["GET", "POST", "PATCH"],
"auth": {
"type": "bearer",
"token_env": "GITHUB_TOKEN"
}
}
}
}
Prepare a call without sending it:
mneme prepare-call op_... \
--auth-config ~/.config/mneme/auth.json \
--auth-profile github \
--json-body '{"title":"ship mcp"}'
Execute a real call only when explicitly confirmed:
mneme execute-call op_... \
--auth-config ~/.config/mneme/auth.json \
--auth-profile github \
--json-body '{"title":"ship mcp"}' \
--send --confirm
Guardrails:
- dry-run is the default for the MCP and CLI executor;
- real HTTP execution requires
confirm=true/--confirm; - mutating unauthenticated calls are blocked;
- auth profiles can restrict allowed HTTP methods and hosts;
MNEME_HTTP_ALLOW_HOSTScan globally restrict execution hosts.
HTTP Search API
POST /search
{
"query": "create a refund for a previous payment",
"limit": 10,
"filters": {
"method": "POST",
"provider_domain": null,
"auth_required": null
},
"token_budget": 4000
}
Other endpoints:
GET /health
GET /stats
GET /search?q=create%20todo
GET /operations/{operation_id}
GET /operations/{operation_id}/spec-slice
GET /operations/{operation_id}/call-template
POST /operations/{operation_id}/prepare-call
POST /operations/{operation_id}/execute-call
Database location
Mneme picks sensible default paths so commands work without --db:
| Use | Resolution order |
|---|---|
| OpenAPI/library index | $MNEME_DB → $XDG_DATA_HOME/mneme/mneme.db → ~/.local/share/mneme/mneme.db → %LOCALAPPDATA%\mneme\mneme.db |
| Notes index | $MNEME_NOTES_DB → $XDG_DATA_HOME/mneme/notes.db → ~/.local/share/mneme/notes.db → %LOCALAPPDATA%\mneme\notes.db |
| Workspace root | $MNEME_WORKSPACE_ROOT → $XDG_DATA_HOME/mneme/workspace/ → ~/.local/share/mneme/workspace/ → %LOCALAPPDATA%\mneme\workspace\ |
Override the index path with --db /path/to/mneme.db and the notes DB with --notes-db /path/to/notes.db on memory subcommands.
Self-host with Docker Compose
cd deploy
docker compose up --build -d
Ingest the demo spec into the Docker volume:
docker compose run --rm mneme \
mneme --db /data/mneme.db add-file /examples/specs/todo.yaml
curl -s -X POST http://127.0.0.1:8080/search \
-H 'content-type: application/json' \
-d '{"query":"create a todo","limit":3}' | jq
Pre-built container images are published to GHCR on tagged releases:
docker pull ghcr.io/joshwani/mneme:latest
Self-host crawler deployment
The recommended deployment model is bring-your-own-infra:
- Run the API container or systemd service.
- Store the SQLite index on a persistent volume.
- Keep a curated
seeds.txtof domains and spec URLs. - Run
mneme crawl-seedsfrom cron or another scheduler. - Back up the SQLite file like any other application data.
Example cron entry:
10 2 * * * cd /opt/mneme && /opt/mneme/.venv/bin/mneme --db /var/lib/mneme/mneme.db crawl-seeds /etc/mneme/seeds.txt >> /var/log/mneme-crawl.log 2>&1
Troubleshooting
Run mneme doctor first. It prints the resolved DB path, index size, installed extras, and a network reachability check. Most reports should include its output.
Common issues:
- Empty search results. The index is empty. Run
mneme demo,mneme crawl-seeds examples/seeds.popular.txt, ormneme add-pylib <pkg>. - MCP server fails to start with an ImportError. The optional MCP extra isn't installed. Run
pip install 'mneme-server[mcp]'. mneme mcp-config --client cursorshowscommand: mnemeinstead of an absolute path. Themnemebinary isn't onPATHin the shell that launches your MCP client. Activate the venv first, or editcommandto the absolute path printed bywhich mneme.- 401/403 when executing a call. Check
mneme auth-profiles --auth-config ~/.config/mneme/auth.jsonand confirm the referenced*_envenvironment variable is set in the launching shell. - "host not allowed" errors when executing. Either widen
allow_methods/allowed_hostsin your profile, or relaxMNEME_HTTP_ALLOW_HOSTS.
When to move beyond SQLite
SQLite + FTS5 is enough for the MVP and for private/team indexes. Move to a larger architecture when you need concurrent crawler workers, millions of callables, vector retrieval, public multi-tenant search, owner verification flows, or moderation/takedown flows.
A later hosted architecture could use object storage for raw specs, Postgres for metadata, Tantivy / Meilisearch / OpenSearch for lexical search, pgvector / Qdrant / LanceDB for embeddings, a queue for crawler jobs, and a read-only search API for agents.
Crawl policy
The MVP is intentionally conservative. It should index intentionally published API descriptions, not private or accidentally exposed internal specs. Recommended rules for operators:
- crawl only submitted domains, submitted URLs, known public directories, and API discovery endpoints;
- respect rate limits and robots/policy pages where applicable;
- do not index specs requiring authentication;
- store provenance for every spec;
- provide opt-out or takedown instructions if operating a public index.
Development
python -m pip install -e '.[dev,mcp,libraries]'
ruff check
ruff format --check
pytest
See CONTRIBUTING.md for details and CHANGELOG.md for release notes.
Roadmap
- Library indexing from npm tarballs (right now you supply a local
.d.ts). - Library indexing from PyPI sdists/wheels (right now the package must be installed or have a local source).
- Hybrid retrieval (BM25 + embeddings) for callable search.
- Owner-verified submissions via DNS TXT or GitHub repo verification.
- Better RFC 9727 Linkset parsing.
- Duplicate clustering by callable similarity.
- Reranking with task/callable labels.
- Callable graph edges for multi-step workflows.
- Public benchmark: natural-language task → expected callable IDs.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mneme_server-0.3.0.tar.gz.
File metadata
- Download URL: mneme_server-0.3.0.tar.gz
- Upload date:
- Size: 79.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4cd105d14f40ed9393c7b128fcd9d462ab2c7a2394a319022dea645f10122a4
|
|
| MD5 |
faad2863553a7ce50c742ebc82591fab
|
|
| BLAKE2b-256 |
f1787a7d0bf30f83b95609ef7648494e8fdd04f2a463f19fc9c4f54b6268250c
|
Provenance
The following attestation bundles were made for mneme_server-0.3.0.tar.gz:
Publisher:
release.yml on Joshwani/mneme
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mneme_server-0.3.0.tar.gz -
Subject digest:
e4cd105d14f40ed9393c7b128fcd9d462ab2c7a2394a319022dea645f10122a4 - Sigstore transparency entry: 1650285229
- Sigstore integration time:
-
Permalink:
Joshwani/mneme@8331a62514ca0543e63f374f61a0d967abc69478 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Joshwani
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8331a62514ca0543e63f374f61a0d967abc69478 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mneme_server-0.3.0-py3-none-any.whl.
File metadata
- Download URL: mneme_server-0.3.0-py3-none-any.whl
- Upload date:
- Size: 76.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c100f18f4ea65f661a3d5a5ec2dede7c4c2d8f7a67bb2728ae4f8fd462f687b
|
|
| MD5 |
c0d4ecbc459be61ceaae5df0a2ed98ba
|
|
| BLAKE2b-256 |
822de722f32da83d836d8ece2493d54f8c816737c51ec5ec8cf58cbaaea01de9
|
Provenance
The following attestation bundles were made for mneme_server-0.3.0-py3-none-any.whl:
Publisher:
release.yml on Joshwani/mneme
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mneme_server-0.3.0-py3-none-any.whl -
Subject digest:
2c100f18f4ea65f661a3d5a5ec2dede7c4c2d8f7a67bb2728ae4f8fd462f687b - Sigstore transparency entry: 1650285270
- Sigstore integration time:
-
Permalink:
Joshwani/mneme@8331a62514ca0543e63f374f61a0d967abc69478 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Joshwani
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8331a62514ca0543e63f374f61a0d967abc69478 -
Trigger Event:
push
-
Statement type: