bluebubbles-mcp
This MCP server lets you interact with Apple iMessage through a BlueBubbles server, enabling message sending, reading, group management, and more.
Check connectivity & server info: ping, get_server_info, get_my_address
List & view chats: list_chats, get_chat, get_chat_messages, get_recent_messages, get_unread_chats, find_chats
Send messages: send_message, send_message_to_address, send_attachment, send_multipart, send_reaction, schedule_message
Edit/unsend messages: edit_message, unsend_message, delete_message
Manage group chats: create_group_chat, rename_group, set_group_icon, remove_group_icon, add_participant, remove_participant, leave_chat, delete_chat
Read/typing status: mark_chat_read, mark_chat_unread, start_typing, stop_typing
Search & retrieve messages: search_messages, get_message, get_attachment_info, download_attachment
Contact management: get_contacts, lookup_contact, find_contact, query_handles, get_handle
Check registrations: check_imessage, check_facetime
Focus & Find My: get_focus_status, find_my_devices, find_my_friends
Scheduled messages: list_scheduled_messages, get_scheduled_message, update_scheduled_message, delete_scheduled_message
Safety & access controls:
Write allowlist — restrict all write operations to a configurable list of phone numbers/emails
Freshness guard — prevent sending to stale conversations by ensuring the chat was recently read and no new messages have arrived
Private API awareness — automatically adapts available tools based on whether the BlueBubbles Private API is enabled
Contact name enrichment — resolves handles to human-readable names in responses and enables name-based searches
Compact responses — returns optimized message data by default for token efficiency, with an
extended=trueoption for full raw fields
Provides access to iMessage via a BlueBubbles server, allowing for listing and searching chats, sending and editing messages, managing contacts, and performing group chat operations like adding or removing participants.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@bluebubbles-mcpSend a message to John saying I'll be there in 5 minutes"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
bluebubbles-mcp
MCP server for BlueBubbles — access iMessage from any MCP client.
Built from scratch with no third-party MCP dependencies beyond the official mcp SDK and httpx.
Prerequisites
Python 3.11+
A running BlueBubbles server with API access enabled
Related MCP server: mac-messages-mcp
Setup
git clone https://github.com/metaember/bluebubbles-mcp.git
cd bluebubbles-mcp
uv syncConfiguration
Add to your MCP client config (e.g. Claude Code ~/.claude/settings.json):
{
"mcpServers": {
"bluebubbles": {
"command": "uv",
"args": ["--directory", "/path/to/bluebubbles-mcp", "run", "python", "-m", "bb_mcp.server"],
"env": {
"BLUEBUBBLES_URL": "https://your-bluebubbles-server",
"BLUEBUBBLES_PASSWORD": "your-server-password"
}
}
}
}Restricting write recipients (allowlist)
By default the server can message anyone. Set BLUEBUBBLES_WRITE_ALLOWLIST to a
comma-separated list of phone numbers and/or emails to restrict every write
(send, reaction, attachment, typing, scheduling, group edits, delete/leave) to
those recipients. It is enforced server-side, so an MCP client — including one
reaching the HTTP transport — cannot bypass it.
"BLUEBUBBLES_WRITE_ALLOWLIST": "+15551234567, partner@example.com",
"BLUEBUBBLES_ALLOWLIST_REGION": "US" // optional, default region for parsing local numbersBehavior:
Unset → unrestricted (backward compatible). Set but empty (
"") → deny all, a safe failure mode.Numbers are matched in normalized E.164 form, so
(555) 123-4567and+15551234567are equivalent; emails match case-insensitively.For a group chat, every participant must be on the list or the write is blocked. A chat whose participants can't be resolved is denied (fail-closed).
Read tools are not restricted, and the message-GUID writes (
edit_message,unsend_message) aren't covered — they only act on an already-sent message and can't reach a new recipient. ABLUEBUBBLES_READ_ALLOWLISTmay be added later.
Private API vs AppleScript
BlueBubbles can drive Messages two ways. AppleScript works on any install with no extra setup but only sends plain texts/attachments. The Private API (requires the BlueBubbles Helper + partially disabled SIP) additionally unlocks tapbacks, edit/unsend, typing indicators, read receipts, group management, threaded replies, and SMS sending.
On startup the server reads /server/info and adapts itself to what's actually
available:
Sends use the Private API when present, and fall back to AppleScript when not — so
send_messageworks on a bare install instead of erroring.Tools that require the Private API (reactions, edit/unsend, typing, read receipts, group management, iMessage/FaceTime availability checks) are hidden from the tool list when it's unavailable, so a client never sees a tool that could only fail.
Override the auto-detection with BLUEBUBBLES_PRIVATE_API:
"BLUEBUBBLES_PRIVATE_API": "auto" // default: introspect /server/info
// "true" — force-enable (assume the Private API is set up)
// "false" — force-disable (AppleScript only; hide Private API tools)If /server/info can't be read at startup, detection fails open (assumes the
Private API is available) so a transient blip doesn't hide half the toolset.
The same /server/info read also discovers the user's own iMessage address,
surfaced by the get_my_address tool. Override it if detection is wrong or you
run multiple handles:
"BLUEBUBBLES_MY_ADDRESS": "+15551234567" // or you@icloud.comContact names
Message and chat data from BlueBubbles carries raw phone numbers and emails, not
names. To save the model from constantly cross-referencing numbers, read
responses are enriched with a contactName beside each handle, and
find_contact / find_chats let it reach people by name instead of number.
Resolution is lazy and cached per session: a response's addresses are looked up
in a single batched /contact/query the first time they're seen, then reused.
Disable it (for leaner responses or if your contact DB is slow) with:
"BLUEBUBBLES_RESOLVE_NAMES": "false" // default: enabledfind_contact / find_chats still work when this is off — they're explicit.
Freshness guard
An agent can reply to a conversation against a stale snapshot — it read the thread
a while ago, a new message arrived in the gap, and it answers without seeing it.
The freshness guard enforces, server-side and per agent, that a send into a chat
only goes through if that agent read the chat within the last hour and nothing
new has arrived since. (A consequence: you must read an existing chat before
sending into it — good hygiene for replies. Starting a brand-new conversation via
create_chat is unaffected; it's for first contact only and, with the guard on,
refuses to reach an existing chat — use send_message for those.)
How it works. The guard keeps a per-agent watermark — the newest message each
agent has seen in each chat (optimistic concurrency, like an ETag on the thread):
Reading a chat with
get_chat_messagesrecords the watermark. Only that tool records — a scan likeget_unread_chatsdoesn't — so to reply you must deliberately open the actual thread.Before a send, the server re-checks the chat's live newest message. If anything (from anyone — the other person, you on another device, or another agent) arrived since your watermark, or your read is older than the TTL, the send is blocked with a note to re-read and re-plan.
Your own send advances your watermark, so sending doesn't block your next send.
See docs/freshness-guard.md for the full design.
On by default. Disable with BLUEBUBBLES_FRESHNESS=off. Tuning:
"BLUEBUBBLES_FRESHNESS": "off", // default: on
"BLUEBUBBLES_FRESHNESS_IDENTITY": "session", // how agents are told apart (default)
"BLUEBUBBLES_WATERMARK_TTL_SECONDS": "3600", // how long a read stays "fresh" (default 1h)
"BLUEBUBBLES_WATERMARK_MAX_AGENTS": "10000" // memory backstop (default 10000)The freshness check is per agent, so it needs to tell agents apart.
BLUEBUBBLES_FRESHNESS_IDENTITY selects how:
session(default): use the MCP transport session — automatic and zero-config for stdio (one agent) and direct HTTP (one session per connecting client). Over HTTP this relies on stateful sessions (FastMCP's default); the server refuses to start if you combine session-identity freshness with stateless HTTP, since every send would be blocked.meta: identify agents by a per-agentagentIdthe caller stamps into request_meta. Required only behind a pooling proxy/airlock, where every agent shares one transport session sosessioncan't distinguish them. Seedocs/freshness-guard.mdfor the airlock contract.
Compact responses & sender filtering
Message reads return a compact projection by default — the fields an
assistant needs (guid, text, handle + contactName, isFromMe,
timestamps, attachment names, reaction/reply linkage) instead of the full raw
BlueBubbles objects, which cuts token usage substantially. Pass extended=true
on any read tool to get the complete raw fields.
Message reads (get_chat_messages, search_messages, get_recent_messages)
also accept from_address to filter by who sent each message — pass a phone
number / email, or "me" for the user's own messages. Filtering is by true
sender (the user when isFromMe, otherwise the message's handle), so it's
correct in both 1:1 and group chats.
Tools
Tool | Description | Annotations |
| Check server connectivity | read-only |
| Server info and health | read-only |
| The user's own iMessage address (to identify their own messages) | read-only |
| List conversations by recent activity | read-only |
| Chat details with participants | read-only |
| Messages from a chat | read-only |
| Search by text, chat, time range | read-only |
| Single message by GUID | read-only |
| All contacts | read-only |
| Look up name by phone/email | read-only |
| Find contacts by name (phone/email unknown) | read-only |
| Find chats involving a contact by name | read-only |
| Check iMessage registration | read-only |
| Check FaceTime registration | read-only |
| List/search known handles | read-only |
| Get a handle by address | read-only |
| Contact's Focus / Do Not Disturb status | read-only |
| Find My — your devices' locations | read-only |
| Find My — friends' locations | read-only |
| List future messages | read-only |
| Get one scheduled message by ID | read-only |
| Messages from last N minutes across all chats | read-only |
| Chats with unread messages + their latest messages | read-only |
| Attachment metadata | read-only |
| Download attachment as base64 | read-only |
| Download a group chat's icon | read-only |
| Send read receipt | idempotent, open-world |
| Mark chat unread (local) | idempotent |
| Rename a group chat | idempotent |
| Set a group chat's icon | idempotent |
| Show typing indicator | open-world |
| Stop typing indicator | open-world |
| Send to an existing chat (1:1 or group) | open-world |
| Start a new 1:1 conversation by phone/email | open-world |
| Create a group chat + first message | open-world |
| Send a file attachment | open-world |
| Send text + attachments as one message | open-world |
| Tapback reaction | open-world |
| Edit a sent message | open-world |
| Schedule a future message | open-world |
| Update a scheduled message | open-world |
| Add to group chat | open-world |
| Retract a message | destructive, open-world |
| Remove from group chat | destructive, open-world |
| Leave a group chat | destructive, open-world |
| Remove a group chat's icon | destructive, open-world |
| Delete a single message | destructive, open-world |
| Delete a conversation | destructive, open-world |
| Cancel scheduled message | destructive, open-world |
Run over HTTP (Docker)
The server speaks stdio by default. Set MCP_TRANSPORT=streamable-http to serve
over Streamable HTTP instead, so any
HTTP-capable MCP client can connect at http://<host>:8000/mcp. The Docker image
sets this for you.
docker build -t bluebubbles-mcp .
docker run --rm -e BLUEBUBBLES_URL -e BLUEBUBBLES_PASSWORD -p 8000:8000 bluebubbles-mcpAs a Compose service — credentials live in this container's own environment (an
env_file or Docker secrets), so they stay isolated to this tool:
services:
bluebubbles-mcp:
build: .
environment:
BLUEBUBBLES_URL: ${BLUEBUBBLES_URL}
BLUEBUBBLES_PASSWORD: ${BLUEBUBBLES_PASSWORD}
restart: unless-stoppedNotes:
The HTTP endpoint is unauthenticated, so don't expose it publicly — keep it on a private/internal network (and drop the published
ports:if a co-located client reaches it over the Compose network).BLUEBUBBLES_URLmust be reachable from inside the container — use a hostname/IP the container can resolve (e.g. the BlueBubbles host's LAN address orhost.docker.internal), notlocalhost.
License
MIT
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Appeared in Searches
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/metaember/bluebubbles-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server