# How the `memory_handoff` Tool Facilitates Agent Communication in AI-Memory

> Discover how the memory_handoff tool enables AI agent communication by persisting structured context snapshots in SQLite. Seamlessly pass working states between AI-coding agents.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: how-to-guide
- Published: 2026-08-28

---

**The `memory_handoff` tool enables seamless agent-to-agent communication by persisting structured context snapshots in SQLite, allowing one AI-coding agent to pass its full working state to another agent that continues the same project.**

In multi-agent workflows, maintaining continuity between different AI coding assistants is critical. The `ai-memory` crate solves this through a formal **handoff protocol** that captures session context, persists it to durable storage, and enables secure retrieval by authorized agents. This article examines the complete handoff lifecycle as implemented in the `akitaonrails/ai-memory` repository.

## Three-Stage Handoff Lifecycle

The `memory_handoff` facility operates through tightly-coupled creation, storage, and retrieval phases.

### Stage 1: Snapshot Creation with `memory_handoff_begin`

When an agent terminates its session, it calls the MCP tool `memory_handoff_begin`. This constructs a **`NewHandoff`** record containing:

- **Workspace and project IDs** – scope the handoff to the correct organizational boundary
- **Originating agent** – identifies which system created the snapshot
- **Optional target hint** – suggests which agent type should receive the handoff
- **Working directory (cwd)** – prevents context leakage across directory boundaries
- **One-paragraph summary** – human-readable progress description
- **Open questions** – unresolved issues for the next agent to address
- **Next steps** – explicit action items
- **Files touched** – the complete working set from the session

The implementation resides in two key files. The data model is defined in [[`crates/ai-memory-core/src/handoff.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/handoff.rs)](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/handoff.rs), where `NewHandoff` and the `Handoff` struct specify the full schema. The MCP endpoint handling the request appears in [[`crates/ai-memory-mcp/src/server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs)](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs) around line 3350.

### Stage 2: Scoped Storage with Ownership Controls

Each handoff row is indexed by a **`HandoffScope`** combining workspace, project, and handoff UUID. Two security mechanisms govern access:

- **`owner_user` field** – when populated, restricts retrieval to that specific operator
- **`OwnerFilter` enum** – supports `All`, `Mine`, or `Unassigned` filtering modes

The handoff's lifecycle progresses through **`HandoffState`** variants: `Open → Accepted → Expired`. This explicit state machine prevents race conditions where multiple agents attempt to claim the same handoff simultaneously.

### Stage 3: Context Retrieval with `memory_handoff_accept`

Incoming agents invoke `memory_handoff_accept` to locate the most recent open handoff matching their scope and working directory. Upon successful match, the state transitions to **`Accepted`** and the full payload returns as structured JSON.

The acceptance logic shares [[`crates/ai-memory-core/src/handoff.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/handoff.rs)](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/handoff.rs) with the creation code, utilizing `HandoffAcceptance` and `HandoffState` transition methods. The MCP handler is implemented in [[`crates/ai-memory-mcp/src/server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs)](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs) near line 3460.

## Why Structured Handoffs Beat Log Inference

The `memory_handoff` tool represents a deliberate architectural choice with four key advantages over implicit context reconstruction:

1. **Deterministic lookup** – Row-based storage guarantees O(1) retrieval versus scanning observation logs
2. **Cross-agent compatibility** – JSON payloads require no agent-specific parsing; Claude Code, Codex, and Command Code interpret identical structures
3. **Security boundaries** – Owner filtering ensures operators cannot hijack each other's sessions
4. **Automatic supersession** – The `cwd` field enables directory-scoped handoffs, eliminating stale context when projects span multiple directories

## Command-Line Usage Examples

Create a handoff when ending a session:

```bash
ai-memory handoff begin \
  --workspace default \
  --project my_project \
  --agent claude-code \
  --summary "Implemented the core parsing loop" \
  --open-questions "How to handle edge-case tokens?" \
  --next-steps "Add robust error handling" \
  --files-touched src/parser.rs src/lib.rs

```

The command returns a JSON payload containing the `handoff_id` for reference.

Retrieve the handoff when starting a new agent:

```bash
ai-memory handoff accept \
  --workspace default \
  --project my_project \
  --agent codex \
  --cwd "$(pwd)"

```

Example response:

