Uh oh!
There was an error while loading. Please reload this page.
Add secure macOS iMessage plugin - #71
Open
maximedegreve wants to merge 4 commits into
Open
Conversation
Add a macOS-only local MCP server with deny-by-default access controls, read-only Messages history, safe AppleScript sends, marketplace registration, documentation, and focused tests.\n\nAdapt the security model from Anthropic's Apache-2.0 iMessage plugin with attribution and Copilot-specific state and branding.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a local macOS iMessage MCP plugin with access controls, configuration workflows, documentation, and tests.
Changes:
- Implements local message reading, search, and replies.
- Adds secure policy configuration and Copilot skills.
- Registers, documents, licenses, and tests the plugin.
Show a summary per file
| File | Description |
|---|---|
README.md | Documents MCP support and licensing. |
.github/plugin/marketplace.json | Registers the iMessage plugin. |
plugins/imessage/.gitignore | Excludes Python cache files. |
plugins/imessage/.mcp.json | Configures the MCP server. |
plugins/imessage/LICENSE | Adds Apache 2.0 license. |
plugins/imessage/NOTICE | Records upstream attribution. |
plugins/imessage/README.md | Documents installation and security. |
plugins/imessage/plugin.json | Defines plugin metadata. |
plugins/imessage/server/__init__.py | Initializes the server package. |
plugins/imessage/server/imessage.py | Implements storage, authorization, queries, and sending. |
plugins/imessage/server/imessage_mcp.py | Implements MCP and configuration CLI. |
plugins/imessage/skills/imessage-configure/SKILL.md | Guides secure configuration. |
plugins/imessage/skills/imessage-messaging/SKILL.md | Guides safe messaging workflows. |
plugins/imessage/tests/test_imessage.py | Tests security and MCP behavior. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Suppressed comments (1)
plugins/imessage/server/imessage.py:503
- Search has the same service-boundary gap: authorized chat IDs scope the query, but matching rows are never filtered by their resolved
m.service/handle service. A spoofable SMS/RCS row joined to an otherwise authorized chat can therefore appear while SMS/RCS is disabled; pass the effective service policy into this query and filter before ordering/limiting.
WHERE cmj.chat_id IN ({placeholders})
AND instr(lower(COALESCE(m.text, '')), lower(?)) > 0
- Files reviewed: 14/14 changed files
- Comments generated: 6
- Review effort level: Balanced
Comment on lines
+716
to
+719
| yield remaining[:split] | ||
| remaining = remaining[split:] | ||
| if remaining.startswith("\n") or remaining.startswith(" "): | ||
| remaining = remaining[1:] |
Comment on lines
+203
to
+208
| if method == "initialize": | ||
| requested = request.get("params", {}).get( | ||
| "protocolVersion", "2025-06-18" | ||
| ) | ||
| result = { | ||
| "protocolVersion": requested, |
Comment on lines
+338
to
+340
| def _policy_update(store: ConfigStore, **changes: Any) -> AccessPolicy: | ||
| policy = replace(store.load(), **changes) | ||
| store.save(policy) |
Comment on lines
+638
to
+642
| return [ | ||
| chat.public_dict() | ||
| for chat in self.database.list_chats(scan_limit=None) | ||
| if self.chat_allowed(chat, policy) | ||
| ][:limit] |
Comment on lines
+321
to
+331
| try: | ||
| request = json.loads(raw_line) | ||
| if not isinstance(request, dict): | ||
| raise ValueError("JSON-RPC message must be an object.") | ||
| response = server.dispatch(request) | ||
| except (json.JSONDecodeError, ValueError) as error: | ||
| response = { | ||
| "jsonrpc": "2.0", | ||
| "id": None, | ||
| "error": {"code": -32700, "message": str(error)}, | ||
| } |
| JOIN chat_message_join AS cmj ON cmj.message_id = m.ROWID | ||
| JOIN chat AS c ON c.ROWID = cmj.chat_id | ||
| LEFT JOIN handle AS h ON h.ROWID = m.handle_id | ||
| WHERE cmj.chat_id = ? |
Report privacy-safe authorized and self-chat counts from the status tool, explain the fail-closed empty state, and cover live owner-alias reloads without an MCP restart. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Recognize local destination identities only when they occur on both incoming and outgoing authenticated iMessages for a trusted account. This supports modern Messages self-chats while excluding recipients, one-sided values, short codes, and SMS/RCS data. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Security and permissions
~/Library/Messages/chat.dbwith SQLite read-only and query-only modes0600file and0700directory permissions under a Copilot-specific Application Support pathUsers must grant Full Disk Access to the application running Copilot CLI for reads and Automation permission to control Messages.app for sends.
Upstream attribution
The security design and typedstream parser are derived from Anthropic's Apache-2.0 iMessage plugin at
anthropics/claude-plugins-officialcommitc54b5608d9be1910de9a5b91c2d15bf6673b9c35. The plugin includes the upstream Apache License 2.0 copyright notice and a modification notice, uses Copilot-specific paths/configuration, and removes upstream outbound branding.Validation
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s plugins/imessage/tests -v(16 tests)initialize,tools/list,imessage_status, andimessage_chatssmoke testscopilot --plugin-dir ./plugins/imessage plugin listgit diff --checkCaveats
message.textcolumn; newer binaryattributedBodycontent is decoded for history on a best-effort basis but is not searchable