@heznpc/imcp 0.7.1 → 0.9.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/README.ko.md +30 -3
- package/README.md +30 -3
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/messages/scripts.d.ts +6 -0
- package/dist/messages/scripts.js +103 -0
- package/dist/messages/scripts.js.map +1 -0
- package/dist/messages/tools.d.ts +3 -0
- package/dist/messages/tools.js +100 -0
- package/dist/messages/tools.js.map +1 -0
- package/package.json +5 -2
package/README.ko.md
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
# iMcp
|
|
2
2
|
|
|
3
|
-
Apple 생태계 전체를 위한 MCP 서버 — Notes, Reminders, Calendar, Contacts, Mail, Music, Finder, Safari, System, Photos, Shortcuts, Apple Intelligence. AI를 Mac에 연결합니다.
|
|
3
|
+
Apple 생태계 전체를 위한 MCP 서버 — Notes, Reminders, Calendar, Contacts, Mail, Messages, Music, Finder, Safari, System, Photos, Shortcuts, Apple Intelligence. AI를 Mac에 연결합니다.
|
|
4
4
|
|
|
5
5
|
> [English](README.md)
|
|
6
6
|
|
|
7
7
|
## 특징
|
|
8
8
|
|
|
9
|
-
- **
|
|
9
|
+
- **110개 도구** (13개 모듈) — Apple 앱 CRUD + 시스템 제어 + Apple Intelligence
|
|
10
10
|
- **20개 프롬프트** — 앱별 워크플로우 + 크로스 모듈 + 개발자 워크플로우 (dev-session, debug-loop, build-log)
|
|
11
11
|
- **4개 MCP 리소스** — Notes, Calendar, Reminders 실시간 데이터 URI
|
|
12
12
|
- **JXA + Swift 브릿지** — JXA로 기본 자동화, EventKit/PhotoKit으로 고급 기능
|
|
13
13
|
- **반복 이벤트/리마인더** — EventKit으로 반복 규칙 생성 (macOS 26+ Swift 브릿지)
|
|
14
14
|
- **사진 가져오기/삭제** — PhotoKit으로 사진 관리 (macOS 26+ Swift 브릿지)
|
|
15
15
|
- **Apple Intelligence** — 온디바이스 요약, 재작성, 교정 (macOS 26+)
|
|
16
|
-
-
|
|
16
|
+
- **네이티브 메뉴바 앱** — SwiftUI 컴패니언 앱으로 상태 모니터링, 권한 설정, 설정 복사
|
|
17
|
+
- **원클릭 셋업** — `setup_permissions` 도구 또는 메뉴바 앱으로 모든 macOS 권한을 한번에 요청
|
|
17
18
|
- **stdio 전송** — 안전한 로컬 통신, 네트워크 노출 없음
|
|
18
19
|
- **Safety Annotations** — 모든 도구에 readOnly/destructive 힌트 적용
|
|
19
20
|
|
|
@@ -49,6 +50,17 @@ npm install
|
|
|
49
50
|
npm run build
|
|
50
51
|
```
|
|
51
52
|
|
|
53
|
+
### 메뉴바 앱 (선택)
|
|
54
|
+
|
|
55
|
+
서버 상태 모니터링과 권한 설정을 위한 네이티브 SwiftUI 컴패니언 앱.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cd app && swift build -c release
|
|
59
|
+
# 바이너리: app/.build/release/iMcpApp
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
기능: 서버 상태 표시, 원클릭 권한 설정, Claude 설정 클립보드 복사.
|
|
63
|
+
|
|
52
64
|
## 도구
|
|
53
65
|
|
|
54
66
|
### Notes (12개)
|
|
@@ -197,6 +209,17 @@ npm run build
|
|
|
197
209
|
| `import_photo` | 파일에서 사진 가져오기 (Swift/PhotoKit) | 쓰기 |
|
|
198
210
|
| `delete_photos` | ID로 사진 삭제 (Swift/PhotoKit) | 파괴적 |
|
|
199
211
|
|
|
212
|
+
### Messages (6개)
|
|
213
|
+
|
|
214
|
+
| 도구 | 설명 | 타입 |
|
|
215
|
+
|------|------|------|
|
|
216
|
+
| `list_chats` | 최근 대화 목록 (참여자 포함) | 읽기 |
|
|
217
|
+
| `read_chat` | 대화 상세 조회 (참여자, 마지막 업데이트 포함) | 읽기 |
|
|
218
|
+
| `search_chats` | 이름/참여자/핸들로 검색 | 읽기 |
|
|
219
|
+
| `send_message` | iMessage/SMS 텍스트 전송 | 쓰기 |
|
|
220
|
+
| `send_file` | iMessage/SMS 파일 첨부 전송 | 쓰기 |
|
|
221
|
+
| `list_participants` | 대화 참여자 목록 | 읽기 |
|
|
222
|
+
|
|
200
223
|
### Shortcuts (4개)
|
|
201
224
|
|
|
202
225
|
| 도구 | 설명 | 타입 |
|
|
@@ -319,6 +342,10 @@ AI 코딩 에이전트(Claude Code, Cursor, Copilot)가 MCP 프롬프트로 호
|
|
|
319
342
|
### Mail
|
|
320
343
|
- 내용 기본 5000자 제한 (`maxLength` 파라미터로 조절 가능)
|
|
321
344
|
|
|
345
|
+
### Messages
|
|
346
|
+
- 개별 메시지 내용(대화 기록)은 JXA로 접근 불가
|
|
347
|
+
- 전송 시 수신자가 Messages 서비스에 등록된 buddy여야 함
|
|
348
|
+
|
|
322
349
|
### Music
|
|
323
350
|
- 스마트 재생목록 읽기 전용
|
|
324
351
|
- 대기열 조작 불가
|
package/README.md
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
# iMcp
|
|
2
2
|
|
|
3
|
-
MCP server for the entire Apple ecosystem — Notes, Reminders, Calendar, Contacts, Mail, Music, Finder, Safari, System, Photos, Shortcuts, and Apple Intelligence. Connect any AI to your Mac.
|
|
3
|
+
MCP server for the entire Apple ecosystem — Notes, Reminders, Calendar, Contacts, Mail, Messages, Music, Finder, Safari, System, Photos, Shortcuts, and Apple Intelligence. Connect any AI to your Mac.
|
|
4
4
|
|
|
5
5
|
> [한국어](README.ko.md)
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- **
|
|
9
|
+
- **110 tools** across 13 modules — full CRUD for Apple apps + system control + Apple Intelligence
|
|
10
10
|
- **20 prompts** — per-app workflows + cross-module + developer workflows (dev-session, debug-loop, build-log)
|
|
11
11
|
- **4 MCP Resources** — live data URIs for notes, calendar, and reminders
|
|
12
12
|
- **JXA + Swift bridge** — JXA for core automation, EventKit/PhotoKit via Swift for advanced features
|
|
13
13
|
- **Recurring events/reminders** — create recurrence rules via EventKit (macOS 26+ Swift bridge)
|
|
14
14
|
- **Photos import/delete** — import and delete photos via PhotoKit (macOS 26+ Swift bridge)
|
|
15
15
|
- **Apple Intelligence** — on-device summarization, rewriting, proofreading (macOS 26+)
|
|
16
|
-
- **
|
|
16
|
+
- **Native menubar app** — SwiftUI companion app for status monitoring, permission setup, config copy
|
|
17
|
+
- **One-click setup** — `setup_permissions` tool or menubar app triggers all macOS permission prompts at once
|
|
17
18
|
- **stdio transport** — secure local communication, no network exposure
|
|
18
19
|
- **Safety annotations** — every tool tagged with readOnly/destructive hints
|
|
19
20
|
|
|
@@ -49,6 +50,17 @@ npm install
|
|
|
49
50
|
npm run build
|
|
50
51
|
```
|
|
51
52
|
|
|
53
|
+
### Menubar App (Optional)
|
|
54
|
+
|
|
55
|
+
A native SwiftUI companion app for server status monitoring and permission setup.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cd app && swift build -c release
|
|
59
|
+
# Binary: app/.build/release/iMcpApp
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Features: server status indicator, one-click permission setup, copy Claude config to clipboard.
|
|
63
|
+
|
|
52
64
|
## Tools
|
|
53
65
|
|
|
54
66
|
### Notes (12 tools)
|
|
@@ -197,6 +209,17 @@ npm run build
|
|
|
197
209
|
| `import_photo` | Import from file path (Swift/PhotoKit) | write |
|
|
198
210
|
| `delete_photos` | Delete photos by ID (Swift/PhotoKit) | destructive |
|
|
199
211
|
|
|
212
|
+
### Messages (6 tools)
|
|
213
|
+
|
|
214
|
+
| Tool | Description | Type |
|
|
215
|
+
|------|-------------|------|
|
|
216
|
+
| `list_chats` | List recent chats with participants | read |
|
|
217
|
+
| `read_chat` | Read chat details including participants and last update | read |
|
|
218
|
+
| `search_chats` | Search by name, participant, or handle | read |
|
|
219
|
+
| `send_message` | Send text via iMessage/SMS | write |
|
|
220
|
+
| `send_file` | Send file attachment via iMessage/SMS | write |
|
|
221
|
+
| `list_participants` | List participants in a chat | read |
|
|
222
|
+
|
|
200
223
|
### Shortcuts (4 tools)
|
|
201
224
|
|
|
202
225
|
| Tool | Description | Type |
|
|
@@ -319,6 +342,10 @@ These prompts are designed for AI coding agents (Claude Code, Cursor, Copilot) t
|
|
|
319
342
|
### Mail
|
|
320
343
|
- Content truncated to 5000 characters by default (configurable via `maxLength` parameter)
|
|
321
344
|
|
|
345
|
+
### Messages
|
|
346
|
+
- Chat message history (individual message content) is not accessible via JXA
|
|
347
|
+
- Send requires the recipient to be a known buddy in the Messages service
|
|
348
|
+
|
|
322
349
|
### Music
|
|
323
350
|
- Smart playlists are read-only
|
|
324
351
|
- No queue manipulation
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { registerSafariTools } from "./safari/tools.js";
|
|
|
15
15
|
import { registerSystemTools } from "./system/tools.js";
|
|
16
16
|
import { registerPhotosTools } from "./photos/tools.js";
|
|
17
17
|
import { registerShortcutsTools } from "./shortcuts/tools.js";
|
|
18
|
+
import { registerMessagesTools } from "./messages/tools.js";
|
|
18
19
|
import { registerIntelligenceTools } from "./intelligence/tools.js";
|
|
19
20
|
import { registerCrossPrompts } from "./cross/prompts.js";
|
|
20
21
|
import { registerResources } from "./shared/resources.js";
|
|
@@ -23,7 +24,7 @@ import { parseConfig } from "./shared/config.js";
|
|
|
23
24
|
const config = parseConfig();
|
|
24
25
|
const server = new McpServer({
|
|
25
26
|
name: "imcp",
|
|
26
|
-
version: "0.
|
|
27
|
+
version: "0.9.0",
|
|
27
28
|
});
|
|
28
29
|
// Apple Apps
|
|
29
30
|
registerNoteTools(server, config);
|
|
@@ -40,6 +41,7 @@ registerSafariTools(server, config);
|
|
|
40
41
|
registerSystemTools(server, config);
|
|
41
42
|
registerPhotosTools(server, config);
|
|
42
43
|
registerShortcutsTools(server, config);
|
|
44
|
+
registerMessagesTools(server, config);
|
|
43
45
|
// Apple Intelligence
|
|
44
46
|
registerIntelligenceTools(server, config);
|
|
45
47
|
// Cross-module workflows
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;AAE7B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,aAAa;AACb,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAClC,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC5B,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACtC,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAChC,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACtC,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAChC,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACrC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAClC,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACnC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACpC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACpC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACpC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACpC,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function listChatsScript(limit: number): string;
|
|
2
|
+
export declare function readChatScript(chatId: string): string;
|
|
3
|
+
export declare function searchMessagesScript(query: string, limit: number): string;
|
|
4
|
+
export declare function sendMessageScript(target: string, text: string): string;
|
|
5
|
+
export declare function sendFileScript(target: string, filePath: string): string;
|
|
6
|
+
export declare function listParticipantsScript(chatId: string): string;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// JXA scripts for Apple Messages automation.
|
|
2
|
+
import { esc } from "../shared/esc.js";
|
|
3
|
+
export function listChatsScript(limit) {
|
|
4
|
+
return `
|
|
5
|
+
const Messages = Application('Messages');
|
|
6
|
+
const chats = Messages.chats();
|
|
7
|
+
const count = Math.min(chats.length, ${limit});
|
|
8
|
+
const result = [];
|
|
9
|
+
for (let i = 0; i < count; i++) {
|
|
10
|
+
const c = chats[i];
|
|
11
|
+
const participants = c.participants();
|
|
12
|
+
result.push({
|
|
13
|
+
id: c.id(),
|
|
14
|
+
name: c.name() || null,
|
|
15
|
+
participants: participants.map(p => ({
|
|
16
|
+
name: p.name(),
|
|
17
|
+
handle: p.handle()
|
|
18
|
+
})),
|
|
19
|
+
updated: c.updatedDate() ? c.updatedDate().toISOString() : null
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
JSON.stringify({total: chats.length, returned: count, chats: result});
|
|
23
|
+
`;
|
|
24
|
+
}
|
|
25
|
+
export function readChatScript(chatId) {
|
|
26
|
+
return `
|
|
27
|
+
const Messages = Application('Messages');
|
|
28
|
+
const chats = Messages.chats.whose({id: '${esc(chatId)}'})();
|
|
29
|
+
if (chats.length === 0) throw new Error('Chat not found: ${esc(chatId)}');
|
|
30
|
+
const chat = chats[0];
|
|
31
|
+
const participants = chat.participants();
|
|
32
|
+
JSON.stringify({
|
|
33
|
+
id: chat.id(),
|
|
34
|
+
name: chat.name() || null,
|
|
35
|
+
participants: participants.map(p => ({name: p.name(), handle: p.handle()})),
|
|
36
|
+
updated: chat.updatedDate() ? chat.updatedDate().toISOString() : null
|
|
37
|
+
});
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
export function searchMessagesScript(query, limit) {
|
|
41
|
+
return `
|
|
42
|
+
const Messages = Application('Messages');
|
|
43
|
+
const chats = Messages.chats();
|
|
44
|
+
const q = '${esc(query)}'.toLowerCase();
|
|
45
|
+
const result = [];
|
|
46
|
+
for (const chat of chats) {
|
|
47
|
+
if (result.length >= ${limit}) break;
|
|
48
|
+
const name = chat.name() || '';
|
|
49
|
+
const participants = chat.participants();
|
|
50
|
+
const participantNames = participants.map(p => p.name() || '').join(' ');
|
|
51
|
+
const participantHandles = participants.map(p => p.handle() || '').join(' ');
|
|
52
|
+
if (name.toLowerCase().includes(q) ||
|
|
53
|
+
participantNames.toLowerCase().includes(q) ||
|
|
54
|
+
participantHandles.toLowerCase().includes(q)) {
|
|
55
|
+
result.push({
|
|
56
|
+
id: chat.id(),
|
|
57
|
+
name: name || null,
|
|
58
|
+
participants: participants.map(p => ({name: p.name(), handle: p.handle()})),
|
|
59
|
+
updated: chat.updatedDate() ? chat.updatedDate().toISOString() : null
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
JSON.stringify({returned: result.length, chats: result});
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
export function sendMessageScript(target, text) {
|
|
67
|
+
return `
|
|
68
|
+
const Messages = Application('Messages');
|
|
69
|
+
const service = Messages.services().find(s => s.serviceType() === 'iMessage') || Messages.services()[0];
|
|
70
|
+
if (!service) throw new Error('No messaging service available');
|
|
71
|
+
const buddy = service.buddies.whose({handle: '${esc(target)}'})();
|
|
72
|
+
if (buddy.length === 0) throw new Error('Buddy not found for handle: ${esc(target)}');
|
|
73
|
+
Messages.send('${esc(text)}', {to: buddy[0]});
|
|
74
|
+
JSON.stringify({sent: true, to: '${esc(target)}', text: '${esc(text)}'.substring(0, 100)});
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
export function sendFileScript(target, filePath) {
|
|
78
|
+
return `
|
|
79
|
+
const Messages = Application('Messages');
|
|
80
|
+
const service = Messages.services().find(s => s.serviceType() === 'iMessage') || Messages.services()[0];
|
|
81
|
+
if (!service) throw new Error('No messaging service available');
|
|
82
|
+
const buddy = service.buddies.whose({handle: '${esc(target)}'})();
|
|
83
|
+
if (buddy.length === 0) throw new Error('Buddy not found for handle: ${esc(target)}');
|
|
84
|
+
const file = Path('${esc(filePath)}');
|
|
85
|
+
Messages.send(file, {to: buddy[0]});
|
|
86
|
+
JSON.stringify({sent: true, to: '${esc(target)}', file: '${esc(filePath)}'});
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
export function listParticipantsScript(chatId) {
|
|
90
|
+
return `
|
|
91
|
+
const Messages = Application('Messages');
|
|
92
|
+
const chats = Messages.chats.whose({id: '${esc(chatId)}'})();
|
|
93
|
+
if (chats.length === 0) throw new Error('Chat not found: ${esc(chatId)}');
|
|
94
|
+
const chat = chats[0];
|
|
95
|
+
const participants = chat.participants();
|
|
96
|
+
const result = participants.map(p => ({
|
|
97
|
+
name: p.name(),
|
|
98
|
+
handle: p.handle()
|
|
99
|
+
}));
|
|
100
|
+
JSON.stringify({chatId: '${esc(chatId)}', chatName: chat.name() || null, participants: result});
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=scripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../src/messages/scripts.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAE7C,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEvC,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO;;;2CAGkC,KAAK;;;;;;;;;;;;;;;;GAgB7C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO;;+CAEsC,GAAG,CAAC,MAAM,CAAC;+DACK,GAAG,CAAC,MAAM,CAAC;;;;;;;;;GASvE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,KAAa,EACb,KAAa;IAEb,OAAO;;;iBAGQ,GAAG,CAAC,KAAK,CAAC;;;6BAGE,KAAK;;;;;;;;;;;;;;;;;GAiB/B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAAc,EACd,IAAY;IAEZ,OAAO;;;;oDAI2C,GAAG,CAAC,MAAM,CAAC;2EACY,GAAG,CAAC,MAAM,CAAC;qBACjE,GAAG,CAAC,IAAI,CAAC;uCACS,GAAG,CAAC,MAAM,CAAC,aAAa,GAAG,CAAC,IAAI,CAAC;GACrE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,MAAc,EACd,QAAgB;IAEhB,OAAO;;;;oDAI2C,GAAG,CAAC,MAAM,CAAC;2EACY,GAAG,CAAC,MAAM,CAAC;yBAC7D,GAAG,CAAC,QAAQ,CAAC;;uCAEC,GAAG,CAAC,MAAM,CAAC,aAAa,GAAG,CAAC,QAAQ,CAAC;GACzE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,OAAO;;+CAEsC,GAAG,CAAC,MAAM,CAAC;+DACK,GAAG,CAAC,MAAM,CAAC;;;;;;;+BAO3C,GAAG,CAAC,MAAM,CAAC;GACvC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runJxa } from "../shared/jxa.js";
|
|
3
|
+
import { ok, err } from "../shared/result.js";
|
|
4
|
+
import { listChatsScript, readChatScript, searchMessagesScript, sendMessageScript, sendFileScript, listParticipantsScript, } from "./scripts.js";
|
|
5
|
+
export function registerMessagesTools(server, _config) {
|
|
6
|
+
server.registerTool("list_chats", {
|
|
7
|
+
title: "List Chats",
|
|
8
|
+
description: "List recent chats in Messages with participants and last update time.",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
limit: z.number().int().min(1).max(200).optional().default(50).describe("Max chats to return (default: 50)"),
|
|
11
|
+
},
|
|
12
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
13
|
+
}, async ({ limit }) => {
|
|
14
|
+
try {
|
|
15
|
+
return ok(await runJxa(listChatsScript(limit)));
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
return err(`Failed to list chats: ${e instanceof Error ? e.message : String(e)}`);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
server.registerTool("read_chat", {
|
|
22
|
+
title: "Read Chat",
|
|
23
|
+
description: "Read chat details including participants and last update time by chat ID.",
|
|
24
|
+
inputSchema: {
|
|
25
|
+
chatId: z.string().describe("Chat ID"),
|
|
26
|
+
},
|
|
27
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
28
|
+
}, async ({ chatId }) => {
|
|
29
|
+
try {
|
|
30
|
+
return ok(await runJxa(readChatScript(chatId)));
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
return err(`Failed to read chat: ${e instanceof Error ? e.message : String(e)}`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
server.registerTool("search_chats", {
|
|
37
|
+
title: "Search Chats",
|
|
38
|
+
description: "Search chats by participant name, handle, or chat name.",
|
|
39
|
+
inputSchema: {
|
|
40
|
+
query: z.string().describe("Search keyword (matches chat name, participant name, or handle)"),
|
|
41
|
+
limit: z.number().int().min(1).max(100).optional().default(20).describe("Max results (default: 20)"),
|
|
42
|
+
},
|
|
43
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
44
|
+
}, async ({ query, limit }) => {
|
|
45
|
+
try {
|
|
46
|
+
return ok(await runJxa(searchMessagesScript(query, limit)));
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
return err(`Failed to search chats: ${e instanceof Error ? e.message : String(e)}`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
server.registerTool("send_message", {
|
|
53
|
+
title: "Send Message",
|
|
54
|
+
description: "Send a text message via iMessage/SMS. Requires a phone number or email as the target handle.",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
target: z.string().describe("Recipient handle (phone number or email, e.g. '+821012345678' or 'user@example.com')"),
|
|
57
|
+
text: z.string().min(1).max(10000).describe("Message text to send"),
|
|
58
|
+
},
|
|
59
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
60
|
+
}, async ({ target, text }) => {
|
|
61
|
+
try {
|
|
62
|
+
return ok(await runJxa(sendMessageScript(target, text)));
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
return err(`Failed to send message: ${e instanceof Error ? e.message : String(e)}`);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
server.registerTool("send_file", {
|
|
69
|
+
title: "Send File",
|
|
70
|
+
description: "Send a file attachment via iMessage/SMS. Requires absolute file path and recipient handle.",
|
|
71
|
+
inputSchema: {
|
|
72
|
+
target: z.string().describe("Recipient handle (phone number or email)"),
|
|
73
|
+
filePath: z.string().describe("Absolute path to the file to send"),
|
|
74
|
+
},
|
|
75
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
76
|
+
}, async ({ target, filePath }) => {
|
|
77
|
+
try {
|
|
78
|
+
return ok(await runJxa(sendFileScript(target, filePath)));
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
return err(`Failed to send file: ${e instanceof Error ? e.message : String(e)}`);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
server.registerTool("list_participants", {
|
|
85
|
+
title: "List Chat Participants",
|
|
86
|
+
description: "List all participants in a specific chat.",
|
|
87
|
+
inputSchema: {
|
|
88
|
+
chatId: z.string().describe("Chat ID"),
|
|
89
|
+
},
|
|
90
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
91
|
+
}, async ({ chatId }) => {
|
|
92
|
+
try {
|
|
93
|
+
return ok(await runJxa(listParticipantsScript(chatId)));
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
return err(`Failed to list participants: ${e instanceof Error ? e.message : String(e)}`);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/messages/tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heznpc/imcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for the entire Apple ecosystem — Notes, Reminders, Calendar, Contacts, Mail, Music, Finder, Safari, Photos, Shortcuts, System, and Apple Intelligence. Connect any AI to your Mac.",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "MCP server for the entire Apple ecosystem — Notes, Reminders, Calendar, Contacts, Mail, Messages, Music, Finder, Safari, Photos, Shortcuts, System, and Apple Intelligence. Connect any AI to your Mac.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
7
7
|
"model-context-protocol",
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
"apple-contacts",
|
|
18
18
|
"mail",
|
|
19
19
|
"apple-mail",
|
|
20
|
+
"messages",
|
|
21
|
+
"imessage",
|
|
20
22
|
"music",
|
|
21
23
|
"apple-music",
|
|
22
24
|
"finder",
|
|
@@ -56,6 +58,7 @@
|
|
|
56
58
|
"scripts": {
|
|
57
59
|
"build": "tsc",
|
|
58
60
|
"swift-build": "cd swift && swift build -c release",
|
|
61
|
+
"app-build": "cd app && swift build -c release",
|
|
59
62
|
"clean": "rm -rf dist",
|
|
60
63
|
"dev": "tsx src/index.ts",
|
|
61
64
|
"lint": "eslint src/",
|