Skip to content

Agent middleware

Use middleware for routing, metrics, rate limits, and other message handling. Middleware runs before content and message events.

Middleware can be registered with agent.use either one at a time or as an array. They are executed in the order they were added.

Middleware functions receive a ctx (context) object and a next function. Normally, middleware calls next() to hand off control to the next one in the chain. However, middleware can also alter the flow in the following ways:

ActionResult
await next()Continue the main chain
returnAccept this message and stop the chain without an event
throw errorStart the error middleware chain
import type {
type AgentMiddleware<ContentTypes = unknown> = (ctx: MessageContext<unknown, ContentTypes>, next: () => Promise<void> | void) => Promise<void>

Processes a message and calls next to continue the middleware chain.

AgentMiddleware
} from "@xmtp/agent-sdk";
const
const ignoreSelf: AgentMiddleware
ignoreSelf
:
type AgentMiddleware<ContentTypes = unknown> = (ctx: MessageContext<unknown, ContentTypes>, next: () => Promise<void> | void) => Promise<void>

Processes a message and calls next to continue the middleware chain.

AgentMiddleware
= async (
ctx: MessageContext<unknown, unknown>
ctx
,
next: () => Promise<void> | void
next
) => {
if (
ctx: MessageContext<unknown, unknown>
ctx
.
MessageContext<unknown, unknown>.message: Message

The SDK message, including its tagged content and retained bytes.

message
.
MessageFields.senderInboxId: string
senderInboxId
===
ctx: MessageContext<unknown, unknown>
ctx
.
ClientContext<unknown>.client: Client

Return the wrapped XMTP client.

client
.
ClientMembers.inboxId: string
inboxId
) return;
await
next: () => Promise<void> | void
next
();
};
const agent: Agent<unknown>
agent
.
Agent<unknown>.use(...middleware: (AgentMiddleware<unknown> | AgentMiddleware<unknown>[])[]): Agent<unknown>

Add message middleware. Middleware runs in registration order.

use
(
const ignoreSelf: AgentMiddleware
ignoreSelf
);
await
const agent: Agent<unknown>
agent
.
Agent<unknown>.start(options?: AgentStreamingOptions): Promise<void>

Start conversation and message streams. Calling this while running is a no-op.

start
();

Register error middleware with agent.errors.use().

Error middleware can be registered with agent.errors.use either one at a time or as an array. They are executed in the order they were added.

Error middleware receives the error, ctx, and a next function. Just like regular middleware, the flow in error middleware depends on how to use next:

ActionResult
await next()Mark the error handled and continue the main chain
await next(error)Send an error to the next error handler
returnStop the current reader without acknowledging the failed message
throw errorSend a new error through the error chain

The error context always has a client. Its message and conversation can be absent. Check them before use.

For a message error, next() accepts recovery and lets processing continue. Return without next() to reject acceptance and stop the reader. The failed message can be delivered again when you open a new default reader. Keep external actions safe if a message is delivered more than once.

A terminal stream error closes both readers before error middleware runs. next() handles that error but does not open replacement readers. Call agent.start() explicitly when your recovery policy permits it.