feat: add mempalace_checkpoint batch save tool

Collapse the Cursor auto-save sequence (check_duplicate Nx + add_drawer
Nx + diary_write 1x) into a single mempalace_checkpoint MCP call so the
host UI renders one tool-call card and keeps its spinner up for the whole
save. The new tool reuses the existing single-item handlers, so semantic
dedup, idempotency, and verbatim guarantees are unchanged.

- mcp_server.py: add tool_checkpoint + register mempalace_checkpoint
- service.py: classify mempalace_checkpoint as a write tool
- cursor save hook: followup now drives one mempalace_checkpoint call
- docs: new mcp-tools.md section, help.md entry, 33 -> 34 tool count sweep
- tests: checkpoint add/dedup/malformed/registry + classify_tool

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
undeadindustries 2026-06-22 13:41:15 +10:00
parent e0da99b2d5
commit 4291fecfdf
22 changed files with 197 additions and 29 deletions

View File

@ -1,6 +1,6 @@
# MemPalace Claude Code Plugin
A Claude Code plugin that gives your AI a persistent memory system. Mine projects and conversations into a searchable palace backed by ChromaDB, with 33 MCP tools, auto-save hooks, and 5 guided skills.
A Claude Code plugin that gives your AI a persistent memory system. Mine projects and conversations into a searchable palace backed by ChromaDB, with 34 MCP tools, auto-save hooks, and 5 guided skills.
## Prerequisites
@ -50,7 +50,7 @@ Set the `MEMPAL_DIR` environment variable to a directory path to automatically r
## MCP Server
The plugin automatically configures a local MCP server with 33 tools for storing, searching, and managing memories. No manual MCP setup is required -- `/mempalace:init` handles everything.
The plugin automatically configures a local MCP server with 34 tools for storing, searching, and managing memories. No manual MCP setup is required -- `/mempalace:init` handles everything.
## Full Documentation

View File

@ -8,7 +8,7 @@
{
"name": "mempalace",
"source": "./.claude-plugin",
"description": "AI memory system — mine projects and conversations into a searchable palace. 33 MCP tools, auto-save hooks, guided setup.",
"description": "AI memory system — mine projects and conversations into a searchable palace. 34 MCP tools, auto-save hooks, guided setup.",
"version": "3.4.1",
"author": {
"name": "milla-jovovich"

View File

@ -1,7 +1,7 @@
{
"name": "mempalace",
"version": "3.4.1",
"description": "Give your AI a memory — mine projects and conversations into a searchable palace. 33 MCP tools, auto-save hooks, and guided setup.",
"description": "Give your AI a memory — mine projects and conversations into a searchable palace. 34 MCP tools, auto-save hooks, and guided setup.",
"author": {
"name": "milla-jovovich"
},

View File

@ -1,6 +1,6 @@
# MemPalace - Codex CLI Plugin
Give your AI a persistent memory -- mine projects and conversations into a searchable palace backed by ChromaDB, with 33 MCP tools, auto-save hooks, and guided skills.
Give your AI a persistent memory -- mine projects and conversations into a searchable palace backed by ChromaDB, with 34 MCP tools, auto-save hooks, and guided skills.
## Prerequisites

View File

@ -1,7 +1,7 @@
{
"name": "mempalace",
"version": "3.4.1",
"description": "Give your AI a memory — mine projects and conversations into a searchable palace. 33 MCP tools, auto-save hooks, and guided setup.",
"description": "Give your AI a memory — mine projects and conversations into a searchable palace. 34 MCP tools, auto-save hooks, and guided setup.",
"author": {
"name": "milla-jovovich"
},
@ -27,7 +27,7 @@
"interface": {
"displayName": "MemPalace",
"shortDescription": "AI memory system for Codex",
"longDescription": "Give your AI a persistent memory — mine projects and conversations into a searchable palace backed by ChromaDB, with 33 MCP tools, auto-save hooks, and guided skills.",
"longDescription": "Give your AI a persistent memory — mine projects and conversations into a searchable palace backed by ChromaDB, with 34 MCP tools, auto-save hooks, and guided skills.",
"developerName": "milla-jovovich",
"category": "Coding",
"capabilities": [

View File

@ -1,6 +1,6 @@
# MemPalace Cursor Plugin
A Cursor IDE plugin that gives your agent a persistent memory system. Auto-registers the `mempalace-mcp` server (33 MCP tools), ships 5 slash commands, two model-invocable skills (setup/mining/search and a recall protocol), and an optional recall rule.
A Cursor IDE plugin that gives your agent a persistent memory system. Auto-registers the `mempalace-mcp` server (34 MCP tools), ships 5 slash commands, two model-invocable skills (setup/mining/search and a recall protocol), and an optional recall rule.
> Hooks (auto-save + session-start memory recall) are shipped separately under `hooks/cursor/` so the plugin is safe to install in any Cursor workspace without touching the agent loop. See [Hooks](#hooks-optional) below.
@ -87,7 +87,7 @@ This plugin ships `mcp.json` at the plugin root, so Cursor auto-loads the `mempa
}
```
All 33 MemPalace MCP tools (`mempalace_search`, `mempalace_add_drawer`, `mempalace_diary_write`, `mempalace_check_duplicate`, `mempalace_diary_read`, …) become available to the agent immediately. No manual `~/.cursor/mcp.json` edit required.
All 34 MemPalace MCP tools (`mempalace_search`, `mempalace_add_drawer`, `mempalace_diary_write`, `mempalace_check_duplicate`, `mempalace_diary_read`, …) become available to the agent immediately. No manual `~/.cursor/mcp.json` edit required.
If the server doesn't appear, confirm `mempalace-mcp` is on the user `$PATH`:

View File

@ -8,7 +8,7 @@
{
"name": "mempalace",
"source": ".",
"description": "AI memory system — mine projects and conversations into a searchable palace. 33 MCP tools, slash commands, and a guided skill for Cursor.",
"description": "AI memory system — mine projects and conversations into a searchable palace. 34 MCP tools, slash commands, and a guided skill for Cursor.",
"author": {
"name": "milla-jovovich"
}

View File

@ -1,6 +1,6 @@
{
"name": "mempalace",
"description": "Give your AI a memory — mine projects and conversations into a searchable palace. 33 MCP tools, slash commands, and a guided skill for Cursor.",
"description": "Give your AI a memory — mine projects and conversations into a searchable palace. 34 MCP tools, slash commands, and a guided skill for Cursor.",
"author": {
"name": "milla-jovovich"
},

View File

@ -225,7 +225,7 @@ Usage and tool reference:
## MCP server
33 MCP tools cover palace reads/writes, knowledge-graph operations,
34 MCP tools cover palace reads/writes, knowledge-graph operations,
cross-wing navigation, drawer management, and agent diaries. Installation
and the full tool list:
[mempalaceofficial.com/reference/mcp-tools](https://mempalaceofficial.com/reference/mcp-tools.html).

View File

@ -163,14 +163,12 @@ _mempal_build_followup() {
import json, sys
wing = sys.argv[1] if len(sys.argv) > 1 else "cursor_session"
msg = (
"MemPalace save checkpoint. "
"(1) Call mempalace_check_duplicate on the key topics, decisions, "
"and verbatim quotes from this session. "
"(2) For each non-duplicate, call mempalace_add_drawer (wing="
+ wing + ", room=<short topic>, content=verbatim quote). "
"(3) Call mempalace_diary_write (agent_name=cursor-ide, wing="
+ wing + ", entry=AAAK-format summary). "
"Then stop."
"MemPalace save checkpoint. Call mempalace_checkpoint ONCE with: "
"items=[{wing: " + wing + ", room: <short topic>, content: <verbatim "
"quote>}, ...] for the key topics, decisions, and verbatim quotes from "
"this session; and diary={agent_name: cursor-ide, wing: " + wing + ", "
"entry: <AAAK-format summary>}. It dedups, files non-duplicates, and "
"writes the diary in one call. Then stop."
)
print(json.dumps({"followup_message": msg}))
' "$WING"

View File

@ -16,7 +16,7 @@ The Python package that powers MemPalace. All modules, all logic.
| `dialect.py` | AAAK compression — entity codes, emotion markers, 30x lossless ratio |
| `knowledge_graph.py` | Temporal entity-relationship graph — SQLite, time-filtered queries, fact invalidation |
| `palace_graph.py` | Room-based navigation graph — BFS traversal, tunnel detection across wings |
| `mcp_server.py` | MCP server — 33 tools, AAAK auto-teach, Palace Protocol, agent diary |
| `mcp_server.py` | MCP server — 34 tools, AAAK auto-teach, Palace Protocol, agent diary |
| `onboarding.py` | Guided first-run setup — asks about people/projects, generates AAAK bootstrap + wing config |
| `entity_registry.py` | Entity code registry — maps names to AAAK codes, handles ambiguous names |
| `entity_detector.py` | Auto-detect people and projects from file content |

View File

@ -29,6 +29,7 @@ AI memory system. Store everything, find anything. Local, free, no API key.
### Palace (write)
- mempalace_add_drawer -- Add a new memory (drawer)
- mempalace_checkpoint -- Save a whole session in one call (dedup + file + diary)
- mempalace_delete_drawer -- Delete a memory (drawer)
### Knowledge Graph

View File

@ -2937,6 +2937,52 @@ def tool_reconnect():
return {"success": False, "error": str(e)}
def tool_checkpoint(items, diary=None, dedup_threshold=0.9):
"""Batch session save in a single call.
Semantic-dedups each item, files the non-duplicates as drawers, then
writes one diary entry. Collapses the per-item ``check_duplicate`` /
``add_drawer`` / ``diary_write`` sequence into one MCP request so the
host UI renders a single tool-call card (and keeps its spinner up for
the whole save) instead of one card per underlying call.
``items`` is a list of ``{"wing", "room", "content"}`` dicts. ``diary``
is an optional ``{"agent_name", "entry", "topic"?, "wing"?}`` dict.
Reuses the existing single-item handlers so dedup/idempotency/WAL
behaviour is identical to calling them directly.
"""
out = {"added": [], "duplicates": [], "errors": []}
if not isinstance(items, list):
return {"error": "items must be a list of {wing, room, content} objects"}
for item in items:
if not isinstance(item, dict):
out["errors"].append({"item": item, "error": "item must be an object"})
continue
wing = item.get("wing")
room = item.get("room")
content = item.get("content")
if not (wing and room and content):
out["errors"].append({"item": item, "error": "wing, room, content required"})
continue
dup = tool_check_duplicate(content, threshold=dedup_threshold)
if dup.get("is_duplicate"):
out["duplicates"].append({"room": room, "matches": dup.get("matches", [])})
continue
res = tool_add_drawer(wing=wing, room=room, content=content, added_by="checkpoint")
if res.get("success"):
out["added"].append(res)
else:
out["errors"].append(res)
if isinstance(diary, dict) and (diary.get("entry") or diary.get("content")):
out["diary"] = tool_diary_write(
agent_name=diary.get("agent_name", "cursor-ide"),
entry=diary.get("entry") or diary.get("content"),
topic=diary.get("topic", "session-checkpoint"),
wing=diary.get("wing", ""),
)
return out
# ==================== MCP PROTOCOL ====================
TOOLS = {
@ -3247,6 +3293,52 @@ TOOLS = {
},
"handler": tool_add_drawer,
},
"mempalace_checkpoint": {
"description": "Save a whole session in one call: semantic-dedups each item, files non-duplicates as drawers, then writes one diary entry. Use this instead of many separate check_duplicate/add_drawer/diary_write calls — it renders as a single tool-call card in the host UI.",
"input_schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"description": "Verbatim items to file. Each is {wing, room, content} — content is the exact words, never summarized.",
"items": {
"type": "object",
"properties": {
"wing": {"type": "string", "description": "Wing (project name)"},
"room": {
"type": "string",
"description": "Room (short topic: decisions, backend...)",
},
"content": {
"type": "string",
"description": "Verbatim content to store",
},
},
"required": ["wing", "room", "content"],
},
},
"diary": {
"type": "object",
"description": "Optional diary entry written after filing: {agent_name, entry, topic?, wing?}. entry is AAAK-format.",
"properties": {
"agent_name": {
"type": "string",
"description": "Agent name (e.g. cursor-ide)",
},
"entry": {"type": "string", "description": "Diary entry in AAAK format"},
"topic": {"type": "string", "description": "Topic tag (optional)"},
"wing": {"type": "string", "description": "Target wing (optional)"},
},
},
"dedup_threshold": {
"type": "number",
"description": "Similarity threshold 0-1 for the per-item dedup check (default 0.9)",
},
},
"required": ["items"],
},
"handler": tool_checkpoint,
},
"mempalace_delete_drawer": {
"description": "Delete a drawer by ID. Irreversible.",
"input_schema": {

View File

@ -54,6 +54,7 @@ READ_TOOLS = frozenset(
WRITE_TOOLS = frozenset(
{
"mempalace_add_drawer",
"mempalace_checkpoint",
"mempalace_delete_drawer",
"mempalace_update_drawer",
"mempalace_diary_write",

View File

@ -42,6 +42,6 @@ search-before-answer so the agent reads the palace instead of guessing.
## Cursor-specific notes
- The `mempalace-mcp` server is auto-registered by this plugin. Once installed, all 33 MemPalace MCP tools (`mempalace_search`, `mempalace_add_drawer`, `mempalace_diary_write`, `mempalace_check_duplicate`, `mempalace_diary_read`, etc.) are available to the agent without any further configuration.
- The `mempalace-mcp` server is auto-registered by this plugin. Once installed, all 34 MemPalace MCP tools (`mempalace_search`, `mempalace_add_drawer`, `mempalace_diary_write`, `mempalace_check_duplicate`, `mempalace_diary_read`, etc.) are available to the agent without any further configuration.
- For automatic background saving every N agent turns plus session-start memory recall, also install the Cursor hooks separately by running `hooks/cursor/install.sh --scope user` from a cloned MemPalace repo. See [`website/guide/cursor-hooks.md`](../../website/guide/cursor-hooks.md) for the full walkthrough.
- The recommended `agent_name` when calling `mempalace_diary_write` from a Cursor session is `cursor-ide` (matches the precedent of `claude-code` and `codex`).

View File

@ -170,6 +170,7 @@ def test_submit_job_uses_client_and_waits(monkeypatch, tmp_path):
def test_service_tool_classification():
assert service.classify_tool("mempalace_search") == "read"
assert service.classify_tool("mempalace_add_drawer") == "write"
assert service.classify_tool("mempalace_checkpoint") == "write"
assert service.classify_tool("mempalace_mine") == "maintenance"
assert service.classify_tool("unknown") == "unknown"

View File

@ -1527,6 +1527,67 @@ class TestWriteTools:
assert result["vector_disabled"] is True
assert result["vector_disabled_reason"] == "capacity mismatch"
def test_checkpoint_files_items_and_writes_diary(self, monkeypatch, config, palace_path, kg):
_patch_mcp_server(monkeypatch, config, kg)
_client, _col = _get_collection(palace_path, create=True)
del _client
from mempalace.mcp_server import tool_checkpoint
result = tool_checkpoint(
items=[
{"wing": "w", "room": "decisions", "content": "Use PostgreSQL for storage."},
{"wing": "w", "room": "backend", "content": "Cache sessions in Redis."},
],
diary={"agent_name": "cursor-ide", "wing": "w", "entry": "SESSION|did.stuff|★"},
)
assert len(result["added"]) == 2
assert result["duplicates"] == []
assert result["errors"] == []
assert all(a["success"] for a in result["added"])
assert result["diary"]["success"] is True
def test_checkpoint_skips_semantic_duplicates(self, monkeypatch, config, kg):
from mempalace import mcp_server
monkeypatch.setattr(
mcp_server,
"tool_check_duplicate",
lambda content, threshold=0.9: {
"is_duplicate": True,
"matches": [{"id": "x", "similarity": 0.95}],
},
)
called = {"add": False}
def _fail_add(**_kwargs):
called["add"] = True
return {"success": True}
monkeypatch.setattr(mcp_server, "tool_add_drawer", _fail_add)
result = mcp_server.tool_checkpoint(
items=[{"wing": "w", "room": "r", "content": "already known"}]
)
assert result["added"] == []
assert len(result["duplicates"]) == 1
assert called["add"] is False
def test_checkpoint_reports_malformed_items(self, monkeypatch, config, kg):
from mempalace import mcp_server
monkeypatch.setattr(
mcp_server, "tool_check_duplicate", lambda *a, **k: {"is_duplicate": False}
)
result = mcp_server.tool_checkpoint(items=[{"wing": "w", "room": "r"}, "not-a-dict"])
assert result["added"] == []
assert len(result["errors"]) == 2
def test_checkpoint_registered_in_tools(self):
from mempalace import mcp_server
assert "mempalace_checkpoint" in mcp_server.TOOLS
assert mcp_server.TOOLS["mempalace_checkpoint"]["handler"] is mcp_server.tool_checkpoint
def test_get_drawer(self, monkeypatch, config, palace_path, seeded_collection, kg):
_patch_mcp_server(monkeypatch, config, kg)
from mempalace.mcp_server import tool_get_drawer

View File

@ -15,7 +15,7 @@ Restart Claude Code, then type `/skills` to verify "mempalace" appears.
With the plugin installed, Claude Code automatically:
- Starts the MemPalace MCP server on launch
- Has access to all 33 tools
- Has access to all 34 tools
- Learns the AAAK dialect and memory protocol from the `mempalace_status` response
- Searches the palace before answering questions about past work

View File

@ -1,6 +1,6 @@
# MCP Integration
MemPalace provides 33 tools through the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/), giving any MCP-compatible AI full read/write access to your palace.
MemPalace provides 34 tools through the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/), giving any MCP-compatible AI full read/write access to your palace.
## Setup
@ -26,7 +26,7 @@ claude mcp add mempalace -- python -m mempalace.mcp_server --palace /path/to/pal
codex mcp add mempalace -- python -m mempalace.mcp_server --palace /path/to/palace
```
Now your AI has all 33 tools available. Ask it anything:
Now your AI has all 34 tools available. Ask it anything:
> *"What did we decide about auth last month?"*

View File

@ -27,7 +27,7 @@ Or by directly editing your OpenClaw configuration:
## How It Works
Once connected, OpenClaw agents receive all 33 tools along with the **Memory Protocol**—a strict behavioral guide indicating they should:
Once connected, OpenClaw agents receive all 34 tools along with the **Memory Protocol**—a strict behavioral guide indicating they should:
1. **Never guess**: Query `mempalace_search` or `mempalace_kg_query` before confidently answering.
2. **Keep an agent diary**: Maintain continuity between sessions by writing to `mempalace_diary_write`.
3. **Manage the Knowledge Graph**: Update declarative facts when things change using `mempalace_kg_add` and `mempalace_kg_invalidate`.

View File

@ -1,6 +1,6 @@
# MCP Tools Reference
Detailed parameter schemas for all 33 MCP tools.
Detailed parameter schemas for all 34 MCP tools.
## Palace — Read Tools
@ -102,6 +102,20 @@ File verbatim content into the palace. Identical content (same deterministic dra
---
### `mempalace_checkpoint`
Save a whole session in one call. Semantic-dedups each item, files the non-duplicates as drawers, then writes one diary entry. Use this instead of many separate `mempalace_check_duplicate` / `mempalace_add_drawer` / `mempalace_diary_write` calls — it renders as a single tool-call card in the host UI (and keeps the spinner up for the whole save). Reuses the same single-item handlers, so dedup, idempotency, and verbatim guarantees are identical.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `items` | array | **Yes** | Verbatim items to file. Each is `{ wing, room, content }` |
| `diary` | object | No | Diary entry written after filing: `{ agent_name, entry, topic?, wing? }` (`entry` is AAAK-format) |
| `dedup_threshold` | number | No | Similarity threshold 01 for the per-item dedup check (default 0.9) |
**Returns:** `{ added: [...], duplicates: [...], errors: [...], diary? }`
---
### `mempalace_delete_drawer`
Delete a drawer by ID. Irreversible.

View File

@ -9,7 +9,7 @@ mempalace/
├── README.md ← project documentation
├── mempalace/ ← core package
│ ├── cli.py ← CLI entry point
│ ├── mcp_server.py ← MCP server (33 tools)
│ ├── mcp_server.py ← MCP server (34 tools)
│ ├── knowledge_graph.py ← temporal entity graph
│ ├── palace_graph.py ← room navigation graph
│ ├── dialect.py ← AAAK compression
@ -56,7 +56,7 @@ Argparse-based CLI with subcommands: `init`, `mine`, `split`, `search`, `compres
### `mcp_server.py` — MCP Server
JSON-RPC over stdin/stdout. Implements the MCP protocol with 33 tools covering palace read/write, drawer CRUD, knowledge graph, navigation, tunnels, agent diary, and system operations. Includes the Memory Protocol and AAAK Spec in status responses.
JSON-RPC over stdin/stdout. Implements the MCP protocol with 34 tools covering palace read/write, drawer CRUD, knowledge graph, navigation, tunnels, agent diary, and system operations. Includes the Memory Protocol and AAAK Spec in status responses.
### `searcher.py` — Semantic Search