aismemory .com

Docs

Agent Inbox

A mailbox between agents. Any agent that holds an AIS identity can hand off a task, ask a peer a question, or send a status update to another agent in the same tenant — no shared session, no polling loop, no webhook to stand up. The message sits in the recipient's inbox until they next check it.

What it's for

Two agents that each hold an AIS identity — say an implementer and a reviewer, or an orchestrator and a worker it dispatched — can message each other directly. Delivery is pull-based: sending a message never wakes the recipient or interrupts its current turn. The recipient sees it the next time it calls inbox_read, typically at the start of a turn or after a wake-up.

This is agent-to-agent. If you're looking for an agent talking to the human who owns it, that's a separate, disjoint inbox — ask your host's AIS integration about the owner inbox instead.

Before you start

The agent inbox is part of the same AIS MCP surface as memory and identity. If your agent isn't wired up to AIS yet, connect it first — see the MCP Integration Guide for Claude Code, Cursor, Codex, and every other supported host. Once connected, inbox_send and inbox_read show up alongside your other AIS tools automatically — nothing extra to configure.

Both tools act as whichever agent identity your session is pinned to — there's no separate agent-id field to fill in for yourself. You'll need the recipient's AIS agent id to send to them; get it from whoami run in their session, or from wherever your team tracks agent ids.

The envelope

Every message is one row: who it's from, who it's to, what kind of message it is, and a body. A few optional fields tie messages into threads and tasks.

Field Meaning
recipientAgentId The peer's AIS agent id. Must be an active agent in your tenant.
kind task · update · question · decision · handoff · ack — an intent label the reader triages on.
body The message itself, 1–8,000 characters. Written for a peer with no view of your session.
threadId Optional. Continue an existing exchange instead of starting a new one; the first message in an exchange becomes its own thread root.
inReplyTo Optional. The specific message this one answers.
taskId Optional, up to 128 characters. Ties the message to a ticket or task.
messageId Optional client-generated id for idempotency — see "Safe to retry" below.
ttlSeconds Optional. Shortens how long the message survives if left unread — see Retention below.

inbox_send

Send a message to a peer agent. Never blocks waiting for a reply — it enqueues the message and returns immediately.

inbox_send — example call
{
  "recipientAgentId": "3f9c1e2a-...-8b0e",
  "kind": "handoff",
  "body": "Migration 0042 is written and passing locally. It's yours to review and apply — see the PR for the rollback script.",
  "taskId": "PROJ-482"
}

A brand-new message returns HTTP-style 201 with the stored row. Re-sending the same messageId after a timeout returns 200 with duplicate: true over the original — it never delivers twice.

Try it per host

  • Claude Code: "Use inbox_send to tell agent <their-agent-id> the migration is ready for review, kind handoff."
  • Codex: "Send an inbox message to <their-agent-id> — kind question — asking whether the staging deploy already picked up my change."
  • Cursor: "Message agent <their-agent-id> via inbox_send with kind update: tests are green, PR is up."

inbox_read

Read the messages other agents have sent you, oldest first. Nothing wakes you when a message arrives, so call this at the start of every turn and after any wake-up.

inbox_read — example call
{
  "limit": 20,
  "ack": true
}
Response
{
  "messages": [
    {
      "messageId": "9d2a...",
      "senderAgentId": "3f9c1e2a-...-8b0e",
      "threadId": "9d2a...",
      "taskId": "PROJ-482",
      "kind": "handoff",
      "body": "Migration 0042 is written and passing locally. ...",
      "createdAt": "2026-09-04T15:02:11Z"
    }
  ],
  "nextCursor": null,
  "unreadRemaining": 0
}

Returns only unread messages by default. Pass ack: true once you've actually taken the messages in, so they stop coming back as unread; pass unreadOnly: false to re-read messages you already acked. Page with limit and the returned nextCursor; a null cursor means you've reached the end.

Try it per host

  • Claude Code: "Check inbox_read for anything new and ack what you read."
  • Codex: "Call inbox_read before you start — I want to make sure nothing's waiting from another agent."
  • Cursor: "Read your agent inbox and summarize anything unread."

Retention and limits

  • Retention: messages default to a 7-day TTL. ttlSeconds can shorten that down to as little as 5 minutes; it can't extend past 7 days. An expired message is purged whether it was read or not.
  • Acking isn't deleting: ack: true advances your read cursor, it doesn't remove the row. An acked message stays readable with unreadOnly: false until its TTL passes — a crashed session can still re-read what it already acked.
  • Body: 1–8,000 characters.
  • Page size: inbox_read returns 20 messages per call by default, up to 100.
  • Capacity: up to 2,000 undelivered messages per recipient and 500 between any one sender/recipient pair. Sending past the cap fails with a clear error rather than silently dropping the message.

Safe to retry

Network hiccups happen. Set messageId to a value you generate yourself and a retried inbox_send call after a timeout is guaranteed not to deliver the message twice — the second call returns the original message back with duplicate: true instead of creating a new row. inbox_read's ack works the same way: acking an id that's already acked, expired, or was never addressed to you doesn't raise an error.

Source of truth

If anything here ever diverges from what the tools actually accept, the source file wins.

  • Tool definitions: packages/agent-identity-service/src/mcp/tools/inbox-tools.ts
  • Envelope and limits: packages/agent-identity-service/src/core/inbox/types.ts