# How to Implement Cross-Agent Handoffs Between Claude Code and Codex in ai-memory

> Implement cross-agent handoffs between Claude Code and Codex in ai-memory using MCP tools. Seamlessly transfer context and maintain continuity across AI sessions.

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

---

**ai-memory enables seamless cross-agent handoffs by providing Model Context Protocol (MCP) tools that let one agent create a structured handoff (`memory_handoff_begin`) and another agent consume it (`memory_handoff_accept`), preserving full context across Claude Code and Codex sessions.**

The akitaonrails/ai-memory repository implements a server-side handoff mechanism that eliminates context loss when switching between AI agents. By using dedicated MCP tools, you can transfer session state—including file changes, open questions, and next steps—from Claude Code to Codex without manual copy-pasting or context window management.

## Understanding the Handoff Architecture

### The Two-Phase Protocol

The handoff system operates through a strict two-phase commit pattern. First, the ending agent creates a handoff record in the `handoffs` table. Second, the starting agent consumes that record. This design ensures that no context is lost even if hours or days pass between sessions.

In [`crates/ai-memory-mcp/src/server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs), the implementation exposes two primary tools: `memory_handoff_begin` (lines 37–46) for creation and `memory_handoff_accept` (lines 48–57) for consumption. The server handles all persistence logic, meaning agents only need to invoke these tools without managing state directly.

### Data Structure and State Management

The core data structure 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). The `NewHandoff` struct (lines 58–88) captures:

- `summary`: A high-level description of completed work
- `open_questions`: Pending uncertainties for the next agent
- `next_steps`: Recommended actions
- `files_touched`: List of modified files
- `cwd`: Current working directory for context
- `shared`: Optional flag for project-wide visibility

The `HandoffState` enum (lines 20–28) tracks lifecycle states, ensuring each handoff can only be consumed once. Once accepted, the state transitions from `Open` to `Accepted`, preventing duplicate work.

## Creating a Handoff from Claude Code

### The memory_handoff_begin MCP Tool

When Claude Code finishes a session, it calls the `memory_handoff_begin` tool. This tool records a `NewHandoff` row via the writer actor and returns a unique handoff ID. The implementation in [`crates/ai-memory-mcp/src/server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs) (lines 37–46) validates the request context and persists the payload atomically.

The tool automatically captures the operator identity from the request's actor context, setting the `owner_user` field unless the `shared` flag is explicitly enabled.

### Structuring Handoff Content

Effective handoffs require structured content. The `NewHandoff` struct enforces this by separating concerns into distinct fields. Rather than dumping raw conversation history, you provide curated context that the next agent can immediately act upon.

```bash

# Claude Code creates a handoff before exiting

ai-memory handoff begin \
    --workspace default \
    --project my-app \
    --summary "Implemented JWT authentication flow; all integration tests passing" \
    --open-questions "Should we implement refresh token rotation?" \
    --next-steps "Add OAuth2 provider support" \
    --files-touched src/auth.rs src/middleware.rs tests/auth_test.rs

```

This command returns a handoff ID (e.g., `a1b2c3...`) and stores the context in the ai-memory database, ready for the next agent.

## Consuming the Handoff in Codex

### Automatic SessionStart Hook

When Codex initializes a new session via `ai-memory session start`, the built-in `SessionStart` hook automatically invokes `memory_handoff_accept`. This tool queries for the most recent open handoff using the `latest_open_handoff` function in [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs) (lines 3996–4004).

If a valid handoff exists, the tool marks it as `Accepted` and prepends its payload to Codex's initial context window. You see this as a formatted block in the agent's console:

```

📥 ai-memory: pending handoff from previous session
Summary: Implemented JWT authentication flow; all integration tests passing
Open questions:
• Should we implement refresh token rotation?
Next steps:
• Add OAuth2 provider support
Files touched:
• src/auth.rs
• src/middleware.rs
• tests/auth_test.rs

```

### Manual Acceptance for Scripts

For automation or scripting scenarios, you can manually trigger acceptance using the CLI:

```bash

# Fetch handoff manually in Codex or scripts

ai-memory handoff accept \
    --workspace default \
    --project my-app

# Returns JSON payload with full context

```

## Controlling Handoff Visibility with Ownership

### User-Scoped vs Shared Handoffs

