Skip to content

fix: Revert memory MCP to per-session stdio (fix Cowork regression) - #14

Merged
mikebronner merged 6 commits into
mainfrom
fix/memory-stdio-revert
Jul 11, 2026
Merged

fix: Revert memory MCP to per-session stdio (fix Cowork regression)#14
mikebronner merged 6 commits into
mainfrom
fix/memory-stdio-revert

Conversation

@mikebronner

Copy link
Copy Markdown
Contributor

Summary

The v0.10.0 switch from per-session stdio to a single shared HTTP server on 127.0.0.1:8765 broke the memory vault in Claude Cowork: Cowork runs in a remote sandbox where nothing listens on that loopback port, so it can't reach the vault. Under the old stdio model each session spawned its own server in-process (in whatever environment it ran, including the Cowork sandbox), so memory worked there.

This reverts the memory MCP to per-session stdio everywhere. The Mac knowingly re-accepts the multi-session index-write concurrency tradeoff the HTTP server was introduced to solve. The revert is complete but reversible — the shared-server implementation stays in the tree; only its invocation stops.

Acceptance bar: the memory vault works in both Claude Cowork (remote sandbox — needs the in-session stdio server) and terminal Claude Code.

Changes (in progress)

  • plugin.json memory MCP → stdio command/args launcher form
  • Stop the shared-server lazy-start (unwire the SessionStart hook + neutralize the warmup health-check block) — reversible, code retained
  • Restore race-guarded index VACUUM on the stdio launcher (atomic mkdir lock, reusing hooks/lib/memory-vacuum.sh)
  • Tests updated + VACUUM-lock test added; suite green
  • Docs repointed to per-session stdio (README, memory-status skill+script, setup skill)
  • Version bump → 0.13.0

Test Plan

  • hooks/test-*.sh suite green (offline; fake-server fixture)
  • shellcheck + bash -n clean on hooks/libs/scripts
  • plugin.json valid JSON, stdio config asserted

Fixes the Cowork memory regression introduced in v0.10.0.

Draft — work in progress.

The v0.10.0 switch to a single shared HTTP server on 127.0.0.1:8765 broke the
memory vault in Claude Cowork, whose remote sandbox has nothing listening on
the host's loopback port. Per-session stdio spawns the server in-process
wherever the session runs (including the Cowork sandbox), so memory works
there again.

Point plugin.json's memory MCP back at the hooks/mcp-memory.sh stdio launcher
(the v0.8.0 command/args form). Replace test-plugin-http-config.sh with
test-plugin-stdio-config.sh, asserting the stdio command config and that no
legacy type/url/headers keys remain.

The shared-HTTP code is retained but no longer wired — a complete but
reversible revert.
With the shared-server supervisor no longer running, nothing maintains the
memory index. Restore out-of-band reclamation on the per-session stdio
launcher, reusing hooks/lib/memory-vacuum.sh rather than the old inline copy.

Add memory_vacuum_locked: a non-blocking atomic-mkdir lock around the gated
memory_vacuum, so among concurrently starting launchers exactly one attempts
the VACUUM and the rest skip immediately. Staleness is PID-liveness (a crashed
launcher's lock is stolen once), mirroring memory-server-up.sh. A VACUUM that
still contends with a live sibling server's writes skips safely via
memory_vacuum's existing SQLite busy timeout — the honest multi-session
tradeoff of per-session stdio.

mcp-memory.sh sources the lib and calls memory_vacuum_locked; the supervisor
keeps calling the unlocked memory_vacuum (it holds the spawn lock in a
no-server-alive window). Tests cover the free / live-held / stale-stolen paths.
With the transport reverted to per-session stdio, the shared HTTP server is a
phantom nobody connects to. Stop auto-starting it — reversibly, keeping every
script in the tree:

- hooks.json: drop the memory-server-up.sh SessionStart hook so the lazy-start
  supervisor never fires.
- session-warmup.sh: remove the shared-server health-check block (and its
  now-dead memory-probe.sh source + MCP_SERVER_NAME). Under stdio nothing
  listens on the loopback port, so memory_probe returned DOWN_NONE every
  startup and printed a phantom "Memory server starting" notice; the MCP host's
  own connect error is the real signal now. A breadcrumb documents re-enable.

memory-server-up.sh / -spawn.sh / -down.sh and memory-probe.sh stay in hooks/
(their tests still pass); re-enabling restores the hooks.json line, the warmup
block, and the http transport. A test asserts the phantom notices no longer
appear at startup.
The memory-status skill + script reported shared-HTTP health (probe, port,
bearer token, start/stop, server.log tail) — all inert under per-session stdio,
where the MCP host spawns the server in-process per session with no listener.

Rewrite both to report what actually determines whether stdio memory works:
resolved vault/cache, launcher presence, whether the server binary is installed,
and index/last-VACUUM facts. start/stop now print an explanatory note (the host
owns the per-session lifecycle) and fall through to status. Add a smoke test
(hooks/test-memory-status.sh) asserting the stdio report, launcher presence,
absence of HTTP/port/token vocabulary, and exit 0.
Repoint the prose docs from shared-HTTP-as-current to per-session-stdio-as-
current, keeping the HTTP path documented as an optional, disabled mode:

- README: rewrite the "Shared memory server" subsection as "Memory server
  transport" — stdio (current, works in Cowork) + a step-by-step "Re-enabling
  the shared HTTP server (optional)" recipe. Drop the shared-server SessionStart
  hook row and the warmup health-check mention; trim the cache layout to the
  stdio artifacts; annotate memory_port / WORKBENCH_MEMORY_{PORT,TOKEN} as inert
  under stdio; replace the "first session may lack memory" limitation (an
  HTTP/token artifact, now gone) with the multi-session index-contention tradeoff.
- setup skill: gate Step 2b (bearer token + settings.json) as "shared HTTP
  server only — skip under stdio"; mark memory_port as HTTP-only; soften the
  restart reminder (stdio config is re-read next session, no restart/token dance);
  fix the plugin-update and token-security notes.
@mikebronner
mikebronner marked this pull request as ready for review July 11, 2026 21:29
@mikebronner
mikebronner merged commit 15597cf into main Jul 11, 2026
1 check passed
@mikebronner
mikebronner deleted the fix/memory-stdio-revert branch July 11, 2026 21:29
mikebronner added a commit that referenced this pull request Jul 26, 2026
…HTTP server.

PR #14 reverted the memory MCP to per-session stdio but never updated
memory-recall.sh, which still curled a shared HTTP server on port 8765
directly. The supervisor that manages that server was unwired by the same
revert, so nothing should have been listening — except an 18-day-old orphan
from the old shared-server model was still alive, silently keeping recall
"working" by accident while every session's every prompt funneled through
one unmonitored 2.4GB process.

Kill the orphan (done separately, out of band) and switch memory-recall.sh
to `markdown-vault-mcp search --json`, a genuine one-shot CLI subcommand —
confirmed via lsof to bind no port and leave nothing running. Trades a warm
daemon's ~25ms response for a one-shot process's ~1s, in exchange for zero
shared state and no daemon left to go unsupervised again.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant