imessage-mcp-send 0.1.0__tar.gz
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.
- imessage_mcp_send-0.1.0/PKG-INFO +65 -0
- imessage_mcp_send-0.1.0/README.md +44 -0
- imessage_mcp_send-0.1.0/pyproject.toml +34 -0
- imessage_mcp_send-0.1.0/src/imessage_mcp_send/__init__.py +10 -0
- imessage_mcp_send-0.1.0/src/imessage_mcp_send/cli.py +22 -0
- imessage_mcp_send-0.1.0/src/imessage_mcp_send/tools.py +93 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: imessage-mcp-send
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Optional add-on for imessage-mcp: exposes a send_message tool so MCP clients can also send iMessages.
|
|
5
|
+
Keywords: mcp,imessage,macos,claude,llm,send
|
|
6
|
+
Author: moritzhwnr
|
|
7
|
+
Author-email: moritzhwnr <moritz.hawener@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Topic :: Communications :: Chat
|
|
14
|
+
Requires-Dist: imessage-mcp>=0.1.0
|
|
15
|
+
Requires-Dist: typer>=0.25.1
|
|
16
|
+
Requires-Python: >=3.13
|
|
17
|
+
Project-URL: Homepage, https://github.com/yourname/imessage-mcp
|
|
18
|
+
Project-URL: Repository, https://github.com/yourname/imessage-mcp
|
|
19
|
+
Project-URL: Issues, https://github.com/yourname/imessage-mcp/issues
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# imessage-mcp-send
|
|
23
|
+
|
|
24
|
+
Optional add-on for [`imessage-mcp`](https://pypi.org/project/imessage-mcp/) that adds a `send_message` MCP tool. With this installed, your MCP client can also **send** iMessages, not just read them.
|
|
25
|
+
|
|
26
|
+
**Sending is irreversible.** Install only if you understand and accept that — and only with an MCP client that surfaces tool calls to you for confirmation before executing them.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv tool install imessage-mcp-send
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This pulls in `imessage-mcp` as a dependency, so you don't need to install it separately.
|
|
35
|
+
|
|
36
|
+
## Quickstart
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
imessage-mcp-send setup # opens the macOS Full Disk Access pane
|
|
40
|
+
imessage-mcp-send serve # MCP server on http://127.0.0.1:8765/mcp
|
|
41
|
+
# exposes list_chats, read_messages, search_messages, send_message
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
On first send, macOS will prompt for **Automation** permission for whatever app is running the CLI (Terminal, iTerm, etc.) to control **Messages**. Click Allow.
|
|
45
|
+
|
|
46
|
+
## With the broker ([`imessage-bridge`](https://pypi.org/project/imessage-bridge/))
|
|
47
|
+
|
|
48
|
+
Install both:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv tool install imessage-mcp-send
|
|
52
|
+
uv tool install imessage-bridge
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`imessage-bridge serve --public` auto-detects this package and exposes send via your broker URL too.
|
|
56
|
+
|
|
57
|
+
## The new tool
|
|
58
|
+
|
|
59
|
+
| Tool | Arguments | Purpose |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `send_message` | `text`, `recipient?`, `chat_id?` | Send to a phone/email **or** reply to a 1:1 chat. Group chats not supported. |
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# imessage-mcp-send
|
|
2
|
+
|
|
3
|
+
Optional add-on for [`imessage-mcp`](https://pypi.org/project/imessage-mcp/) that adds a `send_message` MCP tool. With this installed, your MCP client can also **send** iMessages, not just read them.
|
|
4
|
+
|
|
5
|
+
**Sending is irreversible.** Install only if you understand and accept that — and only with an MCP client that surfaces tool calls to you for confirmation before executing them.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv tool install imessage-mcp-send
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This pulls in `imessage-mcp` as a dependency, so you don't need to install it separately.
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
imessage-mcp-send setup # opens the macOS Full Disk Access pane
|
|
19
|
+
imessage-mcp-send serve # MCP server on http://127.0.0.1:8765/mcp
|
|
20
|
+
# exposes list_chats, read_messages, search_messages, send_message
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
On first send, macOS will prompt for **Automation** permission for whatever app is running the CLI (Terminal, iTerm, etc.) to control **Messages**. Click Allow.
|
|
24
|
+
|
|
25
|
+
## With the broker ([`imessage-bridge`](https://pypi.org/project/imessage-bridge/))
|
|
26
|
+
|
|
27
|
+
Install both:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv tool install imessage-mcp-send
|
|
31
|
+
uv tool install imessage-bridge
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`imessage-bridge serve --public` auto-detects this package and exposes send via your broker URL too.
|
|
35
|
+
|
|
36
|
+
## The new tool
|
|
37
|
+
|
|
38
|
+
| Tool | Arguments | Purpose |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `send_message` | `text`, `recipient?`, `chat_id?` | Send to a phone/email **or** reply to a 1:1 chat. Group chats not supported. |
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
MIT
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "imessage-mcp-send"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Optional add-on for imessage-mcp: exposes a send_message tool so MCP clients can also send iMessages."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "moritzhwnr", email = "moritz.hawener@gmail.com" },
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"imessage-mcp>=0.1.0",
|
|
13
|
+
"typer>=0.25.1",
|
|
14
|
+
]
|
|
15
|
+
keywords = ["mcp", "imessage", "macos", "claude", "llm", "send"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Operating System :: MacOS :: MacOS X",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Topic :: Communications :: Chat",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/yourname/imessage-mcp"
|
|
26
|
+
Repository = "https://github.com/yourname/imessage-mcp"
|
|
27
|
+
Issues = "https://github.com/yourname/imessage-mcp/issues"
|
|
28
|
+
|
|
29
|
+
[project.scripts]
|
|
30
|
+
imessage-mcp-send = "imessage_mcp_send.cli:main"
|
|
31
|
+
|
|
32
|
+
[build-system]
|
|
33
|
+
requires = ["uv_build>=0.11.2,<0.12.0"]
|
|
34
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""imessage-mcp-send — adds a send_message tool to the imessage-mcp server.
|
|
2
|
+
|
|
3
|
+
Importing this package has a SIDE EFFECT: it registers `send_message` on
|
|
4
|
+
the shared FastMCP instance (`imessage_mcp.server.mcp`). Both binaries —
|
|
5
|
+
`imessage-mcp-send` (this package) and `imessage-bridge` (when installed
|
|
6
|
+
alongside) — pick up the tool automatically by importing `tools`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Side-effect import: registers send_message on the shared mcp instance.
|
|
10
|
+
from imessage_mcp_send import tools as tools # noqa: F401
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Binary `imessage-mcp-send` — same Typer app as `imessage-mcp`, but with
|
|
2
|
+
the `send_message` tool registered on the shared FastMCP instance.
|
|
3
|
+
|
|
4
|
+
We don't add new Typer commands here. The capability the user installs this
|
|
5
|
+
package for is exposed via the MCP protocol, not the CLI. `serve` from
|
|
6
|
+
imessage-mcp picks up the additional tool automatically because we import
|
|
7
|
+
`imessage_mcp_send` (which triggers `tools` registration) before `app()`
|
|
8
|
+
runs.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
# Side-effect import: registers send_message on imessage_mcp.server.mcp.
|
|
14
|
+
import imessage_mcp_send # noqa: F401
|
|
15
|
+
from imessage_mcp.cli import app
|
|
16
|
+
|
|
17
|
+
# Reflect the upgraded capability in --help.
|
|
18
|
+
app.info.help = "MCP server exposing iMessage (read AND send, local-only)."
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main() -> None:
|
|
22
|
+
app()
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""The send_message MCP tool. Side-effect: registers on import.
|
|
2
|
+
|
|
3
|
+
Sending is irreversible. The MCP client (Claude Desktop, etc.) is expected
|
|
4
|
+
to surface tool calls to the user for confirmation — we don't add our own
|
|
5
|
+
confirmation here because there's no human in the MCP request loop. If
|
|
6
|
+
your client doesn't gate tool use, that's a client issue, not a tool one.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import subprocess
|
|
12
|
+
|
|
13
|
+
# Importing the singleton `mcp` from imessage-mcp's server lets the
|
|
14
|
+
# @mcp.tool() decorator below register on the SAME instance the read-only
|
|
15
|
+
# server already uses. When the server starts, all 4 tools are exposed.
|
|
16
|
+
from imessage_mcp._imessage import open_db
|
|
17
|
+
from imessage_mcp.server import mcp
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _send_via_applescript(recipient: str, text: str) -> str:
|
|
21
|
+
"""Run AppleScript to ask Messages.app to send. Returns a result string."""
|
|
22
|
+
# Pass recipient + text as positional args (not interpolated into the
|
|
23
|
+
# script source) so a maliciously-crafted message body can't break out
|
|
24
|
+
# of string literals and inject AppleScript.
|
|
25
|
+
script = """
|
|
26
|
+
on run argv
|
|
27
|
+
set theBuddy to item 1 of argv
|
|
28
|
+
set theText to item 2 of argv
|
|
29
|
+
tell application "Messages"
|
|
30
|
+
set targetService to 1st service whose service type = iMessage
|
|
31
|
+
set theBuddy to buddy theBuddy of targetService
|
|
32
|
+
send theText to theBuddy
|
|
33
|
+
end tell
|
|
34
|
+
end run
|
|
35
|
+
"""
|
|
36
|
+
result = subprocess.run(
|
|
37
|
+
["osascript", "-e", script, recipient, text],
|
|
38
|
+
capture_output=True,
|
|
39
|
+
text=True,
|
|
40
|
+
)
|
|
41
|
+
if result.returncode != 0:
|
|
42
|
+
return f"ERROR: send failed — {result.stderr.strip() or 'unknown'}"
|
|
43
|
+
return f"sent to {recipient}"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@mcp.tool()
|
|
47
|
+
def send_message(
|
|
48
|
+
text: str,
|
|
49
|
+
recipient: str | None = None,
|
|
50
|
+
chat_id: int | None = None,
|
|
51
|
+
) -> str:
|
|
52
|
+
"""Send an iMessage via Messages.app.
|
|
53
|
+
|
|
54
|
+
IRREVERSIBLE side effect. Provide EXACTLY ONE of `recipient` or `chat_id`.
|
|
55
|
+
Group chats are not currently supported.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
text: The message body to send.
|
|
59
|
+
recipient: E.164 phone number (e.g. "+491234567890") or iMessage email.
|
|
60
|
+
chat_id: A chat ID from list_chats. Must be a 1:1 chat.
|
|
61
|
+
|
|
62
|
+
Returns: A status string starting with "sent" on success or "ERROR" on failure.
|
|
63
|
+
"""
|
|
64
|
+
if (recipient is None) == (chat_id is None):
|
|
65
|
+
return "ERROR: provide exactly one of `recipient` or `chat_id`."
|
|
66
|
+
|
|
67
|
+
if chat_id is not None:
|
|
68
|
+
# Resolve a chat_id to a single handle. AppleScript can technically
|
|
69
|
+
# address group chats via `text chat id`, but it's unreliable on
|
|
70
|
+
# modern macOS — better to refuse than half-work.
|
|
71
|
+
try:
|
|
72
|
+
conn = open_db()
|
|
73
|
+
except FileNotFoundError as e:
|
|
74
|
+
return f"ERROR: {e}"
|
|
75
|
+
handles = conn.execute(
|
|
76
|
+
"""
|
|
77
|
+
SELECT h.id FROM chat_handle_join chj
|
|
78
|
+
JOIN handle h ON h.ROWID = chj.handle_id
|
|
79
|
+
WHERE chj.chat_id = ?
|
|
80
|
+
""",
|
|
81
|
+
(chat_id,),
|
|
82
|
+
).fetchall()
|
|
83
|
+
if not handles:
|
|
84
|
+
return f"ERROR: no handles found for chat {chat_id}"
|
|
85
|
+
if len(handles) > 1:
|
|
86
|
+
return (
|
|
87
|
+
f"ERROR: chat {chat_id} is a group ({len(handles)} participants); "
|
|
88
|
+
"group sending is not supported."
|
|
89
|
+
)
|
|
90
|
+
recipient = handles[0][0]
|
|
91
|
+
|
|
92
|
+
assert recipient is not None # guaranteed by the XOR check above
|
|
93
|
+
return _send_via_applescript(recipient, text)
|