Get Chat History
get_chat_historyRetrieve chat history for a WhatsApp contact or group by JID. Returns normalized messages with sender, timestamp, and content.
Instructions
Get chat history for a specific contact or group JID. 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/get-chat-history.ts:37-71 (handler)The handler function that executes the get_chat_history tool logic. Calls Evolution API at /chat/findMessages/{instance} with remoteJid filter, then normalizes and returns messages as JSON.
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/get-chat-history.ts:8-25 (schema)Input schema for get_chat_history: remoteJid (required, WhatsApp JID), limit (optional number 1-200, default 50), offset (optional number, 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/get-chat-history.ts:27-71 (registration)Registration function registerGetChatHistory that calls server.registerTool('get_chat_history', ...) with schema and handler.
export function registerGetChatHistory(server: McpServer, client: EvolutionClient): void { server.registerTool( "get_chat_history", { title: "Get Chat History", description: "Get chat history for a specific contact or group JID. " + "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/index.ts:82-82 (registration)Registration call in the central registerAllTools function that wires up registerGetChatHistory(server, client).
registerGetChatHistory(server, client); - src/util/normalize.ts:54-90 (helper)The normalizeMessage utility function that extracts id, fromMe, remoteJid, timestamp, type, text, mediaKey, and quotedMessageId from raw Evolution API message objects.
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; }