Context and helpers
Every XMTP agent event handler receives a rich MessageContext
object that provides access to the message, conversation, client, and powerful helper methods. This context makes it easy to build responsive agents without repetitive boilerplate code.
MessageContext overview
The MessageContext
object contains everything you need to handle messages effectively:
interface MessageContext {
message: DecodedMessage; // The decoded message object
conversation: Conversation; // The active conversation
client: Client; // The underlying XMTP client
// Helper methods
sendTextReply(text: string): Promise<void>;
sendReaction(emoji: string): Promise<void>;
getSenderAddress(): string;
// ... and more
}
Message object
The message
property contains the decoded message with all its metadata:
agent.on('text', async (ctx) => {
const message = ctx.message;
console.log('Content:', message.content);
console.log('Sender:', message.senderInboxId);
console.log('Sent at:', message.sentAt);
console.log('Message ID:', message.id);
console.log('Conversation ID:', message.conversationId);
console.log('Content type:', message.contentType);
});
Access different content types
// Text messages
agent.on('text', async (ctx) => {
const textContent: string = ctx.message.content;
console.log('User said:', textContent);
});
// Reactions
agent.on('reaction', async (ctx) => {
const reaction = ctx.message.content;
console.log('Reaction:', reaction.content);
console.log('Reference:', reaction.reference);
});
// Replies
agent.on('reply', async (ctx) => {
const reply = ctx.message.content;
console.log('Reply text:', reply.content);
console.log('Original message:', reply.reference);
});
Conversation object
The conversation
property provides access to the current conversation and its methods:
agent.on('text', async (ctx) => {
const conversation = ctx.conversation;
console.log('Conversation ID:', conversation.id);
// Send a message
await ctx.sendText('Hello from the conversation!');
// Get conversation members
const members = await ctx.conversation.members();
console.log('Member count:', members.length);
// Check conversation type
if (conversation.peerInboxId) {
console.log('This is a DM with:', conversation.peerInboxId);
} else {
console.log('This is a group conversation');
}
});
Client object
The client
property gives you access to the underlying XMTP client:
agent.on('text', async (ctx) => {
const client = ctx.client;
console.log('Agent inbox ID:', client.inboxId);
console.log('Installation ID:', client.installationId);
// Access conversations manager
const conversations = client.conversations;
// Create new conversations
const newDM = await client.conversations.newDm('target-inbox-id');
const newGroup = await client.conversations.newGroup(['inbox1', 'inbox2'], {
groupName: 'New Group',
});
});
Helper methods
The context provides convenient helper methods for common operations:
sendTextReply()
Send a text reply in one line:
agent.on('text', async (ctx) => {
await ctx.sendTextReply('Thanks for your message!');
// Equivalent to:
// await ctx.sendText("Thanks for your message!");
});
sendReaction()
Add reactions to messages easily:
agent.on('text', async (ctx) => {
const content = ctx.message.content.toLowerCase();
if (content.includes('good')) {
await ctx.sendReaction('👍');
} else if (content.includes('bad')) {
await ctx.sendReaction('👎');
} else {
await ctx.sendReaction('🤔');
}
});
getSenderAddress()
Get the Ethereum address of the message sender:
agent.on('text', async (ctx) => {
const senderAddress = ctx.getSenderAddress();
console.log('Message from:', senderAddress);
// Use for authorization
if (senderAddress === '0x1234...') {
await ctx.sendTextReply('Hello, admin!');
}
});
Advanced context usage
Member information
Get detailed information about conversation members:
agent.on('text', async (ctx) => {
const members = await ctx.conversation.members();
for (const member of members) {
console.log('Member inbox ID:', member.inboxId);
console.log('Permission level:', member.permissionLevel);
console.log('Consent state:', member.consentState);
// Get Ethereum address
const ethIdentifier = member.accountIdentifiers.find(
(id) => id.identifierKind === IdentifierKind.Ethereum
);
if (ethIdentifier) {
console.log('Ethereum address:', ethIdentifier.identifier);
}
}
});
Message history
Access conversation message history:
agent.on('text', async (ctx) => {
// Get all messages
const messages = await ctx.conversation.messages();
console.log('Total messages:', messages.length);
// Get recent messages
const recentMessages = messages.slice(-10); // Last 10 messages
for (const msg of recentMessages) {
console.log(`${msg.senderInboxId}: ${msg.content}`);
}
});
Custom context extensions
You can extend the context in middleware:
const databaseMiddleware: AgentMiddleware = async (ctx, next) => {
// Add database connection to context
(ctx as any).db = await getDatabaseConnection();
try {
await next();
} finally {
await (ctx as any).db.close();
}
};
agent.use(databaseMiddleware);
agent.on('text', async (ctx: any) => {
// Use the extended context
const user = await ctx.db.findUser(ctx.getSenderAddress());
await ctx.sendTextReply(`Hello ${user.name}!`);
});
Error handling with context
Use context information for better error handling:
agent.on('text', async (ctx) => {
try {
await processMessage(ctx);
} catch (error) {
console.error('Error processing message:', {
messageId: ctx.message.id,
conversationId: ctx.conversation.id,
sender: ctx.getSenderAddress(),
error: error.message,
});
await ctx.sendTextReply(
'Sorry, I encountered an error processing your message.'
);
}
});
Context in different event types
New conversation context
agent.on('dm', async (ctx) => {
// Context for new DM conversations
console.log('New DM started');
console.log('Peer:', ctx.conversation.peerInboxId);
await ctx.sendTextReply('Welcome to our DM! How can I help?');
});
agent.on('group', async (ctx) => {
// Context for new group conversations
console.log('Added to group:', ctx.conversation.name);
const members = await ctx.conversation.members();
await ctx.sendTextReply(
`Hello everyone! I see there are ${members.length} members here.`
);
});
Unknown message context
agent.on('unknownMessage', async (ctx) => {
console.log('Unknown content type:', ctx.message.contentType);
await ctx.sendTextReply("I received a message type I don't understand yet.");
});