Zenod Memory

dev.zenodPublished by zenod.devOfficial publisherv0.1.0Updated Nov 7, 2026

Memory you own: capture on WhatsApp, filed into your Markdown vault, recalled with citations.

VerifiedStreamable HTTPWeb executableFiles & StorageKnowledge & MemoryDeveloper Tools

Overview

AI-generated overview

A memory agent over MCP that stores what you tell it in your own GitHub Markdown vault and lets an assistant search, read and ask cited questions.

What you can do
  • Store memories and ingest media or artifacts asynchronously, then poll for a receipt with commit SHA and GitHub URL
  • Search the vault by keyword and read a single note by path
  • Ask fuzzy, cross-note questions through a read-only research loop that returns a synthesized answer with cited sources
  • Check the status and result of queued store and ingest jobs
When to use it
Useful when you want an assistant to keep durable, citable personal memory that you own as plain Markdown in a git repo, rather than in a vendor's database. Suits people who already keep notes in GitHub and want recall with receipts.
Requirements
A remote MCP endpoint over streamable HTTP, or a self-hosted Docker instance. Self-hosting needs a GitHub repo you own plus a token that can push to it, an Anthropic or OpenAI LLM key, and a bearer token for the MCP endpoint (pinned via ZENOD_API_TOKEN or generated on first boot). The endpoint always requires an Authorization bearer token; the manifest declares OAuth.
Before you install
The server holds a GitHub token that can push to your vault repo, and an LLM API key; treat the bearer token like a password. Writes are real commits to your repo, and ingest sends media to extraction providers. Self-hosting requires Docker and environment variables; the README notes the standalone unit folder is still a blueprint while the running image is the real behavior.

Installation

In SourceWeft

  1. Open Zenod Memory in the dashboard and add it to a workspace.
  2. Connect the required credentials. They are stored privately and never shown on public pages.
  3. Enable the server for the chats that should use its tools.

Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.

Use with an agent

Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.

Read https://sourceweft.com/mcp/dev.zenod%2Fmemory/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.

Read the install guide the agent follows

Other MCP clients

Add this to your client's mcpServers config. The client completes OAuth sign-in on first connect.

{
  "mcpServers": {
    "zenod-memory": {
      "type": "http",
      "url": "https://cloud.zenod.dev/mcp"
    }
  }
}

README

Zenod — the memory unit

Zenod is a memory agent you talk to over plain MCP (streamable HTTP). You give it a GitHub repo to use as your memory vault; it stores what you tell it as immutable evidence + filed meaning with citations, and lets you search and read it back. Every write lands as a real git commit — Zenod hands you the commit SHA and GitHub URL as the receipt.

One box, one endpoint, one repo. Zenod is the only thing that holds your repo token. You never write the repo from anywhere else — attempts from another path fail loudly.


Quickstart — point a plain MCP client at a fresh Zenod

You need: Docker, a GitHub repo you own (empty is fine) + a token that can push to it, and an LLM key (Anthropic or OpenAI). No suite, no console — just this unit.

1. Run a fresh instance

From this folder (units/zenod/). Until the repo split fires, the image is the repo root image with AGENT=zenod:

bash
# from the repo rootdocker build -t zenod .
docker run --rm -p 8080:8080 \  -v "$PWD/zenod-data:/data" \  -e AGENT=zenod \  -e ZENOD_DATA_DIR=/data \  -e VAULT_REPO="your-org/your-memory-repo" \  -e GITHUB_TOKEN="ghp_xxx_a_token_that_can_push_to_that_repo" \  -e ANTHROPIC_API_KEY="sk-ant-..." \  -e ZENOD_API_TOKEN="pick-any-long-secret-string" \  zenod

