Skip to main content
Glama
FloLey
by FloLey

Personal Memory Wiki

A persistent personal memory system for Claude. It accumulates, organizes, and surfaces a single user's knowledge over time, built as a Python MCP server plus nightly consolidation and weekly digest daemons. All data is plain Markdown in a local git repo. No database, no embeddings, no RAG.

This project currently lives inside the FloLey-public-website repo, in this self-contained memory-wiki/ folder, so it can be lifted into its own repo later with a folder copy. The code is path-agnostic via the WIKI_ROOT environment variable.

Status: Nightly dream (automatic schedule)

The dream can run on its own, once a night, via an in-process scheduler thread started with the server. The mode is set from the console (/ui/dream) and stored in the wiki (dream_schedule.json): off (default), dry-run (propose nightly, review in the morning), or execute (apply nightly). It fires once a day after a chosen local hour; "once a day" is enforced by the presence of the day's report, so restarts never double-run and a missed night is caught up. Nothing happens until you switch it on.

Related MCP server: Chronos MCP

Status: Slice 9 (staged dream pipeline + editable prompts)

The dream now runs as a three-stage pipeline instead of one big call:

  1. triage (input: policy + all short-term + the long-term index) clusters and routes each unit;

  2. decide (per unit, reads only the touched pages) chooses the action;

  3. write (per integrate/promote) produces the page's final content + a one-line index description.

So no call ever needs the whole long-term memory; cost scales with the number of captures, not the wiki size. The dry-run stops after stage 2 (reports the decisions); execute runs stage 3 and applies in one commit, with the index regenerated by code (descriptions from stage 3). The three stage prompts are editable files (prompts/triage.md, decide.md, write.md, seeded) viewable at /ui/prompts; the JSON schema is injected by code so editing guidance cannot break the contract. Each stage's model is WIKI_DREAM_MODEL by default, overridable per stage via WIKI_DREAM_MODEL_TRIAGE / _DECIDE / _WRITE.

Status: Slice 8 (temporal items + dream execution)

Adds two things:

  • Temporal items (temporal/): dated, transient things (todo, reminder, event) that live until a date, then are archived. Every item has a due date; a durable fact with no expiry belongs on a long-term page, not here. remember takes optional due and type; the daemon files dated captures here rather than into long-term knowledge; prime() surfaces the active ones.

  • Dream execution: the dream can now apply a plan (not just propose). The model returns a structured JSON plan (full page contents, temporal items, the new index, which short-term entries were consumed); the applier writes pages, creates temporal items, updates the index, drops consumed short-term entries, expires past-due temporal items, all in one revertible commit. It never deletes long-term content. The dry-run stays; /ui/dream has both a dry-run and an "Execute" button.

Status: Slice 7 (consolidation daemon, dry-run)

Slice 7 adds the dream: a consolidation pass that reads short-term memory and the policy (DREAM.md) and proposes how to distil captures into long-term pages. It runs as a dry-run for now: it writes a report to dream_reports/ and changes nothing. Trigger it from the console at /ui/dream (a "Run a dream" button) and read the report there. The policy DREAM.md ships with a default and is seeded into the wiki on first run (never overwriting an edited one); tune it in /ui. The model is configurable via WIKI_DREAM_MODEL (defaults to Opus) and needs ANTHROPIC_API_KEY. Execution (actually applying the plan) is a later slice, deliberately after iterating on the policy via dry-runs.

Status: Slice 6 (lean tool surface)

Slice 1 proved the chain end to end. Slice 2 added GitHub OAuth. Slice 3 added short-term memory. Slice 4 added the web console at /ui. Slice 5 added rich reading. Slice 6 consolidates the MCP surface to four tools: a small surface is easier for the model to use correctly (fewer ways to pick the wrong tool).

MCP tools (for Claude):

  • prime() - call FIRST: loads the grounding context (the self/ pages then the long-term and short-term indexes) in one call.

  • read(path) - read any file: a page, an index, or a short-term entry. Tolerant of paths (a bare self/identity.md works) and suggests alternatives on a miss rather than asserting a file does not exist.

  • search(query_text, max_results?) - full-text search, returning path:line: text matches.

  • remember(content, summary?, tags?) - capture into short-term memory: writes short_term/entries/{id}.md, appends to the index, commits with stm:.

  • GET /health - liveness probe for the Docker healthcheck (public, not a tool).

Writing to long-term memory is intentionally not an MCP tool: it happens through the web console (manual:) or the future nightly daemon, not a live conversation. All tools honour the owner allow-list and the path guard, so long_term/private/ is never read or searched.

Short-term memory is the open, fast-to-write layer. It accumulates as you talk; a later consolidation phase will distil it into curated long-term pages. Over MCP, remember is intentionally the only write path: structural long-term edits belong to the web console or the (future) nightly daemon, not to a live conversation.

Web console (slice 4)

A private, browser-facing console served by the same process at /ui:

  • /ui - overview: lists every markdown file under the wiki (the structure).

  • /ui/page/{path} - view a page rendered from markdown.

  • /ui/edit?path=... - edit a page (or create a new one when path is empty).

  • POST /ui/save, POST /ui/delete - write or soft-delete, committed with a manual: prefix. Soft-delete removes the file from the working tree but keeps it in git history.

  • /ui/login, /ui/auth/callback, /ui/logout - GitHub login flow.

