Introducing Socket Firewall: Free, Proactive Protection for Your Software Supply Chain.Learn More
Socket
Book a DemoInstallSign in
Socket

@xmtp/agent-sdk

Package Overview
Dependencies
Maintainers
3
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@xmtp/agent-sdk

XMTP Agent SDK for interacting with XMTP networks

Source
npmnpm
Version
1.1.1
Version published
Weekly downloads
1.8K
-43.59%
Maintainers
3
Weekly downloads
 
Created
Source

XMTP Agent SDK

Build event‑driven, middleware‑powered messaging agents on the XMTP network. 🚀

Documentation

Full agent building guide: Build an XMTP Agent

This SDK is based on familiar Node.js patterns: you register event listeners, compose middleware, and extend behavior just like you would in frameworks such as Express. This makes it easy to bring existing JavaScript and TypeScript skills into building conversational agents.

Installation

Choose your package manager:

npm install @xmtp/agent-sdk
# or
pnpm add @xmtp/agent-sdk
# or
yarn add @xmtp/agent-sdk

Quick Start

import { Agent } from "@xmtp/agent-sdk";
import { getTestUrl } from "@xmtp/agent-sdk/debug";
import { createUser, createSigner } from "@xmtp/agent-sdk/user";

// 1. Create a local user + signer (you can plug in your own wallet signer)
const user = createUser();
const signer = createSigner(user);

// 2. Spin up the agent
const agent = await Agent.create(signer, {
  env: "dev", // or 'production'
  dbPath: null, // in-memory store; provide a path to persist
});

// 3. Respond to text messages
agent.on("text", async (ctx) => {
  await ctx.sendText("Hello from my XMTP Agent! 👋");
});

// 4. Log when we're ready
agent.on("start", (ctx) => {
  console.log(`We are online: ${getTestUrl(ctx.client)}`);
});

await agent.start();

Environment Variables

The XMTP Agent SDK allows you to use environment variables (process.env) for easier configuration without modifying code. Simply set the following variables and call Agent.createFromEnv():

Available Variables:

VariablePurposeExample
XMTP_WALLET_KEYPrivate key for walletXMTP_WALLET_KEY=0x1234...abcd
XMTP_ENVNetwork environmentXMTP_ENV=dev or XMTP_ENV=production
XMTP_DB_ENCRYPTION_KEYDatabase encryption keyXMTP_DB_ENCRYPTION_KEY=0xabcd...1234

Using the environment variables, you can setup your agent in just a few lines of code:

// Load variables from .env file
process.loadEnvFile(".env");

// Create agent using environment variables
const agent = await Agent.createFromEnv();

Agents can also recognize the following environment variables:

VariablePurposeExample
XMTP_FORCE_DEBUGActivate debugging logsXMTP_FORCE_DEBUG=true

Core Concepts

1. Event‑Driven Architecture

Subscribe only to what you need using Node’s EventEmitter interface. Events you can listen for:

Message Events

  • attachment – a new incoming remote attachment message
  • message – all incoming messages (fires for every message regardless of type)
  • reaction – a new incoming reaction message
  • reply – a new incoming reply message
  • text – a new incoming text message
  • unknownMessage – a message that doesn't match any specific type

Conversation Events

  • conversation – a new group or DM conversation
  • dm – a new DM conversation
  • group – a new group conversation

Lifecycle Events

  • start / stop – agent lifecycle events
  • unhandledError – unhandled errors

Example

// Listen to specific message types
agent.on("text", async (ctx) => {
  console.log(`Text message: ${ctx.message.content}`);
});

agent.on("reaction", async (ctx) => {
  console.log(`Reaction: ${ctx.message.content}`);
});

agent.on("reply", async (ctx) => {
  console.log(`Reply to: ${ctx.message.content.reference}`);
});

// Listen to new conversations
agent.on("dm", async (ctx) => {
  await ctx.conversation.send("Welcome to our DM!");
});

agent.on("group", async (ctx) => {
  await ctx.conversation.send("Hello group!");
});

// Listen to unhandled events
agent.on("unhandledError", (error) => {
  console.error("Agent error", error);
});

agent.on("unknownMessage", (ctx) => {
  console.error("Message type is unknown", ctx);
});

