# jons-mcp-imessage
A local MCP server for querying and sending iMessages on macOS.
This FastMCP server exposes tools through the Model Context Protocol (MCP), enabling AI assistants to read your iMessage history and send messages.
## Requirements
- macOS (tested on Tahoe 26.x, should work on recent versions)
- Python 3.10+
- Messages.app (for sending messages)
## Permissions Required
This server requires specific macOS permissions to function. **Permission issues are the most common cause of problems.**
### Full Disk Access (Required for Reading Messages)
The application running this server needs Full Disk Access to read `~/Library/Messages/chat.db`.
**To grant Full Disk Access:**
1. Open **System Settings** (or System Preferences on older macOS)
2. Navigate to **Privacy & Security** → **Full Disk Access**
3. Click the lock icon and authenticate if needed
4. Click the **+** button
5. Add the appropriate application:
- **If using Claude Desktop**: Add `Claude.app` (usually in /Applications)
- **If running from terminal**: Add your terminal app (e.g., `Terminal.app`, `iTerm.app`, `Zed.app`)
- **If running via another app**: Add that specific application
6. Restart the application after granting access
**How to verify:** Run `check_permissions` tool - it will report whether database access is working.
### Automation Permission (Required for Sending Messages)
To send messages via AppleScript, the application needs permission to control Messages.app.
**This permission is prompted automatically** the first time you try to send a message. Click "OK" to allow.
**To manually grant or verify:**
1. Open **System Settings** → **Privacy & Security** → **Automation**
2. Find your application (Terminal, Claude Desktop, etc.)
3. Ensure **Messages** is checked
### Contacts Permission (Optional - For Contact Name Enrichment)
The server can enrich message responses with contact names from your Contacts app. This is **optional** - all features work without it, but you'll see phone numbers/emails instead of names.
**To enable contact name enrichment:**
1. Open **System Settings** (or System Preferences on older macOS)
2. Navigate to **Privacy & Security** → **Contacts**
3. Click the lock icon and authenticate if needed
4. Click the **+** button
5. Add the appropriate application:
- **If using Claude Desktop**: Add `Claude.app` (usually in /Applications)
- **If running from terminal**: Add your terminal app (e.g., `Terminal.app`, `iTerm.app`)
6. Restart the application after granting access
**What you get with Contacts permission:**
- Message responses include `contact_name` field (e.g., "John Smith" instead of just "+15551234567")
- Conversation responses include `participant_names` field
- `lookup_contact` tool becomes available for explicit contact lookups
**Without Contacts permission:**
- Contact name fields will be `null`
- `lookup_contact` returns an error message
- All other functionality works normally (graceful degradation)
## Installation
```bash
# Clone the repository
git clone <your-repo-url>
cd jons-mcp-imessage
# Install with uv
uv pip install -e .
```
## Running the Server
```bash
uv run jons-mcp-imessage
```
## Adding to Claude Code
```bash
# Register the MCP server with Claude Code
claude mcp add jons-mcp-imessage -- uv run --directory /path/to/jons-mcp-imessage jons-mcp-imessage
```
## Adding to Claude Desktop
Add the following to your Claude Desktop configuration file:
**Location:** `~/Library/Application Support/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"jons-mcp-imessage": {
"command": "uv",
"args": [
"run",
"--directory",
"/path/to/jons-mcp-imessage",
"jons-mcp-imessage"
],
"env": {
"OPENAI_API_KEY": "sk-your-openai-api-key"
}
}
}
}
```
**Notes:**
- Replace `/path/to/jons-mcp-imessage` with the actual path to this repository
- Replace `sk-your-openai-api-key` with your OpenAI API key (required for semantic search)
- If you don't have an OpenAI key, omit the `env` section entirely - keyword search will still work
- Restart Claude Desktop after modifying the config
## Search Features
The search system combines two powerful search methods:
### Keyword Search (FTS5)
Full-text search using SQLite FTS5 with BM25 ranking. Supports:
- Phrase search: `"exact phrase"`
- Prefix matching: `word*`
- Proximity: `word1 NEAR word2`
- Boolean: `word1 AND word2`, `word1 OR word2`, `NOT word`
### Semantic Search
AI-powered search using OpenAI embeddings to find conceptually similar messages, even when exact keywords don't match.
### Setup
1. **Basic Setup** (keyword search only): Works out of the box
2. **Full Setup** (hybrid search): Set your OpenAI API key:
```bash
export OPENAI_API_KEY=your-api-key
```
### Search Modes
- `hybrid` (default): Combines keyword + semantic results using RRF
- `keyword`: FTS5 only (no API key needed)
- `semantic`: Embedding similarity only (requires API key)
### Index Management
The search index is stored separately from iMessage's chat.db at:
`~/.local/share/jons-mcp-imessage/search_index.db`
Tools available:
- `search_index_status` - Check index health and sync status
- `rebuild_search_index` - Full reindex (use if issues occur)
### Troubleshooting
| Issue | Solution |
|-------|----------|
| "No results found" | Check if index is synced with `search_index_status` |
| Semantic search not working | Verify `OPENAI_API_KEY` is set |
| Slow first search | Index is building in background, try again shortly |
| Stale results | chat.db may be locked by Messages app |
## Available Tools
### Reading Messages
| Tool | Description |
|------|-------------|
| `check_permissions` | Verify database access and diagnose permission issues |
| `list_conversations` | List all conversations with metadata (participants, last message, etc.) |
| `get_conversation_messages` | Get messages from a specific conversation by contact or chat_id |
| `get_recent_messages` | Get recent messages across all conversations |
| `get_message_context` | Get messages before/after a specific message in the same thread |
| `search_messages` | Search messages by text content with optional filters |
| `search_contacts` | Search for contacts/handles by phone number or email in iMessage database |
| `lookup_contact` | Look up contact name from Contacts app by phone/email (requires Contacts permission) |
### Sending Messages
| Tool | Description |
|------|-------------|
| `send_message` | Send a message to an existing conversation |
### send_message Limitations
**Important:** The `send_message` tool has significant limitations:
1. **Existing conversations only**: Can only send to contacts you have previously messaged. New contacts require starting a conversation manually in Messages.app first.
2. **No delivery confirmation**: The tool reports success when the message is handed to Messages.app, but cannot confirm actual delivery. Messages may silently fail if:
- The recipient has blocked you
- The phone number/email is invalid
- Network issues occur
3. **Messages.app must be running**: If Messages.app is not running, the tool will fail with a "Messages not running" error.
4. **Service detection**: By default, attempts iMessage first. Use `service="SMS"` to force SMS for non-iMessage contacts.
## Example Usage
```python
# Check if permissions are configured correctly
check_permissions()
# List your 10 most recent conversations
list_conversations(limit=10)
# Get messages from a specific contact
get_conversation_messages(contact="+15551234567", limit=20)
# Search for messages containing specific text
search_messages(query="dinner plans", sender="+15551234567")
# Get context around a specific message (5 messages before and after)
get_message_context(rowid=12345, before=5, after=5)
# Send a message (to existing conversation only)
send_message(recipient="+15551234567", message="Hello!")
```
## Troubleshooting
### "Permission denied" or "Unable to read database"
**Cause:** Full Disk Access not granted.
**Fix:** Follow the Full Disk Access instructions above. Make sure to:
- Grant access to the correct application (the one actually running the server)
- Restart the application after granting access
### "Messages not running (-600)"
**Cause:** Messages.app is not running.
**Fix:** Open Messages.app before sending messages.
### "Not allowed to send Apple events (-1743)"
**Cause:** Automation permission not granted.
**Fix:**
1. Go to System Settings → Privacy & Security → Automation
2. Find your application and enable Messages access
3. If not listed, try sending a message again to trigger the permission prompt
### "Can't get buddy id" or "existing conversations only"
**Cause:** Trying to message a contact you haven't messaged before.
**Fix:** Start a conversation with this contact manually in Messages.app first, then try again.
### Messages appear empty or "(non-text message)"
**Cause:** The message contains only attachments (images, videos) or is a system message (like "renamed the group").
**Note:** This is expected behavior. The server extracts text content only.
## Privacy Considerations
This server accesses your local iMessage database and optionally your Contacts database. Be aware that:
- **All message history is accessible**: The server can read all messages stored locally
- **Contact information is exposed**: Phone numbers and email addresses are visible
- **Attachments are referenced**: File paths to attachments (photos, videos) are included
- **No cloud access**: Only locally-stored messages are accessible (not messages only on iCloud)
### Contact Name Privacy
If you grant Contacts permission:
- **Contact names come from YOUR Contacts app**: The server reads your personal contact list to enrich message responses
- **Names are NOT stored**: Contact names are resolved at query time and are NOT stored in any database or search index
- **Names are cached in-memory only**: The server loads all contacts into memory on first use, cache is cleared on restart
- **All Contacts databases are read**: Includes main Contacts DB plus any per-source databases (iCloud, CardDAV, etc.)
Use appropriate caution when granting AI assistants access to this server.
## Development
### Setup
```bash
# Install with dev dependencies
uv pip install -e ".[dev]"
```
### Running Tests
```bash
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=src
# Run a specific test file
uv run pytest tests/test_parser.py
```
### Code Quality
```bash
# Type check
uv run mypy src/jons_mcp_imessage
# Format code
uv run black src tests
# Lint code
uv run ruff check src tests
```
## Project Structure
```
jons-mcp-imessage/
├── src/
│ └── jons_mcp_imessage/
│ ├── __init__.py # Package exports
│ ├── constants.py # Configuration constants
│ ├── exceptions.py # Custom exceptions
│ ├── utils.py # Utility functions
│ ├── server.py # FastMCP server setup
│ ├── db/
│ │ ├── __init__.py # Database module exports
│ │ ├── connection.py # SQLite connection management
│ │ ├── models.py # Pydantic data models
│ │ ├── parser.py # attributedBody binary parser
│ │ └── queries.py # Query helpers and utilities
│ └── tools/
│ ├── __init__.py # Tool exports
│ ├── health.py # Permission checking
│ ├── contacts.py # Contact search
│ ├── conversations.py # Conversation tools
│ ├── messages.py # Message tools
│ └── send.py # Message sending
├── tests/
│ ├── test_parser.py # attributedBody parser tests
│ ├── test_db.py # Database utility tests
│ └── test_send.py # Send tool tests
├── docs/
│ └── IMESSAGE_DATABASE_FORMAT.md # Database format documentation
├── pyproject.toml # Project configuration
├── CLAUDE.md # AI assistant guidance
└── README.md # This file
```
## License
MIT