Auth: a browser GitHub login (the same OAuth app as the MCP endpoint), restricted to the owner (WIKI_ALLOWED_GITHUB_LOGIN), with a signed, HttpOnly/Secure session cookie. Forms carry a signed CSRF token. Markdown is rendered with raw HTML disabled, so stored content cannot inject markup. In local dev (WIKI_AUTH_DISABLED=1) the login is bypassed.

One-time GitHub change required: the console callback is https://wiki.florent-lejoly.be/ui/auth/callback, which is not a subpath of the MCP callback. Set the GitHub OAuth app's Authorization callback URL to the root https://wiki.florent-lejoly.be/; GitHub accepts any subpath of it, so both /auth/callback (Claude) and /ui/auth/callback (console) work.

Authentication (slice 2)

The server is protected by GitHub OAuth via FastMCP's GitHubProvider (an OAuth proxy that runs the standard OAuth 2.1 + PKCE discovery flow Claude.ai expects). On top of "any valid GitHub login", an allow-list middleware (AllowedUserMiddleware) restricts access to a single GitHub account (WIKI_ALLOWED_GITHUB_LOGIN), so the wiki stays private to its owner.

Controlled by environment:

  • WIKI_AUTH_DISABLED=1 runs the server open. The dev compose sets this, so local development needs no secrets.

  • Otherwise GH_OAUTH_CLIENT_ID and GH_OAUTH_CLIENT_SECRET are required; the server refuses to start without them (production can never come up silently open). WIKI_JWT_SIGNING_KEY should be set to a stable random value so issued tokens survive restarts and the user is not forced to re-authorize on every deploy.

In production these come from repository Actions secrets, injected by the deploy workflow into a gitignored .env on the VPS. The /health route stays public regardless, for the container healthcheck.

GitHub OAuth app setup (one time)

Create a GitHub OAuth App (Settings -> Developer settings -> OAuth Apps) with:

  • Homepage URL: https://wiki.florent-lejoly.be

  • Authorization callback URL: https://wiki.florent-lejoly.be/auth/callback

Then set the repository Actions secrets GH_OAUTH_CLIENT_ID, GH_OAUTH_CLIENT_SECRET, WIKI_JWT_SIGNING_KEY (names must not start with GITHUB_, which GitHub reserves).

Architecture

  • Transport: Streamable HTTP, MCP mounted at /mcp, listening on :8765.

  • Data: a Docker named volume wiki_data mounted at /srv/wiki. The entrypoint seeds it from seed/ only if empty, then git inits it, so redeploys never clobber data.

  • Public URL: https://wiki.florent-lejoly.be/mcp, fronted by Caddy (auto-HTTPS) in the main docker-compose.yml.

Tests

The logic (storage, temporal items, the dream pipeline, path guard, prime) is covered by a fast pytest suite that runs against a throwaway wiki, with git commits stubbed. From memory-wiki/:

pip install pytest        # or: pip install -e ".[dev]"
pytest -q

The suite also runs in CI on every push. The tests need no API key or network: the model-calling steps are stubbed.

Run locally

From the repo root:

docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build mcp-server

Then:

# Liveness
curl -s localhost:8765/health        # -> {"ok": true}

# Inspect tools and call them interactively (Streamable HTTP transport)
npx @modelcontextprotocol/inspector
#   connect to: http://localhost:8765/mcp
#   call ping, then read_long_term_index

Connect Claude.ai (after deploy + DNS)

  1. Make sure wiki.florent-lejoly.be resolves to the same public IP as florent-lejoly.be (one DNS record, see the deploy note below).

  2. In Claude.ai: Settings -> Connectors -> Add custom connector.

  3. URL: https://wiki.florent-lejoly.be/mcp.

  4. Claude redirects you to GitHub to log in and consent. Only the allow-listed GitHub account can use the tools.

  5. Ask Claude to call ping, then read_long_term_index().

Deploy note (one manual prerequisite)

The build and deploy are automated by the repo's GitHub Actions workflow (a floley-public-website-mcp image is built and pushed, then the VPS pulls it). The only manual step is the DNS record for wiki.florent-lejoly.be. Caddy issues HTTPS automatically on the first request once DNS resolves.

Layout

memory-wiki/
  Dockerfile
  docker-entrypoint.sh    # seed-if-empty + git init, then run the server
  pyproject.toml
  src/wiki_server/
    server.py             # FastMCP app: tools + /health
    paths.py              # path validation under WIKI_ROOT, refuses private/
  seed/                   # initial wiki content, copied into the volume once

Roadmap (next)

Built so far: OAuth, remember() + the lean read/search tools, the web console, the staged dream (dry-run, execute, editable prompts, per-stage model + cost), temporal items, the people/places/organizations taxonomy, and the automatic nightly schedule. The codebase is split into focused modules with a pytest suite run in CI.

Still open from the original vision: the weekly digest (a periodic summary of what changed and what is coming up). Optional later: notifications, and a "reorg" dream to fold residual duplicate pages.

F
license - not found
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/FloLey/memory-wiki'

If you have feedback or need assistance with the MCP directory API, please join our Discord server