⚠️ Important: The "message" event fires for every incoming message, regardless of type. When using the "message" event, always filter message types to prevent infinite loops. Without proper filtering, your agent might respond to its own messages or react to system messages like read receipts.

Best Practice Example

import { filter } from "@xmtp/agent-sdk";

agent.on("message", async (ctx) => {
  // Filter for specific message types
  if (filter.isText(ctx.message)) {
    await ctx.conversation.send(`Echo: ${ctx.message.content}`);
  }
});

2. Middleware Support

Extend your agent with custom business logic using middlewares. Compose cross-cutting behavior like routing, telemetry, rate limiting, analytics, and feature flags, or plug in your own.

Standard Middleware

Middlewares 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, a middleware calls next() to hand off control to the next one in the chain. However, a middleware can also alter the flow in the following ways:

  • Use next() to continue the chain and pass control to the next middleware
  • Use return to stop the chain and prevent events from firing
  • Use throw to trigger the error-handling middleware chain

Example

import { Agent, AgentMiddleware, filter } from "@xmtp/agent-sdk";

const onlyText: AgentMiddleware = async (ctx, next) => {
  if (filter.isText(ctx.message)) {
    // Continue to next middleware
    await next();
  }
  // Break middleware chain
  return;
};

const agent = await Agent.createFromEnv();
agent.use(onlyText);

Error-Handling Middleware

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:

  • Use next() to mark the error as handled and continue with the main middleware chain
  • Use next(error) to forward the original (or transformed) error to the next error handler
  • Use return to end error handling and stop the middleware chain
  • Use throw to raise a new error to be caught by the error chain

Example

import { Agent, AgentErrorMiddleware } from "@xmtp/agent-sdk";

const errorHandler: AgentErrorMiddleware = async (error, ctx, next) => {
  if (error instanceof Error) {
    // Transform the error and pass it along
    await next(`Validation failed: ${error.message}`);
  } else {
    // Let other error handlers deal with it
    await next(error);
  }
};

const agent = await Agent.createFromEnv();
agent.errors.use(errorHandler);

Default Error Handler

Any error not handled by custom error middleware is caught by the default error handler and published to the unhandledError topic, where it can be observed.

Example

agent.on("unhandledError", (error) => {
  console.log("Caught error", error);
});

3. Built‑in Filters

Instead of manually checking every incoming message, you can use the provided filters.

Example

import { filter } from "@xmtp/agent-sdk";

// Using filter in message handler
agent.on("text", async (ctx) => {
  if (filter.isText(ctx.message)) {
    await ctx.conversation.send("You sent a text message!");
  }
});

// Combine multiple conditions
agent.on("text", async (ctx) => {
  if (
    filter.hasDefinedContent(ctx.message) &&
    !filter.fromSelf(ctx.message, ctx.client) &&
    filter.isText(ctx.message)
  ) {
    await ctx.conversation.send("Valid text message received ✅");
  }
});

For convenience, the filter object can also be imported as f:

// You can import either name:
import { filter, f } from "@xmtp/agent-sdk";

// Both work the same way:
if (f.isText(ctx.message)) {
  // Handle message...
}

Available Filters:

You can find all available prebuilt filters here.

4. Rich Context

Every message event handler receives a MessageContext with:

  • message – the decoded message object
  • conversation – the active conversation object
  • client – underlying XMTP client
  • Helpers like sendTextReply(), sendReaction(), getSenderAddress, and more

Example

agent.on("text", async (ctx) => {
  await ctx.sendTextReply("Reply using helper ✨");
});

5. Starting Conversations

These functionalities let you start a conversation:

// Direct Message
const dm = await agent.createDmWithAddress("0x123");
await dm.send("Hello!");

// Group Conversation
const group = await agent.createGroupWithAddresses(["0x123", "0x456"]);
await group.addMembers(["0x789"]);
await group.send("Hello group!");

Adding Custom Content Types

Pass codecs when creating your agent to extend supported content:

const agent = await Agent.create(signer, {
  env: "dev",
  dbPath: null,
  codecs: [new MyContentType()],
});

Debugging

Contributing / Feedback

We’d love your feedback: open an issue or discussion. PRs welcome for docs, examples, and core improvements.

Build something delightful. Then tell us what you wish was easier.

Happy hacking 💫

Keywords

agents

FAQs

Package last updated on 01 Oct 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts