Skip to main content

aleth-mcp

The MCP face for the memory engine: the ten memory verbs, remember, recall, show, search, retract, forget, supersede, connect, status, nodes: exposed as MCP tools, as a thin shell over the aleth-client wire core.

Three of those correct a belief and they are NOT interchangeable: retract says it was wrong, supersede says it was true and something newer replaced it, forget destroys it.

Any MCP-speaking agent gets persistent, associative, erasable memory with one config entry:

{
  "mcpServers": {
    "memory": { "command": "aleth-mcp" }
  }
}

What it is (and is not)

  • Thin. Every tool is one client call; results are the frozen wire models as plain dicts. No state of its own, no extra verbs.
  • Lazy. The server starts and lists tools even before the engine is up; the first call connects. Connection problems come back in-band ({"error": ...}), so the agent sees them instead of a dead server.
  • Engine-agnostic on the wire. It speaks the frozen memory wire, so it works against the standalone memory server and the full daemon alike -- set MEMORY_DISCOVERY_DIR if the engine's discovery file lives somewhere non-default.
  • forget really forgets. Permanent erasure (graph + storage + vacuum), distinct from retract (kept but flagged). That split is the product's thesis, not a convenience.

Install / run

pip install aleth-mcp
aleth-mcp          # stdio MCP server (bare invocation -- how hosts launch it)

Register with an AI host

The install subcommand writes the server entry into a host's own config file (merge-only, backed up, idempotent). The bare aleth-mcp invocation is unchanged, only the install / uninstall / hosts subcommands trigger the writer.

Start the engine first, and run install from the folder you started it in. The engine keeps its memory under the folder aleth start runs in, and a host launches this server from a folder of its own. So install finds the running engine from where you are and writes its location into the host's entry (ALETH_DISCOVERY_DIR); every host you install that way shares the one engine. Started it with --data-dir? Pass the same --data-dir to install.

aleth-mcp hosts                 # list hosts + which are detected here
aleth-mcp install claude-code   # add "memory" to ./.mcp.json
aleth-mcp install --all         # install into every detected host
aleth-mcp install cursor --project --instructions   # config + rules file
aleth-mcp install codex --dry-run                   # show the diff, write nothing
aleth-mcp uninstall claude-code                     # remove exactly our entry

Flags: --all (detected hosts only), --project / --user (scope, where the host supports both), --dry-run (unified diff, no write), --instructions (also inject the memory instruction block), --hooks (also wire automatic capture, see below).

Behaviour: existing config is parsed and only our memory entry is added/replaced (unknown keys preserved); a timestamped .bak is written next to the file before every change; re-running with identical content writes nothing. JSON hosts are reserialized (2-space indent; all other keys survive). TOML and comment-bearing files are edited by surgical text splice. If a JSONC-capable file (VS Code, Zed) actually contains comments, the writer fails soft: it refuses to touch the file and prints the exact snippet to paste, rather than dropping your comments.

What gets written where

Host Config file (default scope) Container key Source
claude-code ./.mcp.json (project) or ~/.claude.json (--user) mcpServers own-knowledge
claude-desktop %APPDATA%\Claude\claude_desktop_config.json / ~/Library/Application Support/Claude/... / ~/.config/Claude/... mcpServers own-knowledge
cursor ~/.cursor/mcp.json (global) or ./.cursor/mcp.json (--project) mcpServers own-knowledge
codex ~/.codex/config.toml [mcp_servers.memory] (TOML) OpenAI docs
vscode ./.vscode/mcp.json servers (note: not mcpServers; stdio type required) VS Code docs
gemini-cli ~/.gemini/settings.json mcpServers own-knowledge
windsurf ~/.codeium/windsurf/mcp_config.json mcpServers own-knowledge
zed ~/.config/zed/settings.json / %APPDATA%\Zed\settings.json context_servers ({command:{...}} shape) Zed docs
cline <VS Code User>/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json mcpServers own-knowledge + cline/cline

Instruction-line injection (--instructions) writes a short, marker-fenced memory usage block into the host's instruction file where one exists: CLAUDE.md (claude-code), AGENTS.md (codex), .cursor/rules/memory.mdc (cursor), GEMINI.md (gemini-cli).

Automatic capture

An agent that has to remember to call remember will sometimes not, and what it did not store is gone. Capture closes that gap at the host, not in the engine: the host runs a command at each lifecycle event, and four of those events are wired to aleth-mcp capture.

Lifecycle moment Command What it does
the user sends a prompt aleth-mcp capture prompt spools what the user said, verbatim
a tool finishes aleth-mcp capture tool spools one line per action: the tool and the argument that names it, never the tool's output
the turn ends aleth-mcp capture stop stores the user's words (origin user) and the agent's record (its final message plus the action list, origin agent), links the two, and links the turn to the previous one
the session ends aleth-mcp capture session-end a bounded last attempt for anything still spooled

Wiring it into a host

aleth-mcp capture hook is the same thing behind one name. It reads the event out of the payload and routes it, so a host config names one command four times instead of four different ones. --hooks writes that wiring for you:

aleth-mcp install claude-code --hooks    # ./.claude/settings.json
aleth-mcp install codex --hooks          # ~/.codex/hooks.json
aleth-mcp install gemini-cli --hooks     # ~/.gemini/settings.json
aleth-mcp install cursor --hooks         # ./.cursor/hooks.json
aleth-mcp install --all --hooks          # every host detected here
aleth-mcp uninstall cursor --hooks       # remove exactly our hooks