That is Option B: configure by hand — the three env vars above are all Zenod needs. (In the full suite the keyring/Console mints the MCP token and pushes VAULT_REPO + GITHUB_TOKEN + LLM key to /api/provision instead; a stranger self-hosting doesn't need that path.)

Zenod is now serving MCP at http://localhost:8080/mcp. Check it is up:

bash
curl -s http://localhost:8080/api/health

2. Point any MCP client at it

The endpoint is vanilla MCP over streamable HTTP. Any MCP client works — e.g. Claude Desktop / claude with a custom connector, or the reference @modelcontextprotocol/inspector:

bash
npx @modelcontextprotocol/inspector# transport: Streamable HTTP# URL:       http://localhost:8080/mcp# header:    Authorization: Bearer <token>

Your token. /mcp always requires a bearer token — there is no tokenless mode. You control it two ways:

  • Pin it (recommended): set ZENOD_API_TOKEN in the run command above to any long secret string. That IS your bearer token — use it directly.
  • Don't pin it: if ZENOD_API_TOKEN is unset, Zenod generates a token on first boot and prints it once to the container logs (docker logs <container> | grep 'MCP bearer'). Copy it from there.

Send it as Authorization: Bearer <token> on every MCP call. (Treat it like a password. Note: GET /api/token is itself auth-gated — you need the token to call it — so use one of the two paths above to learn it in the first place.)

Run tools/list. You should see store_memory, ingest_memory, search_memory, get_memory, ask_brain, and get_task_result (full surface in SEAM-SURFACE.md).

Codex Desktop bearer-token setup

Codex Desktop's MCP server settings have two separate concepts:

  • URL: paste the Zenod MCP URL, for example https://<host>/mcp.
  • Bearer token env var: enter the environment variable name, for example ZENOD_MCP_TOKEN.

Do not paste the token itself into the Bearer token env var field. Codex reads that field as the name of an environment variable, then reads the actual bearer token from the Codex process environment.

For Codex CLI, this works when codex is launched from the same shell:

bash
export ZENOD_MCP_TOKEN="<token>"codex mcp add zenod --url https://<host>/mcp --bearer-token-env-var ZENOD_MCP_TOKEN

A normal shell export is temporary: it lasts only for that shell session and child processes. It does not survive a machine restart, and it does not affect an already running macOS GUI app launched from Finder, Dock, or Spotlight. For Codex Desktop, set the variable in the environment before starting Codex Desktop, or use a literal Authorization: Bearer <token> header in the MCP server settings.

3. Store / search / get

Store (mutating, async — returns a jobId, then poll):

jsonc
// tools/call store_memory{ "content": "My home insurance renews on March 3rd every year." }// -> returns { jobId: "..." }  (status: queued)
// tools/call get_task_result{ "jobId": "..." }// -> when done:// { status: "done", result: {//     evidenceRef: "Log/2026-07-05.md#^e-7f3a2c",//     pagesTouched: ["Areas/Insurance.md"],//     commitSha: "a1b2c3d...",              // <- your receipt: a real git commit//     githubUrls: ["https://github.com/your-org/your-memory-repo/blob/main/Areas/Insurance.md"]// } }

Open the githubUrls link — the memory is a committed file in your repo.

Ingest media/artifacts (mutating, async — returns a jobId, then poll):

jsonc
// tools/call ingest_memory{  "mediaType": "screenshot",  "artifactUrl": "https://example.test/screen.png",  "filename": "screen.png",  "sourceHint": "Claude upload",  "contentHint": "remember the renewal date visible in this screenshot",  "hints": ["insurance"]}// -> returns { jobId: "...", kind: "media_ingest", status: "queued" }
// tools/call get_task_result{ "jobId": "..." }// -> when done, result includes:// {//   status: "done",//   rawArtifact: { handle, archiveUrl, sha256 },//   extraction: { handle, ocrHandle, transcriptHandle, archiveUrl, provider },//   digest: { evidenceRef, pagesTouched, commitSha, githubUrls }// }// Opaque handles that Zenod cannot resolve still return a loud// media_ingest_processor_unavailable receipt instead of fake success.

Search (read, fast, no LLM):

jsonc
// tools/call search_memory{ "query": "insurance renewal" }// -> hits: [{ path, snippet, score, githubUrl }], or the explicit text//    "No memories match 'insurance renewal'." when there are none.

Get (read one note by path from a search hit):

jsonc
// tools/call get_memory{ "path": "Areas/Insurance.md" }// -> { path, frontmatter, body, githubUrl }

For fuzzy or cross-note questions ("what do I know about X?"), call ask_brain instead of search_memory — it runs a read-only research loop and returns a synthesized answer with cited sources.


What you can rely on (the contract)

  • The endpoint is standard MCP — nothing custom on the wire. tools/list + tools/call is all a caller needs to know. See ../../docs/SEAM-SPEC.md.
  • Every write returns a receipt. store_memory gives you a commitSha + GitHub URL (via get_task_result). ingest_memory gives an async media receipt with raw artifact, extraction/transcript archive, digest, commit, and archive fields. A read returns data or an explicit "none". No silent acks; failures error loudly with { code, message }.
  • Only Zenod holds your repo token. It is supplied to this one box (env or /api/provision) and read in exactly one place internally. Nothing else in the suite can write your repo. Any write attempt down another path fails loudly.
  • Your memory is just a git repo. Clone it, read it, leave — no lock-in.

Files in this unit

FileWhat it is
README.mdthis quickstart (an acceptance item)
SEAM-SURFACE.mdthe MCP tool surface + receipt shapes
EXTRACTION-MAP.mdfile-level extraction map (blueprint, RD-4 staged)
Dockerfileroot-image-reuse note (staged; build from repo-root Dockerfile)
docker-compose.zenod.ymldeploy stub

Status honesty: this folder is the seam of record for Zenod-as-a-unit. The physical extraction from the fused Console is STAGED behind the RD-4 split trigger and has not fired — see EXTRACTION-MAP.md. The running behavior above is real today (image with AGENT=zenod); the standalone folder is blueprint.

Source: units/zenod/README.md at commit 0d25720

Tools

0
This server asks you to sign in, so SourceWeft could not read its tools without an account and has not indexed them. What a connected account sees can depend on that account and its permissions.

Version history

1
  1. v0.1.0LatestNov 7, 2026

Score

85% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 daysRequired25 pts
  • Auth declaredRequired10 pts
  • Tools listed15 pts

    Answer tools/list without a credential, or ship a package that starts in a sandbox without one.

  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts