# pi ↔ MemPalace extension The canonical source of `~/.pi/agent/extensions/mempalace.ts` — the bridge that wires the [MemPalace](https://github.com/MemPalace/mempalace) MCP server into the [pi coding-agent](https://github.com/mariozechner/pi-coding-agent) harness. `install.sh` at the repo root symlinks `mempalace.ts` from this directory into `~/.pi/agent/extensions/` so the live file on every machine tracks version control. Works on macOS and Linux (the extension itself is plain Node / TypeScript; the symlink is a POSIX `ln -s`). --- ## What it does 1. **Spawns `mempalace-mcp`** as a subprocess and does the MCP stdio JSON-RPC handshake (`initialize` + `notifications/initialized` + `tools/list`). 2. **Registers each MCP tool** as a pi tool with its real `inputSchema` passed through via `Type.Unsafe(...)` (see gotcha below). 3. **Wake-up auto-injection** (`before_agent_start`, one-shot per fresh session): calls `mempalace_status` + `mempalace_diary_read` and injects the result as a `mempalace-wakeup` system message so the agent orients itself the way `~/.agents/skills/mempalace/SKILL.md` describes. Skipped on resume/fork (context is already in the thread). 4. **Manual wind-down** via a `/mempalace-diary [topic]` slash command: sends a prompt asking the LLM to call `mempalace_diary_write` with an AAAK-formatted entry summarizing the session. Not fully auto because pi sessions are typically short/tactical and `session_shutdown` fires too late to drive another LLM turn. ## Fail-soft If `mempalace-mcp` can't be spawned (PATH missing, binary crashes at startup, …) the extension logs to stderr and returns early. pi keeps working without palace tools rather than refusing to start. ## Identity `agent_name` for diary calls comes from `$MEMPALACE_AGENT_NAME`, defaulting to `"pi"`. First diary write against that identity creates `wing_` in the palace. Set the env var if you want to run pi under a distinct identity on a given machine (e.g. `pi-laptop` vs `pi-server`). ## Debugging - `MEMPALACE_EXT_DEBUG=1` — surface `mempalace-mcp` stderr into pi's stderr. Without this, stderr is drained silently so a misbehaving server doesn't flood the TUI. - If a tool call fails with a generic "Internal tool error", spawn `mempalace-mcp` manually with raw JSON-RPC on stdin to read the server-side error — much faster than guessing. ## The `Type.Unsafe` gotcha Earlier versions of this extension registered every MCP tool with `parameters: Type.Object({}, { additionalProperties: true })`, which discarded each tool's real `inputSchema`. The LLM then saw no parameter names and had to guess, leading to bugs like `mempalace_diary_read` being called with `agent=` instead of the required `agent_name=` and crashing the Python server with `TypeError: missing 1 required positional argument`. The fix (≈ lines 160-170) is to wrap the incoming JSON Schema with `Type.Unsafe<...>(tool.inputSchema)`. TypeBox schemas are plain JSON Schema at runtime plus a `Symbol` marker, so wrapping an externally-sourced schema with `Unsafe` is sufficient — no conversion to a full TypeBox tree is needed, and the LLM now sees every tool's real parameter names. If you ever need to re-loosen the schema for debugging, fall back to the `Type.Object({}, { additionalProperties: true })` default only for that specific tool, not globally. ## File layout ``` mempalace-toolkit/ └── extensions/ └── pi/ ├── README.md ← this file └── mempalace.ts ← symlinked into ~/.pi/agent/extensions/ ``` `install.sh` detects pi by probing for `~/.pi/agent/extensions/` and only creates the symlink when that directory exists. On machines without pi the file stays dormant in the repo. Re-runs are idempotent (same pattern as `bin/` and `SKILL.md`).