
Security News
Google’s OSV Fix Just Added 500+ New Advisories — All Thanks to One Small Policy Change
A data handling bug in OSV.dev caused disputed CVEs to disappear from vulnerability feeds until a recent fix restored over 500 advisories.
@xmtp/agent-sdk
Advanced tools
Build event‑driven, middleware‑powered messaging agents on the XMTP network. 🚀
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.
Choose your package manager:
npm install @xmtp/agent-sdk
# or
pnpm add @xmtp/agent-sdk
# or
yarn add @xmtp/agent-sdk
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();
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:
Variable | Purpose | Example |
---|---|---|
XMTP_WALLET_KEY | Private key for wallet | XMTP_WALLET_KEY=0x1234...abcd |
XMTP_ENV | Network environment | XMTP_ENV=dev or XMTP_ENV=production |
XMTP_DB_ENCRYPTION_KEY | Database encryption key | XMTP_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:
Variable | Purpose | Example |
---|---|---|
XMTP_FORCE_DEBUG | Activate debugging logs | XMTP_FORCE_DEBUG=true |
Subscribe only to what you need using Node’s EventEmitter
interface. Events you can listen for:
Message Events
attachment
– a new incoming remote attachment messagemessage
– all incoming messages (fires for every message regardless of type)reaction
– a new incoming reaction messagereply
– a new incoming reply messagetext
– a new incoming text messageunknownMessage
– a message that doesn't match any specific typeConversation Events
conversation
– a new group or DM conversationdm
– a new DM conversationgroup
– a new group conversationLifecycle Events
start
/ stop
– agent lifecycle eventsunhandledError
– unhandled errorsExample
// 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}`);
}
});
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.
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:
next()
to continue the chain and pass control to the next middlewarereturn
to stop the chain and prevent events from firingthrow
to trigger the error-handling middleware chainExample
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 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
:
next()
to mark the error as handled and continue with the main middleware chainnext(error)
to forward the original (or transformed) error to the next error handlerreturn
to end error handling and stop the middleware chainthrow
to raise a new error to be caught by the error chainExample
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);
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);
});
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.
Every message event handler receives a MessageContext
with:
message
– the decoded message objectconversation
– the active conversation objectclient
– underlying XMTP clientsendTextReply()
, sendReaction()
, getSenderAddress
, and moreExample
agent.on("text", async (ctx) => {
await ctx.sendTextReply("Reply using helper ✨");
});
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!");
Pass codecs
when creating your agent to extend supported content:
const agent = await Agent.create(signer, {
env: "dev",
dbPath: null,
codecs: [new MyContentType()],
});
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 💫
FAQs
XMTP Agent SDK for interacting with XMTP networks
The npm package @xmtp/agent-sdk receives a total of 1,684 weekly downloads. As such, @xmtp/agent-sdk popularity was classified as popular.
We found that @xmtp/agent-sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
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.
Security News
A data handling bug in OSV.dev caused disputed CVEs to disappear from vulnerability feeds until a recent fix restored over 500 advisories.
Research
/Security News
175 malicious npm packages (26k+ downloads) used unpkg CDN to host redirect scripts for a credential-phishing campaign targeting 135+ organizations worldwide.
Security News
Python 3.14 adds template strings, deferred annotations, and subinterpreters, plus free-threaded mode, an experimental JIT, and Sigstore verification.