Status
Private backlog on this fork only. Do not implement until the pi 8-layer stack is on upstream main (or abandoned). Do not add commits to stack/1…stack/8.
Do not mention this issue on upstream PRs.
What this is
kunickiaj’s review of the original pi PR listed an important follow-up (not one of the seven blockers):
MCP/viewer memory access and schema behavior should share core primitives to avoid authorization and contract drift.
His stack sketch put this in layer 1: “Shared spool/memory-access refactors.”
We already shipped spool + the kind catalog in layer 1. We did not ship shared access helpers. This issue is the rest of that follow-up.
Plain language: MCP and viewer HTTP are two doors into the same notebook. The rules for “what kinds exist” and “may this caller see/write this note” must live in @codemem/core. Each door calls those rules. They must not keep their own copies.
Already done (do not redo)
packages/core/src/memory-kinds.ts — kinds + validateMemoryKind
- MCP
memory-kinds.ts re-exports core
- Viewer remember routes import
REMEMBER_MEMORY_KINDS
- Bounded JSON parser exported from viewer helpers; L3 POSTs use it
- Auth check both doors already use is
store.timeline(..., filters)
Still to do
Schema (contract)
- MCP
memoryKindSchema in packages/mcp-server/src/schemas.ts must be derived from REMEMBER_MEMORY_KINDS. Delete the handwritten z.enum(["discovery", …]).
- One filter-key list in
@codemem/core. Viewer SCHEMA_FILTER_NAMES and MCP filterNames both come from it. No second hand-sorted array.
Access (authorization)
Move the twins into core. Callers pass only what differs:
getVisibleMemory(store, id, filters) → store.timeline(null, id, 0, 0, filters).find(...)
getVisibleMemories / forgetVisibleMemory the same way
rememberVisibleMemory(store, input, { toolVersion, metadata, envProject, cwd, user, now })
MCP keeps toolVersion: "mcp-ts", metadata: { mcp: true }.
Viewer keeps toolVersion: "viewer-api", metadata: { viewer: true }.
Existing MCP tests in memory-access.test.ts should keep passing against the core functions (or thin aliases).
Must not change (regression guards)
Sharing primitives does not mean sharing defaults.
- Viewer read project default stays
null. Do not inject process cwd / CODEMEM_PROJECT on HTTP.
- MCP read may still default project from cwd/env. Pass
defaultProject in; do not pick one policy for both.
- Writes on both doors already honor explicit
project or env only. Keep that.
- Do not extract
expandMemories, distill file loading, HTTP parseGetFilters, or the retrieval ledger.
When to implement
- Layer 1 is on upstream
main.
- Prefer waiting until layer 3 (viewer routes) is also there, so one PR can switch both doors.
- Branch from upstream
main, not from stack/8.
- Do not shrink this back to “lists only.”
Verification
pnpm exec vitest run packages/mcp-server/src/memory-access.test.ts packages/mcp-server/src/server.test.ts
CODEMEM_EMBEDDING_DISABLED=1 pnpm exec vitest run packages/viewer-server/src/routes/memory-tools.test.ts
- MCP kind enum ===
REMEMBER_MEMORY_KINDS
- Viewer schema filter names === MCP
filterNames === core list
- Nested-cwd / no-implicit-project tests on the viewer still pass
tool_version / session metadata still mcp-ts vs viewer-api
Status
Private backlog on this fork only. Do not implement until the pi 8-layer stack is on upstream
main(or abandoned). Do not add commits tostack/1…stack/8.Do not mention this issue on upstream PRs.
What this is
kunickiaj’s review of the original pi PR listed an important follow-up (not one of the seven blockers):
His stack sketch put this in layer 1: “Shared spool/memory-access refactors.”
We already shipped spool + the kind catalog in layer 1. We did not ship shared access helpers. This issue is the rest of that follow-up.
Plain language: MCP and viewer HTTP are two doors into the same notebook. The rules for “what kinds exist” and “may this caller see/write this note” must live in
@codemem/core. Each door calls those rules. They must not keep their own copies.Already done (do not redo)
packages/core/src/memory-kinds.ts— kinds +validateMemoryKindmemory-kinds.tsre-exports coreREMEMBER_MEMORY_KINDSstore.timeline(..., filters)Still to do
Schema (contract)
memoryKindSchemainpackages/mcp-server/src/schemas.tsmust be derived fromREMEMBER_MEMORY_KINDS. Delete the handwrittenz.enum(["discovery", …]).@codemem/core. ViewerSCHEMA_FILTER_NAMESand MCPfilterNamesboth come from it. No second hand-sorted array.Access (authorization)
Move the twins into core. Callers pass only what differs:
getVisibleMemory(store, id, filters)→store.timeline(null, id, 0, 0, filters).find(...)getVisibleMemories/forgetVisibleMemorythe same wayrememberVisibleMemory(store, input, { toolVersion, metadata, envProject, cwd, user, now })MCP keeps
toolVersion: "mcp-ts",metadata: { mcp: true }.Viewer keeps
toolVersion: "viewer-api",metadata: { viewer: true }.Existing MCP tests in
memory-access.test.tsshould keep passing against the core functions (or thin aliases).Must not change (regression guards)
Sharing primitives does not mean sharing defaults.
null. Do not inject process cwd /CODEMEM_PROJECTon HTTP.defaultProjectin; do not pick one policy for both.projector env only. Keep that.expandMemories, distill file loading, HTTPparseGetFilters, or the retrieval ledger.When to implement
main.main, not fromstack/8.Verification
pnpm exec vitest run packages/mcp-server/src/memory-access.test.ts packages/mcp-server/src/server.test.tsCODEMEM_EMBEDDING_DISABLED=1 pnpm exec vitest run packages/viewer-server/src/routes/memory-tools.test.tsREMEMBER_MEMORY_KINDSfilterNames=== core listtool_version/ session metadata stillmcp-tsvsviewer-api