@goforgeit/mcp-imessage 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,6 +6,8 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
6
6
  import { z } from "zod";
7
7
  import { execFile } from "child_process";
8
8
  import { promisify } from "util";
9
+ import { realpathSync } from "fs";
10
+ import { fileURLToPath } from "url";
9
11
  var execFileAsync = promisify(execFile);
10
12
  async function runAppleScript(script) {
11
13
  const { stdout } = await execFileAsync("osascript", ["-e", script]);
@@ -304,7 +306,13 @@ function createIMessageMCPServer() {
304
306
  }
305
307
  };
306
308
  }
307
- var isMainModule = process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\/g, "/"));
309
+ var isMainModule = (() => {
310
+ try {
311
+ return process.argv[1] && fileURLToPath(import.meta.url) === realpathSync(process.argv[1]);
312
+ } catch {
313
+ return false;
314
+ }
315
+ })();
308
316
  if (isMainModule || process.env.FORGE_MCP_START === "true") {
309
317
  const mcpServer = createIMessageMCPServer();
310
318
  mcpServer.start().catch((err) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @goforgeit/mcp-imessage\n *\n * Forge MCP server for iMessage on macOS.\n * Reads messages from ~/Library/Messages/chat.db via sqlite3 CLI,\n * searches contacts and sends messages via AppleScript.\n */\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface IMessageMCPServer {\n server: McpServer;\n getRegisteredTools(): string[];\n start(): Promise<void>;\n}\n\nexport async function runAppleScript(script: string): Promise<string> {\n const { stdout } = await execFileAsync('osascript', ['-e', script]);\n return stdout.trim();\n}\n\nexport async function runSqlite(dbPath: string, query: string): Promise<string> {\n const { stdout } = await execFileAsync('sqlite3', ['-json', dbPath, query]);\n return stdout.trim();\n}\n\nexport function normalizePhoneNumber(phone: string): string[] {\n const cleaned = phone.replace(/[^0-9+]/g, '');\n\n if (/^\\+1\\d{10}$/.test(cleaned)) return [cleaned];\n if (/^1\\d{10}$/.test(cleaned)) return [`+${cleaned}`];\n if (/^\\d{10}$/.test(cleaned)) return [`+1${cleaned}`];\n\n const formats = new Set<string>();\n if (cleaned.startsWith('+1')) {\n formats.add(cleaned);\n } else if (cleaned.startsWith('1')) {\n formats.add(`+${cleaned}`);\n } else {\n formats.add(`+1${cleaned}`);\n }\n return Array.from(formats);\n}\n\nfunction getDbPath(): string {\n return `${process.env.HOME}/Library/Messages/chat.db`;\n}\n\nexport function createIMessageMCPServer(): IMessageMCPServer {\n const server = new McpServer({\n name: '@goforgeit/mcp-imessage',\n version: '0.1.0',\n });\n\n const registeredTools: string[] = [];\n\n // --- search_contacts ---\n server.tool(\n 'search_contacts',\n 'Search contacts by name, phone number, or email address',\n {\n query: z.string().describe('Search query (name, phone, or email)'),\n },\n async ({ query }) => {\n const script = `\n tell application \"Contacts\"\n set output to \"[\"\n set isFirst to true\n repeat with p in every person\n if ((name of p as text) contains ${JSON.stringify(query)}) then\n if not isFirst then\n set output to output & \",\"\n end if\n set output to output & \"{\"\n set output to output & \"\\\\\"name\\\\\":\\\\\"\" & (name of p as text) & \"\\\\\",\"\n set output to output & \"\\\\\"phones\\\\\":[\"\n set firstPhone to true\n repeat with ph in phones of p\n if not firstPhone then\n set output to output & \",\"\n end if\n set output to output & \"\\\\\"\" & (value of ph) & \"\\\\\"\"\n set firstPhone to false\n end repeat\n set output to output & \"],\"\n set output to output & \"\\\\\"emails\\\\\":[\"\n set firstEmail to true\n repeat with em in emails of p\n if not firstEmail then\n set output to output & \",\"\n end if\n set output to output & \"\\\\\"\" & (value of em) & \"\\\\\"\"\n set firstEmail to false\n end repeat\n set output to output & \"]\"\n set output to output & \"}\"\n set isFirst to false\n end if\n end repeat\n return output & \"]\"\n end tell\n `;\n try {\n const results = await runAppleScript(script);\n return { content: [{ type: 'text' as const, text: results }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Search failed: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('search_contacts');\n\n // --- list_conversations ---\n server.tool(\n 'list_conversations',\n 'List recent iMessage conversations with contact info and last message preview',\n {\n limit: z.number().optional().default(20).describe('Maximum conversations to return'),\n },\n async ({ limit }) => {\n const dbPath = getDbPath();\n const query = `\n SELECT\n c.ROWID as chat_id,\n c.display_name,\n c.chat_identifier,\n h.id as handle_id,\n (SELECT m2.text FROM message m2\n JOIN chat_message_join cmj2 ON cmj2.message_id = m2.ROWID\n WHERE cmj2.chat_id = c.ROWID\n ORDER BY m2.date DESC LIMIT 1) as last_message,\n (SELECT datetime(m3.date/1000000000 + strftime('%s', '2001-01-01'), 'unixepoch', 'localtime')\n FROM message m3\n JOIN chat_message_join cmj3 ON cmj3.message_id = m3.ROWID\n WHERE cmj3.chat_id = c.ROWID\n ORDER BY m3.date DESC LIMIT 1) as last_date,\n c.service_name\n FROM chat c\n LEFT JOIN chat_handle_join chj ON chj.chat_id = c.ROWID\n LEFT JOIN handle h ON h.ROWID = chj.handle_id\n GROUP BY c.ROWID\n ORDER BY last_date DESC\n LIMIT ${limit}\n `;\n\n try {\n const stdout = await runSqlite(dbPath, query);\n if (!stdout) {\n return {\n content: [{ type: 'text' as const, text: 'No conversations found.' }],\n };\n }\n const conversations = JSON.parse(stdout);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(conversations, null, 2) }],\n };\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Failed to list conversations: ${msg}` }],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('list_conversations');\n\n // --- read_imessages ---\n server.tool(\n 'read_imessages',\n 'Read recent iMessages from a specific contact by phone number or email',\n {\n phone_number: z.string().describe('Phone number or email of the contact'),\n limit: z.number().optional().default(10).describe('Maximum messages to retrieve'),\n },\n async ({ phone_number, limit }) => {\n const dbPath = getDbPath();\n const phoneFormats = normalizePhoneNumber(phone_number);\n const placeholders = phoneFormats.map((f) => `'${f}'`).join(', ');\n\n const query = `\n SELECT\n m.ROWID as message_id,\n CASE\n WHEN m.text IS NOT NULL AND m.text != '' THEN m.text\n ELSE '[No text content]'\n END as content,\n datetime(m.date/1000000000 + strftime('%s', '2001-01-01'), 'unixepoch', 'localtime') as date,\n h.id as sender,\n m.is_from_me,\n m.cache_has_attachments\n FROM message m\n INNER JOIN handle h ON h.ROWID = m.handle_id\n WHERE h.id IN (${placeholders})\n AND (m.text IS NOT NULL OR m.cache_has_attachments = 1)\n AND m.item_type = 0\n AND m.is_audio_message = 0\n ORDER BY m.date DESC\n LIMIT ${limit}\n `;\n\n try {\n const stdout = await runSqlite(dbPath, query);\n if (!stdout) {\n return {\n content: [{ type: 'text' as const, text: 'No messages found for this contact.' }],\n };\n }\n const messages = JSON.parse(stdout);\n return { content: [{ type: 'text' as const, text: JSON.stringify(messages, null, 2) }] };\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n if (msg.includes('unable to open database') || msg.includes('permission denied')) {\n return {\n content: [\n {\n type: 'text' as const,\n text:\n 'Cannot access Messages database. Grant Full Disk Access to your terminal/IDE:\\n' +\n '1. Open System Settings > Privacy & Security > Full Disk Access\\n' +\n '2. Enable your terminal app\\n' +\n '3. Restart the terminal',\n },\n ],\n };\n }\n return {\n content: [{ type: 'text' as const, text: `Failed to read messages: ${msg}` }],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('read_imessages');\n\n // --- get_unread_imessages ---\n server.tool(\n 'get_unread_imessages',\n 'Get all unread iMessages across all conversations',\n {\n limit: z.number().optional().default(10).describe('Maximum unread messages to retrieve'),\n },\n async ({ limit }) => {\n const dbPath = getDbPath();\n const query = `\n SELECT\n m.ROWID as message_id,\n CASE\n WHEN m.text IS NOT NULL AND m.text != '' THEN m.text\n ELSE '[No text content]'\n END as content,\n datetime(m.date/1000000000 + strftime('%s', '2001-01-01'), 'unixepoch', 'localtime') as date,\n h.id as sender,\n m.is_from_me,\n m.cache_has_attachments\n FROM message m\n INNER JOIN handle h ON h.ROWID = m.handle_id\n WHERE m.is_from_me = 0\n AND m.is_read = 0\n AND (m.text IS NOT NULL OR m.cache_has_attachments = 1)\n AND m.is_audio_message = 0\n AND m.item_type = 0\n ORDER BY m.date DESC\n LIMIT ${limit}\n `;\n\n try {\n const stdout = await runSqlite(dbPath, query);\n if (!stdout) {\n return {\n content: [{ type: 'text' as const, text: 'No unread messages.' }],\n };\n }\n const messages = JSON.parse(stdout);\n return { content: [{ type: 'text' as const, text: JSON.stringify(messages, null, 2) }] };\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Failed to get unread messages: ${msg}` }],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('get_unread_imessages');\n\n // --- send_imessage ---\n server.tool(\n 'send_imessage',\n 'Send an iMessage to a contact via the macOS Messages app',\n {\n recipient: z.string().describe('Phone number or email of the recipient'),\n message: z.string().describe('Message content to send'),\n },\n async ({ recipient, message }) => {\n const script = `\n tell application \"Messages\"\n send ${JSON.stringify(message)} to buddy ${JSON.stringify(recipient)} of (service 1 whose service type = iMessage)\n end tell\n `;\n try {\n await runAppleScript(script);\n return {\n content: [{ type: 'text' as const, text: `Message sent successfully to ${recipient}` }],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to send message: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('send_imessage');\n\n return {\n server,\n getRegisteredTools() {\n return [...registeredTools];\n },\n async start() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n },\n };\n}\n\n// CLI entry point\nconst isMainModule =\n process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\\\/g, '/'));\n\nif (isMainModule || process.env.FORGE_MCP_START === 'true') {\n const mcpServer = createIMessageMCPServer();\n mcpServer.start().catch((err) => {\n console.error('Failed to start iMessage MCP server:', err);\n process.exit(1);\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @goforgeit/mcp-imessage\n *\n * Forge MCP server for iMessage on macOS.\n * Reads messages from ~/Library/Messages/chat.db via sqlite3 CLI,\n * searches contacts and sends messages via AppleScript.\n */\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface IMessageMCPServer {\n server: McpServer;\n getRegisteredTools(): string[];\n start(): Promise<void>;\n}\n\nexport async function runAppleScript(script: string): Promise<string> {\n const { stdout } = await execFileAsync('osascript', ['-e', script]);\n return stdout.trim();\n}\n\nexport async function runSqlite(dbPath: string, query: string): Promise<string> {\n const { stdout } = await execFileAsync('sqlite3', ['-json', dbPath, query]);\n return stdout.trim();\n}\n\nexport function normalizePhoneNumber(phone: string): string[] {\n const cleaned = phone.replace(/[^0-9+]/g, '');\n\n if (/^\\+1\\d{10}$/.test(cleaned)) return [cleaned];\n if (/^1\\d{10}$/.test(cleaned)) return [`+${cleaned}`];\n if (/^\\d{10}$/.test(cleaned)) return [`+1${cleaned}`];\n\n const formats = new Set<string>();\n if (cleaned.startsWith('+1')) {\n formats.add(cleaned);\n } else if (cleaned.startsWith('1')) {\n formats.add(`+${cleaned}`);\n } else {\n formats.add(`+1${cleaned}`);\n }\n return Array.from(formats);\n}\n\nfunction getDbPath(): string {\n return `${process.env.HOME}/Library/Messages/chat.db`;\n}\n\nexport function createIMessageMCPServer(): IMessageMCPServer {\n const server = new McpServer({\n name: '@goforgeit/mcp-imessage',\n version: '0.1.0',\n });\n\n const registeredTools: string[] = [];\n\n // --- search_contacts ---\n server.tool(\n 'search_contacts',\n 'Search contacts by name, phone number, or email address',\n {\n query: z.string().describe('Search query (name, phone, or email)'),\n },\n async ({ query }) => {\n const script = `\n tell application \"Contacts\"\n set output to \"[\"\n set isFirst to true\n repeat with p in every person\n if ((name of p as text) contains ${JSON.stringify(query)}) then\n if not isFirst then\n set output to output & \",\"\n end if\n set output to output & \"{\"\n set output to output & \"\\\\\"name\\\\\":\\\\\"\" & (name of p as text) & \"\\\\\",\"\n set output to output & \"\\\\\"phones\\\\\":[\"\n set firstPhone to true\n repeat with ph in phones of p\n if not firstPhone then\n set output to output & \",\"\n end if\n set output to output & \"\\\\\"\" & (value of ph) & \"\\\\\"\"\n set firstPhone to false\n end repeat\n set output to output & \"],\"\n set output to output & \"\\\\\"emails\\\\\":[\"\n set firstEmail to true\n repeat with em in emails of p\n if not firstEmail then\n set output to output & \",\"\n end if\n set output to output & \"\\\\\"\" & (value of em) & \"\\\\\"\"\n set firstEmail to false\n end repeat\n set output to output & \"]\"\n set output to output & \"}\"\n set isFirst to false\n end if\n end repeat\n return output & \"]\"\n end tell\n `;\n try {\n const results = await runAppleScript(script);\n return { content: [{ type: 'text' as const, text: results }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Search failed: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('search_contacts');\n\n // --- list_conversations ---\n server.tool(\n 'list_conversations',\n 'List recent iMessage conversations with contact info and last message preview',\n {\n limit: z.number().optional().default(20).describe('Maximum conversations to return'),\n },\n async ({ limit }) => {\n const dbPath = getDbPath();\n const query = `\n SELECT\n c.ROWID as chat_id,\n c.display_name,\n c.chat_identifier,\n h.id as handle_id,\n (SELECT m2.text FROM message m2\n JOIN chat_message_join cmj2 ON cmj2.message_id = m2.ROWID\n WHERE cmj2.chat_id = c.ROWID\n ORDER BY m2.date DESC LIMIT 1) as last_message,\n (SELECT datetime(m3.date/1000000000 + strftime('%s', '2001-01-01'), 'unixepoch', 'localtime')\n FROM message m3\n JOIN chat_message_join cmj3 ON cmj3.message_id = m3.ROWID\n WHERE cmj3.chat_id = c.ROWID\n ORDER BY m3.date DESC LIMIT 1) as last_date,\n c.service_name\n FROM chat c\n LEFT JOIN chat_handle_join chj ON chj.chat_id = c.ROWID\n LEFT JOIN handle h ON h.ROWID = chj.handle_id\n GROUP BY c.ROWID\n ORDER BY last_date DESC\n LIMIT ${limit}\n `;\n\n try {\n const stdout = await runSqlite(dbPath, query);\n if (!stdout) {\n return {\n content: [{ type: 'text' as const, text: 'No conversations found.' }],\n };\n }\n const conversations = JSON.parse(stdout);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(conversations, null, 2) }],\n };\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Failed to list conversations: ${msg}` }],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('list_conversations');\n\n // --- read_imessages ---\n server.tool(\n 'read_imessages',\n 'Read recent iMessages from a specific contact by phone number or email',\n {\n phone_number: z.string().describe('Phone number or email of the contact'),\n limit: z.number().optional().default(10).describe('Maximum messages to retrieve'),\n },\n async ({ phone_number, limit }) => {\n const dbPath = getDbPath();\n const phoneFormats = normalizePhoneNumber(phone_number);\n const placeholders = phoneFormats.map((f) => `'${f}'`).join(', ');\n\n const query = `\n SELECT\n m.ROWID as message_id,\n CASE\n WHEN m.text IS NOT NULL AND m.text != '' THEN m.text\n ELSE '[No text content]'\n END as content,\n datetime(m.date/1000000000 + strftime('%s', '2001-01-01'), 'unixepoch', 'localtime') as date,\n h.id as sender,\n m.is_from_me,\n m.cache_has_attachments\n FROM message m\n INNER JOIN handle h ON h.ROWID = m.handle_id\n WHERE h.id IN (${placeholders})\n AND (m.text IS NOT NULL OR m.cache_has_attachments = 1)\n AND m.item_type = 0\n AND m.is_audio_message = 0\n ORDER BY m.date DESC\n LIMIT ${limit}\n `;\n\n try {\n const stdout = await runSqlite(dbPath, query);\n if (!stdout) {\n return {\n content: [{ type: 'text' as const, text: 'No messages found for this contact.' }],\n };\n }\n const messages = JSON.parse(stdout);\n return { content: [{ type: 'text' as const, text: JSON.stringify(messages, null, 2) }] };\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n if (msg.includes('unable to open database') || msg.includes('permission denied')) {\n return {\n content: [\n {\n type: 'text' as const,\n text:\n 'Cannot access Messages database. Grant Full Disk Access to your terminal/IDE:\\n' +\n '1. Open System Settings > Privacy & Security > Full Disk Access\\n' +\n '2. Enable your terminal app\\n' +\n '3. Restart the terminal',\n },\n ],\n };\n }\n return {\n content: [{ type: 'text' as const, text: `Failed to read messages: ${msg}` }],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('read_imessages');\n\n // --- get_unread_imessages ---\n server.tool(\n 'get_unread_imessages',\n 'Get all unread iMessages across all conversations',\n {\n limit: z.number().optional().default(10).describe('Maximum unread messages to retrieve'),\n },\n async ({ limit }) => {\n const dbPath = getDbPath();\n const query = `\n SELECT\n m.ROWID as message_id,\n CASE\n WHEN m.text IS NOT NULL AND m.text != '' THEN m.text\n ELSE '[No text content]'\n END as content,\n datetime(m.date/1000000000 + strftime('%s', '2001-01-01'), 'unixepoch', 'localtime') as date,\n h.id as sender,\n m.is_from_me,\n m.cache_has_attachments\n FROM message m\n INNER JOIN handle h ON h.ROWID = m.handle_id\n WHERE m.is_from_me = 0\n AND m.is_read = 0\n AND (m.text IS NOT NULL OR m.cache_has_attachments = 1)\n AND m.is_audio_message = 0\n AND m.item_type = 0\n ORDER BY m.date DESC\n LIMIT ${limit}\n `;\n\n try {\n const stdout = await runSqlite(dbPath, query);\n if (!stdout) {\n return {\n content: [{ type: 'text' as const, text: 'No unread messages.' }],\n };\n }\n const messages = JSON.parse(stdout);\n return { content: [{ type: 'text' as const, text: JSON.stringify(messages, null, 2) }] };\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Failed to get unread messages: ${msg}` }],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('get_unread_imessages');\n\n // --- send_imessage ---\n server.tool(\n 'send_imessage',\n 'Send an iMessage to a contact via the macOS Messages app',\n {\n recipient: z.string().describe('Phone number or email of the recipient'),\n message: z.string().describe('Message content to send'),\n },\n async ({ recipient, message }) => {\n const script = `\n tell application \"Messages\"\n send ${JSON.stringify(message)} to buddy ${JSON.stringify(recipient)} of (service 1 whose service type = iMessage)\n end tell\n `;\n try {\n await runAppleScript(script);\n return {\n content: [{ type: 'text' as const, text: `Message sent successfully to ${recipient}` }],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to send message: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('send_imessage');\n\n return {\n server,\n getRegisteredTools() {\n return [...registeredTools];\n },\n async start() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n },\n };\n}\n\n// CLI entry point\nimport { realpathSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nconst isMainModule = (() => {\n try {\n return process.argv[1] &&\n fileURLToPath(import.meta.url) === realpathSync(process.argv[1]);\n } catch { return false; }\n})();\n\nif (isMainModule || process.env.FORGE_MCP_START === 'true') {\n const mcpServer = createIMessageMCPServer();\n mcpServer.start().catch((err) => {\n console.error('Failed to start iMessage MCP server:', err);\n process.exit(1);\n });\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goforgeit/mcp-imessage",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "type": "module",
5
5
  "description": "Forge MCP server for iMessage — read, search, and send messages via macOS Messages app",
6
6
  "bin": {