```json
{
  "handoff_id":"123e4567-e89b-12d3-a456-426614174000",
  "summary":"Implemented the core parsing loop",
  "open_questions":["How to handle edge-case tokens?"],
  "next_steps":["Add robust error handling"],
  "files_touched":["src/parser.rs","src/lib.rs"],
  "state":"accepted"
}

```

## Programmatic Rust Integration

For embedded clients, the MCP protocol exposes equivalent Rust methods:

```rust
let handoff = client
    .memory_handoff_begin(NewHandoff {
        workspace_id: ws_id,
        project_id: proj_id,
        from_session_id: Some(sess_id),
        from_agent: AgentKind::ClaudeCode,
        to_agent: Some(AgentKind::Codex),
        cwd: Some(std::env::current_dir()?.into()),
        summary: "Implemented the core parsing loop".into(),
        open_questions: vec!["How to handle edge-case tokens?".into()],
        next_steps: vec!["Add robust error handling".into()],
        files_touched: vec!["src/parser.rs".into(), "src/lib.rs".into()],
        owner_user: None,
    })
    .await?;

```

Subsequent agent retrieval:

```rust
let accepted = client
    .memory_handoff_accept(HandoffAcceptance {
        handoff_id: handoff.id,
        workspace_id: ws_id,
        project_id: proj_id,
        accepting_agent: AgentKind::Codex,
        accepting_session: None,
        accepting_user: None,
        owner_filter: OwnerFilter::All,
        receiving_cwd: Some(std::env::current_dir()?.to_string_lossy().into()),
    })
    .await?;

```

## Core Implementation Files

| Path | Responsibility |
|------|--------------|
| [[`crates/ai-memory-core/src/handoff.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/handoff.rs)](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/handoff.rs) | `NewHandoff`, `Handoff`, `HandoffState`, `HandoffAcceptance` definitions |
| [[`crates/ai-memory-mcp/src/server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs)](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs) | MCP tool handlers: `memory_handoff_begin`, `memory_handoff_accept`, `memory_handoff_cancel` |
| [[`crates/ai-memory-cli/src/commands/handoff.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-cli/src/commands/handoff.rs)](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-cli/src/commands/handoff.rs) | CLI wrappers exposing `ai-memory handoff begin/accept` |
| [[`crates/ai-memory-store/migrations/V02__handoffs.sql`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/migrations/V02__handoffs.sql)](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/migrations/V02__handoffs.sql) | SQLite schema for the `handoffs` table |

## Summary

- **`memory_handoff_begin`** creates structured context snapshots with summary, questions, next steps, and file metadata
- **Scoped storage** via `HandoffScope` and `owner_user` enables both project-wide and private handoffs
- **`memory_handoff_accept`** retrieves and claims open handoffs, transitioning state to prevent double-handling
- **Working directory filtering** automatically invalidates stale handoffs when agents switch directories
- **Cross-agent JSON payloads** ensure compatibility across Claude Code, Codex, and other MCP-compliant systems

## Frequently Asked Questions

### How does `memory_handoff` differ from simple log files?

Unlike log files that require parsing and inference, `memory_handoff` persists explicit structured records with guaranteed schema. The `Handoff` struct in [`crates/ai-memory-core/src/handoff.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/handoff.rs) defines mandatory fields (summary, next steps, files touched) that agents populate intentionally rather than extracting heuristically. This eliminates ambiguity and reduces token consumption when summarizing prior work.

### Can multiple agents claim the same handoff?

No. The `HandoffState` enum enforces exclusive ownership: once an agent calls `memory_handoff_accept`, the state atomically transitions from `Open` to `Accepted`. Subsequent acceptance attempts receive no matching records. This prevents race conditions in concurrent agent deployments.

### What happens to unclaimed handoffs?

Handoffs in the `Open` state eventually transition to `Expired` based on implementation-defined TTL policies. The schema in [`V02__handoffs.sql`](https://github.com/akitaonrails/ai-memory/blob/main/V02__handoffs.sql) supports timestamp tracking, and the MCP server may apply cleanup policies not exposed directly in the tool interface.

### Does `memory_handoff` work across different AI vendors?

Yes. Because the payload is standard JSON without vendor-specific extensions, any MCP-compliant agent can interpret handoffs created by another. The `AgentKind` enum in the source code identifies agents (ClaudeCode, Codex, CommandCode) for metadata purposes but does not gate acceptance—an agent receives and acts upon handoff content regardless of which system created it.