AI Tools
Declare custom plugin tools and understand the host integration needed to expose them to agents.
Plugins can declare tools in the tool hook returned by init(). The current open-wa MCP endpoint does not discover or invoke plugin-declared tools, so use these examples only when you are building a host integration that connects the hook to an agent.
Declare a tool
Each tool has a name, description, argument schema, and execution function. The host integration decides whether and how agents can discover and call it.
init: async ({ client }) => ({
tool: {
toolName: {
description: 'What this tool does',
args: {
argName: z.string().describe('Description of the argument'),
},
execute: async (args, context) => {
// Tool implementation
return 'result';
},
},
},
})Fields
- description: What the tool does. AI agents use this to decide when to call the tool.
- args: Zod schema defining the arguments the tool accepts.
- execute: The function that runs when the tool is called.
ToolContext
The execute function receives a ToolContext with these guaranteed fields:
execute: async (args, context) => {
context.logger.info('Tool called', { sessionId: context.sessionId });
if (context.abort.aborted) {
return 'Tool cancelled before work started.';
}
return `Tool ran for session ${context.sessionId}`;
}sessionId: current session IDlogger: scoped plugin loggerabort:AbortSignalfor cancellation
The tool context does not include client. If a tool needs to call WhatsApp methods, capture client from the plugin init() input and use it inside the returned tool handlers.
Example: Lookup Contact Tool
export default createPlugin({
meta: { name: 'contact-tools' },
init: async ({ client }) => ({
tool: {
lookupContact: {
description: 'Look up a contact by phone number',
args: {
phone: z.string().describe('Phone number to look up'),
},
execute: async ({ phone }, context) => {
if (typeof phone !== 'string') {
return JSON.stringify({ success: false, error: 'phone must be a string' });
}
const chatId = `${phone}@c.us`;
const contact = await client.getContact(chatId);
context.logger.info('Contact looked up', { phone });
return JSON.stringify({ success: true, contact });
},
},
},
}),
});Example: Send Message Tool
init: async ({ client }) => ({
tool: {
sendMessage: {
description: 'Send a WhatsApp message to a contact',
args: {
phone: z.string().describe('Phone number to send to'),
message: z.string().describe('Message text'),
},
execute: async ({ phone, message }, context) => {
if (typeof phone !== 'string' || typeof message !== 'string') {
return JSON.stringify({ success: false, error: 'phone and message must be strings' });
}
const chatId = `${phone}@c.us`;
const messageId = await client.sendText(chatId, message);
context.logger.info('Message sent', { phone, messageId });
return JSON.stringify({ success: true, messageId });
},
},
},
})Write a useful tool contract
Descriptive Names
Use a name that says what the action does, such as lookupContact or sendMessage.
Clear Descriptions
Describe when to use the tool and what it returns:
description: 'Look up a contact by phone number. Returns the contact name and profile info.'Argument Descriptions
Describe each argument, including its expected format:
args: {
phone: z.string().describe('Phone number with country code, e.g. 1234567890'),
}Error Handling
Return useful errors to the caller and log the failure:
execute: async (args, context) => {
try {
if (typeof args.chatId !== 'string' || typeof args.message !== 'string') {
return JSON.stringify({ success: false, error: 'chatId and message must be strings' });
}
const messageId = await client.sendText(args.chatId, args.message);
return JSON.stringify({ success: true, messageId });
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
context.logger.error('Failed to send message', { error: message });
return JSON.stringify({ success: false, error: message });
}
}Return Structured Data
Tool handlers return strings. If you want to return structured data to an agent, serialize a JSON object:
return JSON.stringify({
success: true,
data: { name: 'John', phone: '1234567890' },
});Related
- Plugin getting started - Build your first plugin
- MCP Integration - AI agent integration
- Hooks reference - Available hooks
Was this helpful?
Your answer includes the page path and docs version.
