# How the MemPalace MCP Server Implements I-9 Tools for Reading, Writing, and Maintaining the Palace

> Discover how the MemPalace MCP server uses a JSON-RPC dispatcher and schema-enforced I-9 tools for efficient data reading, writing, and maintenance. Learn more about its robust implementation.

- Repository: [MemPalace/mempalace](https://github.com/MemPalace/mempalace)
- Tags: internals
- Published: 2026-06-06

---

**The MemPalace MCP server centralizes all data access through a single JSON-RPC dispatcher that validates, logs, and executes a suite of schema-enforced I-9 tools defined in [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py).**

The [MemPalace/mempalace](https://github.com/MemPalace/mempalace) repository provides a memory-palace storage system where the MCP server implements a comprehensive set of operations for reading, writing, and maintaining the palace. According to the source code in [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py), these capabilities surface as **I-9 tools**—a named collection of high-level Python functions registered in a central dictionary and invoked through a unified request handler.

## I-9 Tool Architecture and Registration

All I-9 tools are implemented as regular Python functions inside [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py) and gathered into the **`TOOLS`** dictionary at lines 1060–1085. Each entry maps a tool name to its executable function, while the companion **`TOOL_SPECS`** object stores `jsonschema` definitions that enforce type constraints at request time.

This design guarantees that callers receive clear validation errors rather than internal tracebacks when parameters are malformed.

## Request Routing and Tool Dispatch

When a client submits a JSON-RPC payload, **`handle_request()`** at line 2760 extracts the `method` and `params` fields. It validates the arguments against `TOOL_SPECS`, looks up the corresponding function in `TOOLS`, executes it, and returns a formatted response. Any unhandled exception is caught by **`_internal_tool_error()`** near line 2740 to ensure the server always emits stable JSON.

Behind the scenes, **`_parse_args()`** at line 159 builds the CLI interface and registers `--tool` sub-commands, while **`_init_logging()`** at line 93 configures a rotating file logger. The imported **`stdio_protect`** utility prevents accidental writes to the MCP process’s stdout and stderr streams.

## Backend Setup and Helper Resolution

Before tools touch storage, lazy helpers resolve the correct backends. **`_get_client()`**, **`_get_collection()`**, and **`_get_kg()`** instantiate the chosen storage layer—typically ChromaDB for vector embeddings—and the SQLite knowledge graph. These helpers ensure that read and write tools do not instantiate expensive connections until they are actually invoked.

## The Three Functional Groups

The I-9 tools are organized into read/query, write/mutate, and maintenance categories.

### Read and Query Tools

These functions retrieve palace state without changing it. Representatives include:

- **`tool_get_drawer`** (line 1576) – Loads a drawer by UUID and returns its stored text and metadata via **`_fetch_all_metadata()`**.
- **`tool_list_drawers`** – Enumerates drawers filtered by wing or room.
- **`tool_search`** – Performs semantic or keyword search against the vector store.
- **`tool_kg_query`** (line 1738) – Executes temporal graph queries against the SQLite knowledge graph, supporting `as_of` filtering and directional traversal.
- **`tool_diary_read`** (line 1991) – Retrieves the most recent `last_n` diary entries for an agent by querying the `DiaryEntry` entity type.

### Write and Mutate Tools

These functions change palace state and automatically record their actions for recovery:

- **`tool_add_drawer`** (line 1368) – Generates a UUID, inserts a row into the `drawers` table through [`palace.py`](https://github.com/MemPalace/mempalace/blob/main/palace.py), and writes the content embedding via **`_collection_upsert()`**.
- **`tool_update_drawer`** (line 1666) – Performs an in-place update; if `content` is supplied it overwrites the vector embedding, otherwise it mutates only taxonomy fields such as `wing` or `room`. Every call is logged via **`_wal_log()`** before committing.
- **`tool_delete_drawer`** (line 1505) – Removes a drawer from both the SQLite index and the vector store. The operation is idempotent, returning a “not found” status if the UUID is already absent.
- **`tool_diary_write`** (line 1867) – Persists a free-form diary entry as a drawer tagged with a topic, recording `agent_name` as the `added_by` metadata for auditing.
- **`tool_kg_add`** (line 1753) – Inserts a new triple into the knowledge graph, automatically handling temporal fields (`started`, `ended`), validating against duplicates, and writing to the WAL.
- **`tool_kg_invalidate`** – Marks a knowledge-graph edge as no longer valid.

### Maintenance Tools

These functions keep the palace consistent and repair indexes:

- **`tool_sync`** (line 1538) – Scans a project directory via **[`miner.py`](https://github.com/MemPalace/mempalace/blob/main/miner.py)**, detects new or modified files, updates existing drawers, and optionally prunes orphaned drawers when source files are deleted or added to `.gitignore`. It returns a detailed dry-run report unless `apply=True`.
- **`tool_reconnect`** – Re-establishes dropped backend connections.
- **`tool_memories_filed_away`** – Verifies that archived memories remain reachable.
- **`tool_hook_settings`** – Adjusts runtime configuration hooks.
- **`tool_graph_stats`** – Returns structural statistics about the knowledge graph.

## Core Implementations in Depth

### Adding and Retrieving Drawers

The **`tool_add_drawer(content, wing, room, tags=[])`** function is the primary entry point for creating new drawers—the lowest-level storage units in MemPalace. It delegates the SQLite row insertion to [`palace.py`](https://github.com/MemPalace/mempalace/blob/main/palace.py) and persists the vector embedding to the Chroma collection. Conversely, **`tool_get_drawer(drawer_id)`** reconstructs the drawer payload by querying metadata and content from both the relational and vector stores.

### Updating and Deleting Drawers

**`tool_update_drawer(drawer_id, content=None, wing=None, room=None)`** offers partial or full updates. Supplying `content` triggers a full re-embedding, while omitting it limits the mutation to taxonomy fields. Before the database commits, **`_wal_log()`** at line 479 appends an operation record to a write-ahead log file. This record underpins the recovery subsystem implemented in **[`mempalace/repair.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/repair.py)**.

### Knowledge Graph and Diary Operations

**`tool_kg_query(entity, as_of=None, direction="both")`** resolves an entity identifier, filters by validity dates, and follows edges in the requested direction. **`tool_kg_add(subject, predicate, object, started=None, ended=None)`** performs the complementary write, ensuring duplicate triples are rejected and temporal boundaries are honored.

For agent audit trails, **`tool_diary_write(agent_name, entry, topic="general", wing="")`** stores entries as specialized drawers, while **`tool_diary_read(agent_name, last_n=10, wing="")`** queries them through the knowledge graph’s `DiaryEntry` entity interface.

## Invoking I-9 Tools: JSON-RPC and CLI Examples

Clients interact with the I-9 tools through JSON-RPC over HTTP or via the first-party CLI.

### Adding a Drawer over JSON-RPC

```python
import json, urllib.request

payload = {
    "jsonrpc": "2.0",
    "method": "add_drawer",
    "params": {
        "content": "Remember to backup the database every Sunday.",
        "wing": "operations",
        "room": "2024-09-01",
        "tags": ["reminder", "backup"]
    },
    "id": 1
}
req = urllib.request.Request(
    "http://127.0.0.1:8000/",
    data=json.dumps(payload).encode(),
    headers={"Content-Type": "application/json"}
)
print(json.loads(urllib.request.urlopen(req).read()))

```

The response contains the new drawer’s UUID and a `status: "ok"` field.

### Reading a Drawer from the CLI

```bash
mempalace --tool get_drawer --drawer-id 7e3a5c9b-2f1d-4c6a-9143-1b5f2a5d9c8e

```

This prints the stored text together with its metadata, including wing, room, and tags.

### Syncing a Project Directory

```bash
mempalace --tool sync --project-dir ~/my_project --wing development --apply

```

**`tool_sync`** scans `~/my_project`, creates or updates drawers for every source file, and removes drawers whose source files have been deleted or added to `.gitignore`. Without `--apply`, it emits a dry-run report.

### Adding a Knowledge-Graph Triple

```bash
mempalace --tool kg_add \
    --subject "Alice" \
    --predicate "works_at" \
    --object "AcmeCorp" \
    --started "2022-01-01"

```

The new edge is persisted in the SQLite graph and becomes available to subsequent `kg_query` calls.

## Summary

- The MemPalace MCP server exposes all operations through the **I-9 tool suite**, a registered collection of Python functions in [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py).
- **`handle_request()`** at line 2760 serves as the single JSON-RPC dispatcher, validating inputs against `TOOL_SPECS` before executing the matching function from the `TOOLS` dictionary.
- Tools are grouped into **read/query**, **write/mutate**, and **maintenance** categories, covering everything from drawer CRUD to knowledge-graph traversal and filesystem synchronization.
- Every mutating operation is recorded via **`_wal_log()`** at line 479, enabling crash recovery through [`mempalace/repair.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/repair.py) and guaranteeing auditable state changes.
- Clients invoke tools uniformly via JSON-RPC or the CLI, with strict schema validation preventing invalid data from entering the palace.

## Frequently Asked Questions

### What does I-9 mean in the MemPalace codebase?

The developers named the tool suite after the U.S. Employment-Eligibility **Form I-9**. The name emphasizes that these functions are the official, auditable entry points for any data entering the palace, and strict schema validation must pass before any write is accepted.

### How does the MCP server validate incoming tool requests?

The server validates every JSON-RPC request against **`TOOL_SPECS`**, a set of `jsonschema` definitions stored directly in [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py). This check occurs inside **`handle_request()`** before the matching tool function is invoked, ensuring type-safe parameters and clear error messages.

### What happens if a write operation crashes before it finishes?

Every mutating tool calls **`_wal_log()`** at line 479 to append an operation record to a write-ahead log. If the server crashes mid-operation, **[`mempalace/repair.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/repair.py)** can replay or reconcile these records, preventing partial writes and keeping the SQLite index consistent with the vector store.

### How can I synchronize an external project with MemPalace?

Use the CLI command **`mempalace --tool sync --project-dir <path> --wing <name> --apply`**. Under the hood, **`tool_sync`** at line 1538 invokes **[`miner.py`](https://github.com/MemPalace/mempalace/blob/main/miner.py)** to scan the directory, then creates, updates, or prunes drawers to match the current filesystem state.