npx skills add ...
npx skills add photon-hq/skills --skill chat-adapter-imessage
Connect the Vercel AI SDK to iMessage. Build AI agents and assistants that communicate over iMessage using chat-adapter-imessage, the official Photon adapter. Local mode (runs on your Mac) and remote mode (Photon's production infrastructure). Covers createiMessageAdapter, postMessage, editMessage, deleteMessage, react, startGatewayListener, types, and integration with the Chat SDK. Keywords: vercel ai sdk, imessage, chat adapter, ai agent, chatbot, conversational ai, messaging, apple messages, vercel, nextjs, ai assistant, chat sdk, real-time, macos.
npx skills add photon-hq/skills --skill chat-adapter-imessage
This skill provides a complete reference for using chat-adapter-imessage, the official Photon adapter for connecting the Vercel AI SDK (Chat SDK) to iMessage.
The adapter acts as a bridge, allowing you to build AI agents and assistants with the Vercel AI SDK that communicate with users over iMessage. It leverages @photon-ai/imessage-kit (self-hosted) and @photon-ai/advanced-imessage-kit (production infrastructure by Photon) under the hood.
Adapter interface from the chat package, making it a drop-in solution.createiMessageAdapter(config)This is the main entry point. It creates and configures an instance of the iMessageAdapter.
iMessageAdapterConfig)| Mode | local | serverUrl | apiKey | Description |
|---|---|---|---|---|
| Local | true | Optional | Optional | Runs on macOS using @photon-ai/imessage-kit. serverUrl and apiKey are ignored. |
| Remote | false | Required | Required | Connects to Photon's infra using @photon-ai/advanced-imessage-kit. |
ChatOnce created, the adapter is passed to the Chat constructor from the Vercel AI SDK.
adapter.initialize(chat)Called by the Chat instance to link the adapter. In remote mode, this method also establishes the WebSocket connection to the Photon server.
adapter.postMessage(threadId, message)Sends a message to a specific iMessage thread.
threadId: The encoded thread identifier, which is the chatGuid (e.g., iMessage;-;+15551234567).message: An AdapterPostableMessage object, which can be a simple string or a FormattedContent object with markdown.adapter.editMessage(threadId, messageId, message)Edits a previously sent message. Only supported in remote mode.
adapter.deleteMessage(threadId, messageId)Unsends/deletes a previously sent message. Only supported in remote mode.
adapter.react(threadId, messageId, emoji)Adds a tapback/reaction to a message. Only supported in remote mode.
emoji: A standard emoji character or a name like heart, like, laugh.adapter.startGatewayListener(options)This is the primary method for receiving messages. It starts a listener that ingests messages from both local and remote SDKs and forwards them to the Chat instance.
adapter.handleWebhook(request)This method is not supported. The adapter does not use traditional webhooks for message ingestion. You must use startGatewayListener() instead.
iMessageThreadIdThe decoded threadId object.
iMessageGatewayMessageDataThe normalized message object that the adapter uses internally.
NativeWebhookPayloadIf using the Self-Hosted Kit's native webhook feature, this is the shape of the JSON payload that will be sent to your endpoint. The adapter can process this payload.
import { Chat } from 'chat';
import { createiMessageAdapter } from 'chat-adapter-imessage';
const adapter = createiMessageAdapter({ local: true });
const chat = new Chat({
adapter,
// ... your other Chat config
});const threadId = 'iMessage;-;+15551234567'; // A direct message thread
// Send a simple text message
await adapter.postMessage(threadId, 'Hello from the adapter!');
// Send a message with markdown (will be converted to plain text)
await adapter.postMessage(threadId, {
markdown: 'Here are the results:\n\n- **Item 1**\n- *Item 2*'
});await adapter.editMessage(
'iMessage;-;+15551234567',
'p:0/GUID-OF-MESSAGE-TO-EDIT',
'This is the corrected text.'
);await adapter.deleteMessage(
'iMessage;-;+15551234567',
'p:0/GUID-OF-MESSAGE-TO-DELETE'
);await adapter.react(
'iMessage;-;+15551234567',
'p:0/GUID-OF-MESSAGE-TO-REACT-TO',
'❤️'
);// In your main application file
const adapter = createiMessageAdapter({ local: true });
const chat = new Chat({ adapter });
// Start listening for incoming iMessages
adapter.startGatewayListener();
// Now, messages sent to your iMessage will be processed by the Chat SDK
chat.on('message', (message) => {
console.log(`[${message.author.userName}]: ${message.text}`);
// Your AI logic here...
});interface iMessageThreadId {
chatGuid: string; // e.g., "iMessage;-;+1234567890"
}interface iMessageGatewayMessageData {
guid: string;
text: string | null;
sender: string;
senderName: string | null;
chatId: string;
isGroupChat: boolean;
isFromMe: boolean;
date: string; // ISO 8601
attachments: iMessageAttachment[];
source: 'local' | 'remote';
raw?: unknown; // Original message object from the underlying SDK
}interface NativeWebhookPayload {
guid: string;
text: string | null;
sender: string;
chatId: string;
// ... and other fields from the Basic Kit Message object
}