Same discipline as the server entry: merge only, timestamped .bak, idempotent, your other hooks and settings untouched, and a file with comments in it is left alone with the snippet printed for you to paste. --project / --user pick the scope; the default is the one that makes sense per host, so Cursor's hooks go in the repo while Codex's go in your home directory. Ship a project's .cursor/ or .codex/ hooks file in version control and everyone working in that repo gets capture.

The Claude Code plugin (packaging/claude-code-plugin/) and the Gemini CLI extension (packaging/gemini-cli-extension/) already carry their hooks, so installing either of those needs no --hooks at all.

What the record is, and is not:

  • It is the record. Nothing is summarised or rewritten by a model. The user's words are stored as said; the agent's record is its own final message and the list of what it ran. Recall gets what happened, not a reading of it.
  • It is selective only where selection is mechanical. A go-signal ("ok", "continue"), a slash command or a host-injected payload is not stored as the user's words; the actions it triggered still are. The agent's own memory_* calls are not recorded as actions, because they are writes already. Long texts are clipped, never dropped.
  • It never costs a turn. Every path exits 0 and reports a failure as one line on stderr. With the engine down, the turn waits in a local spool and is stored at the next stop, of this session or any other; a window that dies mid-turn leaves a spool that the next stop anywhere sweeps up.
  • Off is one variable. ALETH_CAPTURE=0 in the shell the host runs in. The spool lives under ALETH_CAPTURE_DIR, else Claude Code's per-plugin data directory, else ~/.aleth/capture. The engine's own settings are untouched.
  • Forgetting still works. Something said in passing is in the store; memory_forget erases it for real, as it always did.
  • Nothing goes to stdout. Some hosts parse a hook's stdout, so every word the capture has to say goes to stderr and every path exits 0. A hook never costs you a turn.

The distilled fact, the correction (supersede / retract) and the erasure stay the agent's job; the plugin's skill and the extension's context file say so at every session start.

Claude Code's payload was measured on a real run. Codex, Gemini CLI and Cursor are wired from their published references and have not been observed on a live install; if one of them renames a field the record gets shorter, never broken.

The remote door (optional)

Everything above is stdio: a pipe, on your machine, started by a host you installed. A web app that takes a remote MCP connector (ChatGPT Developer Mode, Claude.ai connectors) cannot speak down a pipe, so there is a second mode.

pip install "aleth-mcp[door]"
aleth-mcp serve-http --public-url https://<your tunnel hostname>

MCP over HTTPS with OAuth 2.1: authorization code with PKCE for browsers, device code for terminals, dynamic client registration so a connector registers itself. Read-only by default; write is a separate button on a consent page served from your own machine, and the scope is enforced at the tool boundary: a read-scoped token calling memory_remember gets an MCP error naming the missing scope.

The door is yours, and so is the key. The authorization server is this same process, the certificate is generated on your disk on first run, and the tunnel is one you run (Cloudflare Tunnel, Tailscale, ngrok, your own reverse proxy). We operate nothing in the path and hold nothing, which is why "we can't" survives the move to the web.

It is off until you type the command. No config setting, no environment variable and no hook reaches it; aleth-mcp install writes only the stdio entry, and the stdio path is unchanged by any of this. Stop the command and every token it issued is gone.

Recipes for each tunnel, the scope-per-verb table, the flags, and a plain list of what this door does not protect against: DOOR.md.

Testing

tests/test_install.py drives the host-config writers against real temp dirs (HOME / APPDATA redirected to tmp_path, no mocked file IO, no test touches a real config dir). The handler layer is exercised end-to-end against a real in-process engine + RPC server + wire client, no mocks, the stdio transport the only layer not under test.

tests/test_door.py starts the remote door on loopback with a generated certificate and drives it over a real socket: the metadata documents, dynamic client registration, the PKCE code flow and the device flow end to end, an expired token, a token bound to another resource, the rate limiter driven past its limit, and a real MCP client proving a read-scoped token cannot write. Four of its tests exist to go RED when a guard is deleted (PKCE verification, expiry, audience and the scope check), and each one was deleted once and watched to fail.

python -m pytest tests/ -q

Metadata

Release files for aleth-mcp 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aleth-mcp 0.3.1
File Size Uploaded Provenance
aleth_mcp-0.3.1.tar.gz 77.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for aleth-mcp 0.3.1
File Interpreter ABI Platform
aleth_mcp-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 139.6 kB

Release files / aleth_mcp-0.3.1.tar.gz

Download URL aleth_mcp-0.3.1.tar.gz
Size 77.3 kB
Tags Source
SHA-256 checksum
How to use checksums
10e7895dc5a0ef11a8faeca126dd6900349ce94140fbb52189db5c1c9bd208a7
BLAKE2b-256 checksum
How to use checksums
8b6bd714faedcf6d60dd73ae0ce83561c65732cfa59922439dcf8d7da0b1dd57
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.15

Release files / aleth_mcp-0.3.1-py3-none-any.whl

Download URL aleth_mcp-0.3.1-py3-none-any.whl
Size 62.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d0e1dff72b0eb037abd0c5f0059b4947412cddee5b2c6a3d25ed71598db9c6ba
BLAKE2b-256 checksum
How to use checksums
75ea32eb38ccd8fe25a8ff8a674d621e52f513527e706e408310696664668ea9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.15

Security for aleth-mcp 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.3.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page