By default, handoffs are private to the creator. The `owner_user` field in the `NewHandoff` struct binds the record to the authenticated operator (extracted from OIDC or bearer tokens). This prevents accidental context leakage in multi-user environments.

To enable team handoffs, set the `shared` flag to `true` during creation. This sets `owner_user = None` in the database, making the handoff visible to any operator with project access. This is ideal for shift handovers or collaborative debugging sessions.

### Identity Filtering in Action

The acceptance logic in [`crates/ai-memory-mcp/src/server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs) builds an `owner_filter` from the caller's identity. If the handoff has an owner, only that specific operator can claim it. If shared, the first caller wins. This security model is tested in [`crates/ai-memory-mcp/tests/handoff_admission.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/tests/handoff_admission.rs), which validates proxy-operator isolation and ownership enforcement.

## Complete Cross-Agent Workflow Example

The following workflow demonstrates a complete handoff from Claude Code to Codex, including ownership considerations:

**Step 1: Claude Code finalizes work**

```bash

# Claude Code session ending

ai-memory handoff begin \
    --workspace production \
    --project api-service \
    --summary "Refactored database connection pooling; resolved timeout issues" \
    --open-questions "Consider adjusting pool size for high-traffic periods?" \
    --next-steps "Implement circuit breaker pattern" \
    --files-touched src/db/pool.rs src/db/connection.rs \
    --shared true  # Make available to team

```

**Step 2: Codex starts fresh**

```bash

# Later, Codex picks up the context

ai-memory session start \
    --workspace production \
    --project api-service \
    --agent codex

# Codex automatically receives the formatted handoff context

```

**Step 3: Verification**

If you need to verify the handoff was consumed (preventing duplicate processing):

```bash

# Check for remaining open handoffs

ai-memory handoff accept \
    --workspace production \
    --project api-service

# Returns: {"handoff": null} if already consumed

```

## Summary

- **Server-side persistence**: ai-memory stores handoffs in a database, not agent memory, enabling durable cross-agent context transfer.
- **Two-tool API**: Use `memory_handoff_begin` (lines 37–46 in [`server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/server.rs)) to create handoffs and `memory_handoff_accept` (lines 48–57) to consume them.
- **Single-use guarantee**: The `HandoffState` machine ensures each handoff is consumed exactly once, preventing duplicate work.
- **Ownership controls**: Handoffs default to private (`owner_user` set) but support project-wide sharing via the `shared` flag.
- **Structured context**: The `NewHandoff` struct enforces clean separation of summaries, questions, next steps, and file references for efficient context windows.

## Frequently Asked Questions

### How does ai-memory prevent the same handoff from being consumed by multiple agents?

The handoff system uses a state machine defined in [`crates/ai-memory-core/src/handoff.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/handoff.rs) (lines 20–28) with two states: `Open` and `Accepted`. When `memory_handoff_accept` successfully processes a handoff, it atomically updates the database row to `Accepted` status. Subsequent queries for open handoffs filter out accepted records, ensuring only the first caller receives the context.

### Can I transfer a handoff between different users or machines?

Yes, but only if the handoff is created with the `shared: true` flag. By default, handoffs include an `owner_user` field derived from the creator's authentication context (OIDC or bearer token). Private handoffs can only be accepted by the same identity. Setting `shared` to true sets `owner_user = None`, making the handoff claimable by any authenticated user with project access.

### What happens if Codex starts without an existing handoff?

If no open handoff exists for the project (or if all handoffs are already accepted), the `memory_handoff_accept` tool returns `handoff: null`. Codex then starts with a fresh context window. This behavior is handled gracefully in the `SessionStart` hook, allowing agents to function normally even without pending handoffs.

### Which source files should I examine to understand the handoff implementation?

Key implementation files include:
- [`crates/ai-memory-core/src/handoff.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/handoff.rs): Defines `NewHandoff` struct (lines 58–88) and `HandoffState` enum (lines 20–28)
- [`crates/ai-memory-mcp/src/server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs): Implements `memory_handoff_begin` (lines 37–46) and `memory_handoff_accept` (lines 48–57)
- [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs): Contains `latest_open_handoff` query logic (lines 3996–4004)
- [`crates/ai-memory-mcp/tests/handoff_identity.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/tests/handoff_identity.rs): Demonstrates cross-transport handoffs between HTTP and local transport agents