imsg-mcp 1.0.0
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.
- package/CHANGELOG.md +6 -0
- package/LICENSE +21 -0
- package/README.md +127 -0
- package/dist/cli.js +611 -0
- package/dist/cli.js.map +1 -0
- package/dist/dateParse-DJXMfq3a.js +74 -0
- package/dist/dateParse-DJXMfq3a.js.map +1 -0
- package/dist/exportFormats-CWWiy5uz.js +108 -0
- package/dist/exportFormats-CWWiy5uz.js.map +1 -0
- package/dist/exportStream-BaheQ6M4.js +130 -0
- package/dist/exportStream-BaheQ6M4.js.map +1 -0
- package/dist/imessage-db-BVDtx0Sn.js +2263 -0
- package/dist/imessage-db-BVDtx0Sn.js.map +1 -0
- package/dist/index.js +3503 -0
- package/dist/index.js.map +1 -0
- package/dist/meta-D3NoTAjA.js +7 -0
- package/dist/meta-D3NoTAjA.js.map +1 -0
- package/dist/setup-DMckHRnI.js +103 -0
- package/dist/setup-DMckHRnI.js.map +1 -0
- package/dist/shutdown-B9ClCyco.js +775 -0
- package/dist/shutdown-B9ClCyco.js.map +1 -0
- package/dist/tui-config-Crn6TZPg.js +122 -0
- package/dist/tui-config-Crn6TZPg.js.map +1 -0
- package/dist/tui.js +2706 -0
- package/dist/tui.js.map +1 -0
- package/dist/watchdog-V3lgEhMp.js +207 -0
- package/dist/watchdog-V3lgEhMp.js.map +1 -0
- package/native/imsg-native.darwin-arm64.node +0 -0
- package/native/index.d.ts +86 -0
- package/native/index.js +582 -0
- package/package.json +135 -0
- package/skills/imsg-mcp/SKILL.md +168 -0
package/package.json
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "imsg-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server, CLI, and TUI for reading and sending iMessage / SMS on macOS — lets AI agents text humans, fetch conversation history, and stream-export entire chats. Includes a self-healing watchdog and an optional Rust acceleration module.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"imsg": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"native/imsg-native.darwin-arm64.node",
|
|
13
|
+
"native/imsg-native.darwin-x64.node",
|
|
14
|
+
"native/index.js",
|
|
15
|
+
"native/index.d.ts",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"CHANGELOG.md",
|
|
19
|
+
"skills"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"model-context-protocol",
|
|
24
|
+
"imessage",
|
|
25
|
+
"messages",
|
|
26
|
+
"sms",
|
|
27
|
+
"macos",
|
|
28
|
+
"ai-agent",
|
|
29
|
+
"ai",
|
|
30
|
+
"anthropic",
|
|
31
|
+
"claude",
|
|
32
|
+
"applescript",
|
|
33
|
+
"tui",
|
|
34
|
+
"terminal-ui",
|
|
35
|
+
"cli",
|
|
36
|
+
"chat-export"
|
|
37
|
+
],
|
|
38
|
+
"author": "George G",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/george43g/imsg-mcp.git"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/george43g/imsg-mcp#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/george43g/imsg-mcp/issues"
|
|
46
|
+
},
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@inkjs/ui": "^2.0.0",
|
|
50
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
51
|
+
"better-sqlite3": "^12.6.2",
|
|
52
|
+
"bplist-parser": "^0.3.2",
|
|
53
|
+
"commander": "^14.0.3",
|
|
54
|
+
"fullscreen-ink": "^0.1.0",
|
|
55
|
+
"imsg-mcp": "link:",
|
|
56
|
+
"ink": "^7.0.1",
|
|
57
|
+
"react": "^19.2.5",
|
|
58
|
+
"zod": "^3.23.0",
|
|
59
|
+
"zod-to-json-schema": "^3.25.2"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@biomejs/biome": "^2.0.0",
|
|
63
|
+
"@napi-rs/cli": "^3.6.2",
|
|
64
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
65
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
66
|
+
"@semantic-release/git": "^10.0.1",
|
|
67
|
+
"@semantic-release/github": "^12.0.6",
|
|
68
|
+
"@semantic-release/npm": "^13.1.5",
|
|
69
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
70
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
71
|
+
"@types/node": "^20.0.0",
|
|
72
|
+
"@types/react": "^19.2.14",
|
|
73
|
+
"ink-testing-library": "^4.0.0",
|
|
74
|
+
"nodemon": "^3.1.14",
|
|
75
|
+
"semantic-release": "^25.0.3",
|
|
76
|
+
"tsx": "^4.19.0",
|
|
77
|
+
"typescript": "^5.5.0",
|
|
78
|
+
"vite": "^7.3.1",
|
|
79
|
+
"vitest": "^2.0.0"
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=24",
|
|
83
|
+
"npm": "not in use"
|
|
84
|
+
},
|
|
85
|
+
"volta": {
|
|
86
|
+
"node": "24.13.0"
|
|
87
|
+
},
|
|
88
|
+
"scripts": {
|
|
89
|
+
"build": "pnpm build:ts && pnpm build:native:optional",
|
|
90
|
+
"build:ts": "vite build",
|
|
91
|
+
"build:native": "cd native && napi build --platform --release",
|
|
92
|
+
"build:native:debug": "cd native && napi build --platform",
|
|
93
|
+
"build:native:optional": "cd native && (napi build --platform --release || (echo '⚠️ Rust native build skipped (rustc not installed). TS fallback will be used.' && exit 0))",
|
|
94
|
+
"dev": "vite build --watch",
|
|
95
|
+
"mcp": "node --env-file=.env --env-file-if-exists=.env.local dist/cli.js mcp",
|
|
96
|
+
"mcp:ai": "node --env-file=.env --env-file-if-exists=.env.ai dist/cli.js mcp",
|
|
97
|
+
"mcp:test": "node --env-file=.env.test dist/cli.js mcp",
|
|
98
|
+
"mcp:test:no-native": "IMSG_DISABLE_NATIVE=1 node --env-file=.env.test dist/cli.js mcp",
|
|
99
|
+
"mcp:no-native": "IMSG_DISABLE_NATIVE=1 node --env-file=.env --env-file-if-exists=.env.local dist/cli.js mcp",
|
|
100
|
+
"dev:mcp": "tsx src/cli.ts mcp",
|
|
101
|
+
"dev:mcp:watch": "nodemon --ext ts --exec 'tsx src/cli.ts mcp'",
|
|
102
|
+
"start": "node --env-file=.env --env-file-if-exists=.env.local dist/cli.js",
|
|
103
|
+
"debug": "node --env-file=.env --env-file-if-exists=.env.local dist/cli.js cli",
|
|
104
|
+
"cli": "node --env-file=.env --env-file-if-exists=.env.local dist/cli.js cli",
|
|
105
|
+
"cli:test": "node --env-file=.env.test dist/cli.js cli",
|
|
106
|
+
"tui": "node --env-file=.env --env-file-if-exists=.env.local dist/cli.js tui",
|
|
107
|
+
"tui:test": "node --env-file=.env.test dist/cli.js tui",
|
|
108
|
+
"tui:no-native": "IMSG_DISABLE_NATIVE=1 node --env-file=.env --env-file-if-exists=.env.local dist/cli.js tui",
|
|
109
|
+
"tui:test:no-native": "IMSG_DISABLE_NATIVE=1 node --env-file=.env.test dist/cli.js tui",
|
|
110
|
+
"doctor": "node --env-file=.env --env-file-if-exists=.env.local dist/cli.js doctor",
|
|
111
|
+
"test": "vitest run",
|
|
112
|
+
"test:native": "vitest run --mode development",
|
|
113
|
+
"test:no-native": "IMSG_DISABLE_NATIVE=1 vitest run",
|
|
114
|
+
"test:watch": "vitest",
|
|
115
|
+
"test:rust": "cd native && cargo test --release",
|
|
116
|
+
"verify": "pnpm lint && pnpm typecheck && pnpm test && pnpm build",
|
|
117
|
+
"lint": "biome check .",
|
|
118
|
+
"typecheck": "tsc --noEmit",
|
|
119
|
+
"fixtures": "tsx scripts/generate-fixtures.ts",
|
|
120
|
+
"fixtures:fresh": "rm -rf fixtures && pnpm fixtures",
|
|
121
|
+
"fixtures:stress": "tsx scripts/generate-fixtures.ts --stress --out fixtures-stress",
|
|
122
|
+
"stress:tui": "tsx scripts/stress-tui.ts",
|
|
123
|
+
"screenshots": "fc-list | grep -qi 'jetbrains mono' || { echo 'Install: brew install --cask font-jetbrains-mono'; exit 1; }; for f in scripts/screenshots/*.tape; do vhs \"$f\"; done",
|
|
124
|
+
"screenshots:check": "pnpm screenshots && git diff --exit-code docs/screenshots/",
|
|
125
|
+
"screenshots:native": "for f in scripts/screenshots/native/*.sh; do echo \"→ $f\"; bash \"$f\" || echo \" (script failed; continuing)\"; done",
|
|
126
|
+
"hook:install": "git config core.hooksPath .githooks && echo '✓ pre-push hook installed (regenerates screenshots before push on macOS)'",
|
|
127
|
+
"pack:mcpb": "pnpm build && rm -rf release/mcpb && mkdir -p release/mcpb && cp -R dist dxt-manifest.json release/mcpb/ && (cd release/mcpb && zip -r ../imsg-mcp.mcpb . -x '*.DS_Store') && echo 'Built release/imsg-mcp.mcpb'",
|
|
128
|
+
"fixtures:if-missing": "node -e \"if(!require('fs').existsSync('fixtures/chat.db')){require('child_process').execSync('pnpm fixtures', {stdio:'inherit'})}\"",
|
|
129
|
+
"rebuild": "pnpm rebuild better-sqlite3",
|
|
130
|
+
"native:build": "cd native && napi build --platform --release",
|
|
131
|
+
"native:build:debug": "cd native && napi build --platform",
|
|
132
|
+
"stress": "tsx scripts/stress-mcp.ts .env.test",
|
|
133
|
+
"stress:live": "tsx scripts/stress-mcp.ts .env.local"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# imsg-mcp
|
|
2
|
+
|
|
3
|
+
Companion skill for agents using or developing the `imsg-mcp` iMessage MCP server.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- When an agent needs to text the human for clarification or approval.
|
|
8
|
+
- When an agent should wait for an iMessage reply before continuing.
|
|
9
|
+
- When an agent needs to inspect recent conversations or unread messages.
|
|
10
|
+
- When developing/debugging the imsg-mcp codebase itself.
|
|
11
|
+
|
|
12
|
+
## Quick start
|
|
13
|
+
|
|
14
|
+
1. Run `imsg doctor` on a new machine to check permissions.
|
|
15
|
+
2. Use `list_conversations` to identify the correct thread (returns `threadSlug`).
|
|
16
|
+
3. Use `send_message` with `threadSlug` for existing threads, especially groups.
|
|
17
|
+
4. Use `wait_for_reply` after sending when the workflow depends on a human answer.
|
|
18
|
+
|
|
19
|
+
## Local machine requirements
|
|
20
|
+
|
|
21
|
+
- macOS only for live reads and sends.
|
|
22
|
+
- Full Disk Access required to read `~/Library/Messages/chat.db` and Address Book.
|
|
23
|
+
- Messages.app must be open to send messages.
|
|
24
|
+
- Node >= 24 (see `package.json` / Volta).
|
|
25
|
+
|
|
26
|
+
## Git LFS (required in cloud / CI / fresh clones)
|
|
27
|
+
|
|
28
|
+
Large binaries (`*.db`, `*.abcddb`) are stored with Git LFS. A normal clone may leave pointer stubs.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
git lfs install # once per machine
|
|
32
|
+
git lfs pull # before pnpm install / tests / running against env-data/
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Thread slugs
|
|
36
|
+
|
|
37
|
+
Stable, human-readable IDs for conversations: `{name}~{service}~{4-hex}` (e.g. `alice~imsg~a3f2`).
|
|
38
|
+
|
|
39
|
+
- `list_conversations` returns `threadSlug` per row
|
|
40
|
+
- `send_message` accepts `threadSlug` or `recipient`
|
|
41
|
+
- `wait_for_reply` accepts `threadSlug` or `chatIdentifier`
|
|
42
|
+
- See `src/thread-slug.ts`, `src/slug-store.ts`
|
|
43
|
+
|
|
44
|
+
## Binaries
|
|
45
|
+
|
|
46
|
+
| Binary | Purpose |
|
|
47
|
+
|--------|---------|
|
|
48
|
+
| `imsg mcp` | MCP stdio server (used by AI hosts like Claude, Cursor) |
|
|
49
|
+
| `imsg cli` | Interactive CLI with REPL |
|
|
50
|
+
| `imsg tui` | Full-screen TUI (Ink/React) |
|
|
51
|
+
|
|
52
|
+
## TUI (`imsg`)
|
|
53
|
+
|
|
54
|
+
Vim-style keybindings: `j/k` move, `gg/G` top/bottom, `Ctrl-d/u` half-page, `{/}` group-jump, `Enter` message details, `o` open attachment, `y` copy slug, `d` toggle dev stats, `Tab` switch panes, `/` filter, `c` compose, `q` quit.
|
|
55
|
+
|
|
56
|
+
## Native Rust module (optional)
|
|
57
|
+
|
|
58
|
+
`native/` contains a Rust acceleration module (`napi-rs` + `rusqlite` + `rayon`). Build with `pnpm native:build`. Falls back to TypeScript automatically if not built. The dev stats panel (`d` key in TUI) shows which engine is active.
|
|
59
|
+
|
|
60
|
+
## Tool limits
|
|
61
|
+
|
|
62
|
+
`limit: 0` = unlimited (bounded only by the per-tool timeout). No upper cap.
|
|
63
|
+
Defaults: 20 for most tools, 100 for `get_unread_messages`.
|
|
64
|
+
|
|
65
|
+
## Self-healing watchdog
|
|
66
|
+
|
|
67
|
+
`src/watchdog.ts` runs three monitors that self-kill (so the host respawns) on:
|
|
68
|
+
- Event-loop p99 lag > 10s
|
|
69
|
+
- RSS > 1GB or monotonic heap growth across 10 × 60s samples
|
|
70
|
+
- Uptime > 24h with no activity in the last hour
|
|
71
|
+
|
|
72
|
+
All thresholds are env-overridable (`IMSG_EVENT_LOOP_KILL_MS`, `IMSG_MAX_RSS_MB`, etc.).
|
|
73
|
+
|
|
74
|
+
## MCP cancellation
|
|
75
|
+
|
|
76
|
+
The server honors `notifications/cancelled`. Long-running handlers (`wait_for_reply`) abort cleanly when the host cancels.
|
|
77
|
+
|
|
78
|
+
## MCP pagination & export
|
|
79
|
+
|
|
80
|
+
- `get_messages` returns a footer with `oldestMessageId`. Pass it as `beforeMessageId` to paginate. Hard cap 5000/call.
|
|
81
|
+
- `export_messages` streams a chat to a file (markdown/csv/json/ndjson) — use this instead of huge `get_messages` calls.
|
|
82
|
+
|
|
83
|
+
## TUI date jump + selection + export
|
|
84
|
+
|
|
85
|
+
- `:` open date-jump (e.g. `1 year ago`, `2024-01-15`).
|
|
86
|
+
- `V` enter visual select; `e` export selection; `y` copy text.
|
|
87
|
+
|
|
88
|
+
## Bounded memory
|
|
89
|
+
|
|
90
|
+
When loaded messages exceed `IMSG_TUI_MSG_HARD_CAP` (default 5000), middle is evicted; first 200 (anchor) + window around cursor stay. Gap markers show evicted regions.
|
|
91
|
+
|
|
92
|
+
## TUI lazy loading + cache
|
|
93
|
+
|
|
94
|
+
- 200 conversations load initially; another 100 lazy-load when within 20 of the end.
|
|
95
|
+
- Older messages lazy-load when scrolling within 10 of the top of a thread.
|
|
96
|
+
- Per-chat cache holds messages for fast re-entry. Tunable: `IMSG_TUI_CACHE_TTL_MS`, `IMSG_TUI_CACHE_STALE_MS`, `IMSG_TUI_CACHE_MEM_PRESSURE_MB`.
|
|
97
|
+
|
|
98
|
+
## Debugging & Logs
|
|
99
|
+
|
|
100
|
+
### MCP tool: `health_check`
|
|
101
|
+
|
|
102
|
+
Returns vital signs (uptime, heap, RSS, event-loop p99, tool calls, engine) in milliseconds — works even when the DB is wedged.
|
|
103
|
+
|
|
104
|
+
### MCP tool: `get_logs`
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
get_logs({ tail: 50, source: "all" })
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- `source: "memory"` — in-process buffer (default)
|
|
111
|
+
- `source: "file"` — NDJSON log from `$TMPDIR/imsg-mcp/`
|
|
112
|
+
- `source: "all"` — both
|
|
113
|
+
|
|
114
|
+
### Log files
|
|
115
|
+
|
|
116
|
+
Full NDJSON logs are written to `$TMPDIR/imsg-mcp/imsg-mcp-{PID}-{date}.ndjson`. These persist across restarts and contain:
|
|
117
|
+
|
|
118
|
+
- `level: "perf"` + `dur_ms` — performance spans for every DB query
|
|
119
|
+
- `level: "info"`, `msg: "heartbeat"` — periodic memory/uptime (every 60s)
|
|
120
|
+
- `msg: "startup"` — process start marker (with PID, node version)
|
|
121
|
+
- `msg: "shutdown"` — graceful exit marker (with uptime, reason)
|
|
122
|
+
|
|
123
|
+
**Crash detection:** If a log file has no `"shutdown"` entry, the process crashed or hung.
|
|
124
|
+
|
|
125
|
+
### Process lifecycle
|
|
126
|
+
|
|
127
|
+
- `src/shutdown.ts` — central cleanup registry, signal handlers (SIGINT, SIGTERM, SIGHUP, SIGQUIT)
|
|
128
|
+
- Orphan detection: parent PID watchdog (detects reparenting to launchd) + stdin EOF detection (MCP host died)
|
|
129
|
+
- After TUI crashes, check for orphans: `ps aux | grep imsg`
|
|
130
|
+
|
|
131
|
+
## Environment variables (Vite / Vitest)
|
|
132
|
+
|
|
133
|
+
Precedence: `.env` → `.env.local` → `.env.[mode]` → `.env.[mode].local`
|
|
134
|
+
|
|
135
|
+
| File | Role |
|
|
136
|
+
|------|------|
|
|
137
|
+
| `.env` | Baseline: `VITE_ENV=development` |
|
|
138
|
+
| `.env.local` | Machine paths (`VITE_IMSG_DB_PATH`, etc.) |
|
|
139
|
+
| `.env.test` | `VITE_ENV=ai` + `env-data/` paths for `pnpm test` |
|
|
140
|
+
|
|
141
|
+
- `pnpm test` — Vitest mode `test` (loads `.env.test`)
|
|
142
|
+
- `pnpm test:native` — mode `development` (Mac paths from `.env.local`)
|
|
143
|
+
- Vitest always mocks AppleScript sends (`VITEST=true`)
|
|
144
|
+
|
|
145
|
+
## Code map
|
|
146
|
+
|
|
147
|
+
| Area | Location |
|
|
148
|
+
|------|----------|
|
|
149
|
+
| MCP tools / Zod | `src/index.ts` |
|
|
150
|
+
| SQLite messages | `src/imessage-db.ts` |
|
|
151
|
+
| Blob parsing | `src/attributed-body-text.ts`, `src/parsers/typedstream-parser.ts` |
|
|
152
|
+
| Contacts | `src/contacts-db.ts` |
|
|
153
|
+
| Slugs | `src/thread-slug.ts`, `src/slug-store.ts` |
|
|
154
|
+
| Send / mock | `src/applescript.ts`, `src/mock-send-db.ts` |
|
|
155
|
+
| TUI | `src/tui/` (Ink/React) |
|
|
156
|
+
| Native module | `native/` (Rust + napi-rs) |
|
|
157
|
+
| Shutdown | `src/shutdown.ts` |
|
|
158
|
+
| Logging | `src/logger.ts` |
|
|
159
|
+
| Config | `src/config.ts` |
|
|
160
|
+
| Types | `src/types.ts` |
|
|
161
|
+
| DB schema | `docs/IMESSAGE_DB_SCHEMA.md` |
|
|
162
|
+
|
|
163
|
+
## Security / guardrails
|
|
164
|
+
|
|
165
|
+
- Treat iMessage data as sensitive local user data.
|
|
166
|
+
- Never publish private message content.
|
|
167
|
+
- Confirm before sending messages on behalf of the user.
|
|
168
|
+
- See `AGENTS.md` for thread isolation and MCP guardrails.
|