Find Messages
find_messagesRetrieve WhatsApp messages for a specific phone number or group JID. Supports pagination with limit and offset parameters.
Instructions
Find messages by remoteJid (phone number or group JID) for the pinned instance. Returns normalized { id, fromMe, remoteJid, timestamp, type, text, mediaKey?, quotedMessageId? } — raw payload dropped to prevent overflow.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| remoteJid | Yes | WhatsApp JID (e.g. 5511999999999@s.whatsapp.net or group@g.us) | |
| limit | No | Max messages to return (default 50, max 200). | |
| offset | No | Skip first N messages (default 0). |
Implementation Reference
- src/tools/find-messages.ts:27-71 (handler)The main handler for the 'find_messages' tool. Registers the tool with server, defines the async handler that calls /chat/findMessages/{instanceName}, normalizes results, and handles errors.
export function registerFindMessages(server: McpServer, client: EvolutionClient): void { server.registerTool( "find_messages", { title: "Find Messages", description: "Find messages by remoteJid (phone number or group JID) for the pinned instance. " + "Returns normalized { id, fromMe, remoteJid, timestamp, type, text, mediaKey?, quotedMessageId? } — raw payload dropped to prevent overflow.", inputSchema: schema, }, async (args) => { try { const limit = args.limit ?? 50; const offset = args.offset ?? 0; const payload = { where: { key: { remoteJid: args.remoteJid } }, limit, offset, }; const data = await client.post(`/chat/findMessages/${client.instanceName}`, payload); // Evolution may return { messages: [...] } or a bare array const rawArr: unknown[] = Array.isArray(data) ? data : Array.isArray((data as { messages?: unknown[] }).messages) ? (data as { messages: unknown[] }).messages : []; // Client-side offset/limit safety net const sliced = rawArr.slice(offset, offset + limit); const normalized = sliced.map((m) => normalizeMessage(m as Parameters<typeof normalizeMessage>[0])); return { content: [{ type: "text" as const, text: JSON.stringify(normalized, null, 2) }], }; } catch (e) { if (e instanceof McpError) { return { isError: true, content: [{ type: "text" as const, text: e.message }] }; } throw e; } } ); } - src/tools/find-messages.ts:8-25 (schema)Input schema for find_messages: requires remoteJid (JidSchema), optional limit (1-200, default 50), optional offset (min 0, default 0).
const schema = { remoteJid: JidSchema, limit: z .number() .int() .min(1) .max(200) .default(50) .optional() .describe("Max messages to return (default 50, max 200)."), offset: z .number() .int() .min(0) .default(0) .optional() .describe("Skip first N messages (default 0)."), }; - src/tools/index.ts:8-8 (registration)Import of registerFindMessages from find-messages.ts in the tools index.
import { registerFindMessages } from "./find-messages.js"; - src/tools/index.ts:81-81 (registration)Registration call: registerFindMessages is invoked in registerAllTools to wire the tool to the MCP server.
registerFindMessages(server, client); - src/util/normalize.ts:54-90 (helper)The normalizeMessage helper used by find_messages to compact raw Evolution message objects into a bounded NormalizedMessage shape (id, fromMe, remoteJid, timestamp, type, text, optional mediaKey and quotedMessageId).
export function normalizeMessage(raw: RawMessage): NormalizedMessage { const key = raw.key ?? {}; const msg = raw.message ?? {}; // Detect media types by checking which message type key exists const MEDIA_TYPES = new Set(["imageMessage", "videoMessage", "audioMessage", "documentMessage", "stickerMessage"]); const type = Object.keys(msg).find((k) => k !== "contextInfo") ?? "unknown"; const isMedia = MEDIA_TYPES.has(type); // Extract text from whichever message field is present const msgObj = msg[type] as Record<string, unknown> | undefined; const text: string | null = (msg.conversation as string | undefined) ?? (msg.extendedTextMessage?.text as string | undefined) ?? ((msgObj as { caption?: string } | undefined)?.caption) ?? null; // Quoted message ID lives in contextInfo.stanzaId const contextInfo = (msgObj as { contextInfo?: { stanzaId?: string } } | undefined)?.contextInfo ?? ((msg.contextInfo as { stanzaId?: string } | undefined)); const quotedMessageId = contextInfo?.stanzaId; const result: NormalizedMessage = { id: key.id ?? "", fromMe: key.fromMe ?? false, remoteJid: key.remoteJid ?? "", timestamp: raw.messageTimestamp ?? 0, type, text, }; // Only attach mediaKey when this is a media message — id doubles as download handle if (isMedia && key.id) result.mediaKey = key.id; if (quotedMessageId) result.quotedMessageId = quotedMessageId; return result; }