How the `memory_handoff` Tool Facilitates Agent Communication in AI-Memory
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), 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) 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_userfield – when populated, restricts retrieval to that specific operatorOwnerFilterenum – supportsAll,Mine, orUnassignedfiltering 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) 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) 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:
- Deterministic lookup – Row-based storage guarantees O(1) retrieval versus scanning observation logs
- Cross-agent compatibility – JSON payloads require no agent-specific parsing; Claude Code, Codex, and Command Code interpret identical structures
- Security boundaries – Owner filtering ensures operators cannot hijack each other's sessions
- Automatic supersession – The
cwdfield enables directory-scoped handoffs, eliminating stale context when projects span multiple directories
Command-Line Usage Examples
Create a handoff when ending a session:
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:
ai-memory handoff accept \
--workspace default \
--project my_project \
--agent codex \
--cwd "$(pwd)"
Example response:
{
"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:
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:
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) |
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) |
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) |
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) |
SQLite schema for the handoffs table |
Summary
memory_handoff_begincreates structured context snapshots with summary, questions, next steps, and file metadata- Scoped storage via
HandoffScopeandowner_userenables both project-wide and private handoffs memory_handoff_acceptretrieves 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 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →