@mgcrea/mcp-apple-messages 1.31.0 → 1.34.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.md +13 -11
- package/dist/cli.js +5 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +158 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{server-C3O37bOh.js → server-D-2jgV-B.js} +562 -37
- package/dist/server-D-2jgV-B.js.map +1 -0
- package/package.json +6 -5
- package/dist/server-C3O37bOh.js.map +0 -1
package/README.md
CHANGED
|
@@ -126,17 +126,19 @@ records exactly what that leaves open; the safe way to measure it is a message t
|
|
|
126
126
|
|
|
127
127
|
## Configuration
|
|
128
128
|
|
|
129
|
-
| Variable | Default |
|
|
130
|
-
| ----------------------------------- | ------- |
|
|
131
|
-
| `APPLE_MESSAGES_RESOLVE_CONTACTS` | on | Look names up in Contacts.
|
|
132
|
-
| `APPLE_MESSAGES_INDEX_MODE` | `auto` | `auto` \| `ro` \| `immutable` \| `off`.
|
|
133
|
-
| `APPLE_MESSAGES_STORE` | auto | Explicit store path.
|
|
134
|
-
| `APPLE_MESSAGES_DEFAULT_RANGE_DAYS` | `30` | Window when only a start is given.
|
|
135
|
-
| `APPLE_MESSAGES_MAX_RESULTS` | `50` | Default page size.
|
|
136
|
-
| `APPLE_MESSAGES_ALLOW_WRITES` | off | Register `send_message` at all.
|
|
137
|
-
| `APPLE_MESSAGES_ALLOW_CODES` | off | Register `find_codes` at all.
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
129
|
+
| Variable | Default | |
|
|
130
|
+
| ----------------------------------- | ------- | ----------------------------------------------------------------------------------------------- |
|
|
131
|
+
| `APPLE_MESSAGES_RESOLVE_CONTACTS` | on | Look names up in Contacts. |
|
|
132
|
+
| `APPLE_MESSAGES_INDEX_MODE` | `auto` | `auto` \| `ro` \| `immutable` \| `off`. |
|
|
133
|
+
| `APPLE_MESSAGES_STORE` | auto | Explicit store path. |
|
|
134
|
+
| `APPLE_MESSAGES_DEFAULT_RANGE_DAYS` | `30` | Window when only a start is given. |
|
|
135
|
+
| `APPLE_MESSAGES_MAX_RESULTS` | `50` | Default page size. |
|
|
136
|
+
| `APPLE_MESSAGES_ALLOW_WRITES` | off | Register `send_message` at all. |
|
|
137
|
+
| `APPLE_MESSAGES_ALLOW_CODES` | off | Register `find_codes` at all. |
|
|
138
|
+
| `APPLE_MESSAGES_ALLOW_WATCHES` | off | Register `watch` and `unwatch`. |
|
|
139
|
+
| `APPLE_MESSAGES_ALLOW_EVENTS` | off | Serve the experimental `messages.received` event (draft MCP Events extension, 2026-07-28 only). |
|
|
140
|
+
| `APPLE_MESSAGES_ALLOW_FILE_SEND` | off | Add `filePath` to `send_message`. |
|
|
141
|
+
| `APPLE_MESSAGES_SEND_RECONCILE_MS` | `5000` | How long to wait for the sent row. |
|
|
140
142
|
|
|
141
143
|
`APPLE_MESSAGES_ALLOW_CODES` gates `find_codes`, which extracts one-time 2FA codes from recently
|
|
142
144
|
received messages. It is a **read**, and it is deliberately not folded into `ALLOW_WRITES`: reaching
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as MESSAGES_SURFACE, V as BUILD_INFO, a as loadConfig, r as createServer } from "./server-
|
|
2
|
+
import { A as MESSAGES_SURFACE, V as BUILD_INFO, a as loadConfig, r as createServer } from "./server-D-2jgV-B.js";
|
|
3
3
|
import { runStdioServer } from "@mgcrea/mcp-apple-core";
|
|
4
4
|
//#region src/cli.ts
|
|
5
5
|
const LOG_PREFIX = "apple-messages-mcp";
|
|
@@ -7,16 +7,17 @@ runStdioServer({
|
|
|
7
7
|
build: BUILD_INFO,
|
|
8
8
|
surface: MESSAGES_SURFACE,
|
|
9
9
|
logPrefix: LOG_PREFIX,
|
|
10
|
-
start: async (logger) => {
|
|
10
|
+
start: async (logger, ctx) => {
|
|
11
11
|
const config = loadConfig();
|
|
12
12
|
const { server, client } = createServer({
|
|
13
13
|
config,
|
|
14
|
-
logger
|
|
14
|
+
logger,
|
|
15
|
+
era: ctx.era
|
|
15
16
|
});
|
|
16
17
|
const status = client.status();
|
|
17
18
|
return {
|
|
18
19
|
server,
|
|
19
|
-
banner: `writes=${config.allowWrites ? "ENABLED" : "disabled"}, store=${status.store.opened ? "open" : "UNREADABLE"}, messages=${status.counts?.messages ?? 0}, chats=${status.counts?.chats ?? 0}, names=${status.contacts.available ? "contacts" : "raw handles"}, index=${config.indexMode}`
|
|
20
|
+
banner: `writes=${config.allowWrites ? "ENABLED" : "disabled"}, watches=${config.allowWatches ? "ENABLED" : "disabled"}, store=${status.store.opened ? "open" : "UNREADABLE"}, messages=${status.counts?.messages ?? 0}, chats=${status.counts?.chats ?? 0}, names=${status.contacts.available ? "contacts" : "raw handles"}, index=${config.indexMode}`
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
}).catch((err) => {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { runStdioServer } from \"@mgcrea/mcp-apple-core\";\n\nimport { BUILD_INFO } from \"./build-info.js\";\nimport { MESSAGES_SURFACE } from \"./client/errors.js\";\nimport { loadConfig } from \"./config.js\";\nimport { createServer } from \"./server.js\";\n\nconst LOG_PREFIX = \"apple-messages-mcp\";\n\nrunStdioServer({\n build: BUILD_INFO,\n surface: MESSAGES_SURFACE,\n logPrefix: LOG_PREFIX,\n start: async (logger) => {\n const config = loadConfig();\n const { server, client } = createServer({ config, logger });\n const status = client.status();\n return {\n server,\n banner:\n `writes=${config.allowWrites ? \"ENABLED\" : \"disabled\"}, ` +\n `store=${status.store.opened ? \"open\" : \"UNREADABLE\"}, ` +\n `messages=${status.counts?.messages ?? 0}, ` +\n `chats=${status.counts?.chats ?? 0}, ` +\n `names=${status.contacts.available ? \"contacts\" : \"raw handles\"}, ` +\n `index=${config.indexMode}`,\n };\n },\n}).catch((err: unknown) => {\n console.error(`[${LOG_PREFIX}] fatal:`, err);\n process.exit(1);\n});\n"],"mappings":";;;;AAQA,MAAM,aAAa;AAEnB,eAAe;CACb,OAAO;CACP,SAAS;CACT,WAAW;CACX,OAAO,OAAO,
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { runStdioServer } from \"@mgcrea/mcp-apple-core\";\n\nimport { BUILD_INFO } from \"./build-info.js\";\nimport { MESSAGES_SURFACE } from \"./client/errors.js\";\nimport { loadConfig } from \"./config.js\";\nimport { createServer } from \"./server.js\";\n\nconst LOG_PREFIX = \"apple-messages-mcp\";\n\nrunStdioServer({\n build: BUILD_INFO,\n surface: MESSAGES_SURFACE,\n logPrefix: LOG_PREFIX,\n start: async (logger, ctx) => {\n const config = loadConfig();\n const { server, client } = createServer({ config, logger, era: ctx.era });\n const status = client.status();\n return {\n server,\n banner:\n `writes=${config.allowWrites ? \"ENABLED\" : \"disabled\"}, ` +\n `watches=${config.allowWatches ? \"ENABLED\" : \"disabled\"}, ` +\n `store=${status.store.opened ? \"open\" : \"UNREADABLE\"}, ` +\n `messages=${status.counts?.messages ?? 0}, ` +\n `chats=${status.counts?.chats ?? 0}, ` +\n `names=${status.contacts.available ? \"contacts\" : \"raw handles\"}, ` +\n `index=${config.indexMode}`,\n };\n },\n}).catch((err: unknown) => {\n console.error(`[${LOG_PREFIX}] fatal:`, err);\n process.exit(1);\n});\n"],"mappings":";;;;AAQA,MAAM,aAAa;AAEnB,eAAe;CACb,OAAO;CACP,SAAS;CACT,WAAW;CACX,OAAO,OAAO,QAAQ,QAAQ;EAC5B,MAAM,SAAS,WAAW;EAC1B,MAAM,EAAE,QAAQ,WAAW,aAAa;GAAE;GAAQ;GAAQ,KAAK,IAAI;EAAI,CAAC;EACxE,MAAM,SAAS,OAAO,OAAO;EAC7B,OAAO;GACL;GACA,QACE,UAAU,OAAO,cAAc,YAAY,WAAW,YAC3C,OAAO,eAAe,YAAY,WAAW,UAC/C,OAAO,MAAM,SAAS,SAAS,aAAa,aACzC,OAAO,QAAQ,YAAY,EAAE,UAChC,OAAO,QAAQ,SAAS,EAAE,UAC1B,OAAO,SAAS,YAAY,aAAa,cAAc,UACvD,OAAO;EACpB;CACF;AACF,CAAC,CAAC,CAAC,OAAO,QAAiB;CACzB,QAAQ,MAAM,IAAI,WAAW,WAAW,GAAG;CAC3C,QAAQ,KAAK,CAAC;AAChB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AppleAutomationError, AppleAutomationError as AppleMessagesError, BuildInfo, CORE_DATA_EPOCH_OFFSET, IndexUnavailableError, Logger, OsascriptRunner, ReadOnlyMode, SchemaDriftError, StoreFacts, SurfaceContext } from "@mgcrea/mcp-apple-core";
|
|
1
|
+
import { AppleAutomationError, AppleAutomationError as AppleMessagesError, ArrivalRow, ArrivalSource, BuildInfo, CORE_DATA_EPOCH_OFFSET, Era, FileIdentity, IndexUnavailableError, Logger, OsascriptRunner, ReadOnlyMode, SchemaDriftError, StoreFacts, SurfaceContext, Watch, WatchRegistry } from "@mgcrea/mcp-apple-core";
|
|
2
2
|
import { AppleContactsClient } from "@mgcrea/mcp-apple-contacts";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import { McpServer } from "@modelcontextprotocol/
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
5
5
|
import { DatabaseSync } from "node:sqlite";
|
|
6
6
|
//#region src/build-info.d.ts
|
|
7
7
|
declare const BUILD_INFO: BuildInfo;
|
|
@@ -42,6 +42,8 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
42
42
|
sendReconcileMs: z.ZodDefault<z.ZodNumber>;
|
|
43
43
|
allowCodes: z.ZodDefault<z.ZodBoolean>;
|
|
44
44
|
allowFileSend: z.ZodDefault<z.ZodBoolean>;
|
|
45
|
+
allowWatches: z.ZodDefault<z.ZodBoolean>;
|
|
46
|
+
allowEvents: z.ZodDefault<z.ZodBoolean>;
|
|
45
47
|
}, z.core.$strict>;
|
|
46
48
|
type Config = z.infer<typeof ConfigSchema>;
|
|
47
49
|
declare const loadConfig: (env?: NodeJS.ProcessEnv) => Config;
|
|
@@ -206,6 +208,26 @@ declare class MessagesStore {
|
|
|
206
208
|
* `Liked "see you at 8"` — which is not something anybody typed.
|
|
207
209
|
*/
|
|
208
210
|
range(q: RangeQuery): MessageRow[];
|
|
211
|
+
/** Highest message ROWID now, or 0 for an empty store. The watch cursor's head. */
|
|
212
|
+
headRowid(): number;
|
|
213
|
+
/**
|
|
214
|
+
* Messages with ROWID above `after`, ascending, at most `limit`.
|
|
215
|
+
*
|
|
216
|
+
* Nothing is filtered out, reactions and my own messages included: a watch
|
|
217
|
+
* cursor advances over every ROWID and the caller decides what is excluded.
|
|
218
|
+
* `message` is AUTOINCREMENT, so a ROWID is never handed out twice and the
|
|
219
|
+
* cursor can only move forward.
|
|
220
|
+
*
|
|
221
|
+
* Exactly one row per ROWID. `#joins` yields a row per chat for a message in
|
|
222
|
+
* several, which would give one cursor two rows and spend two slots of a read's
|
|
223
|
+
* limit on one message, so the chat here is picked: the lowest `chat_id`.
|
|
224
|
+
*
|
|
225
|
+
* No text: a watch carries metadata only, so `text` is always null here and
|
|
226
|
+
* the scan never pays for decoding a body.
|
|
227
|
+
*/
|
|
228
|
+
arrivalsAfter(after: number, limit: number): (MessageRow & {
|
|
229
|
+
rowid: number;
|
|
230
|
+
})[];
|
|
209
231
|
/**
|
|
210
232
|
* Text search, in two passes — and the second one is the point.
|
|
211
233
|
*
|
|
@@ -505,6 +527,21 @@ declare class AppleMessagesClient {
|
|
|
505
527
|
totalGroups: number;
|
|
506
528
|
totalRows: number;
|
|
507
529
|
};
|
|
530
|
+
/**
|
|
531
|
+
* The stored spellings that name the same correspondent as `typed`. Candidates,
|
|
532
|
+
* not answers: a locally-spelled number reaches `+33…` here, so a caller that
|
|
533
|
+
* needs an exact spelling still has to compare. `[]` when the store is closed.
|
|
534
|
+
* `pool` is the store's handles when the caller already read them.
|
|
535
|
+
*/
|
|
536
|
+
storeHandlesFor(typed: string, pool?: readonly string[]): string[];
|
|
537
|
+
/**
|
|
538
|
+
* The open store and the file it came from, for a watch. Throws the same
|
|
539
|
+
* reason every other read throws when the store is off or unreadable.
|
|
540
|
+
*/
|
|
541
|
+
watchStore(): {
|
|
542
|
+
store: MessagesStore;
|
|
543
|
+
path: string;
|
|
544
|
+
};
|
|
508
545
|
/**
|
|
509
546
|
* Send one message. A real one, to a real person, immediately.
|
|
510
547
|
*
|
|
@@ -622,13 +659,12 @@ declare class SendFailedError extends AppleAutomationError {
|
|
|
622
659
|
*
|
|
623
660
|
* ## Why a GUID rather than a rowid
|
|
624
661
|
*
|
|
625
|
-
* Both exist, and the rowid is faster to look up. The GUID wins anyway
|
|
626
|
-
*
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
*
|
|
631
|
-
* against: plausible, wrong, and silent.
|
|
662
|
+
* Both exist, and the rowid is faster to look up. The GUID wins anyway, and not
|
|
663
|
+
* because rowids are reused: `message` is `AUTOINCREMENT`, so a deleted row's id
|
|
664
|
+
* is never handed to a later insert (the watch cursor relies on exactly that,
|
|
665
|
+
* and `watch.test.ts` pins it). A rowid is a position in this one database file.
|
|
666
|
+
* It means nothing on another device or after a sync rebuilds the store, whereas
|
|
667
|
+
* the GUID is the message's own identity and travels with it.
|
|
632
668
|
*
|
|
633
669
|
* The GUID is also what Apple itself joins on — `associated_message_guid` for
|
|
634
670
|
* reactions, `thread_originator_guid` for replies — so it is the identifier the
|
|
@@ -885,6 +921,106 @@ declare const PRELUDE = "\nObjC.import(\"AppKit\");\n\nfunction ok(data) { retur
|
|
|
885
921
|
*/
|
|
886
922
|
declare const SEND_MESSAGE = "\nObjC.import(\"AppKit\");\n\nfunction ok(data) { return JSON.stringify({ ok: true, data: data }); }\nfunction err(code, message, extra) {\n var e = { code: code, message: String(message) };\n if (extra) e.detail = extra;\n return JSON.stringify({ ok: false, error: e });\n}\n\n/** Read one property defensively — every read on this surface is allowed to fail. */\nfunction prop(fn, fallback) {\n try {\n var v = fn();\n return v === undefined ? fallback : v;\n } catch (e) {\n return fallback;\n }\n}\n\nfunction isMessagesRunning() {\n var apps = $.NSRunningApplication.runningApplicationsWithBundleIdentifier(\"com.apple.MobileSMS\");\n return apps.count > 0;\n}\n\n/** The dictionary's service type enumeration: SMS, iMessage, RCS. */\nfunction serviceEnum(name) {\n var s = String(name || \"\").toLowerCase();\n if (s === \"sms\") return \"SMS\";\n if (s === \"rcs\") return \"RCS\";\n return \"iMessage\";\n}\n\n/**\n * Find something `send` will accept as its `to`.\n *\n * Ordered cheapest and most-likely-to-work first. Every rung is wrapped, every\n * failure is recorded, and the caller is told which one answered — on a surface\n * whose whole read half is known broken, \"it worked\" without \"how\" is not a\n * result anyone can act on later.\n */\nfunction resolveTarget(M, p, tried) {\n var i;\n\n // 1. The chat guid the file lane read out of chat.db. No enumeration.\n if (p.chatGuid) {\n try {\n var chat = M.chats.byId(p.chatGuid);\n chat.id();\n return { target: chat, strategy: \"chat-guid\", kind: \"chat\" };\n } catch (e) {\n tried.push(\"chat-guid: \" + (e.message || e));\n }\n }\n\n if (!p.handle) return null;\n\n // 2. The guid Messages composes for a one-to-one chat, spelled the way the\n // store spells it: \"iMessage;-;+15551234567\". Constructed rather than read,\n // so it is below the real one and above everything that enumerates.\n var services = p.service ? [serviceEnum(p.service)] : [\"iMessage\", \"SMS\", \"RCS\"];\n for (i = 0; i < services.length; i++) {\n var guess = services[i] + \";-;\" + p.handle;\n try {\n var guessed = M.chats.byId(guess);\n guessed.id();\n return { target: guessed, strategy: \"chat-guid-guess\", kind: \"chat\", guid: guess };\n } catch (e2) {\n tried.push(\"chat-guid-guess(\" + guess + \"): \" + (e2.message || e2));\n }\n }\n\n // 3. A participant reached through its account. This enumerates, so it is\n // expected to fail with \"Application isn't running\" — kept because it is\n // the form every AppleScript example on the internet uses, and because if\n // launching the app does wake the scripting interface, this is what works.\n for (i = 0; i < services.length; i++) {\n try {\n var accounts = M.accounts.whose({ serviceType: services[i] })();\n for (var j = 0; j < accounts.length; j++) {\n try {\n var buddy = accounts[j].participants.whose({ handle: p.handle })()[0];\n if (buddy) {\n buddy.id();\n return { target: buddy, strategy: \"account-participant\", kind: \"participant\" };\n }\n } catch (e4) {\n tried.push(\"account-participant(\" + services[i] + \"): \" + (e4.message || e4));\n }\n }\n } catch (e3) {\n tried.push(\"accounts(\" + services[i] + \"): \" + (e3.message || e3));\n }\n }\n\n // 4. The flat participant list, last because it is the widest enumeration.\n try {\n var flat = M.participants.whose({ handle: p.handle })()[0];\n if (flat) {\n flat.id();\n return { target: flat, strategy: \"participant\", kind: \"participant\" };\n }\n tried.push(\"participant: no participant with that handle\");\n } catch (e5) {\n tried.push(\"participant: \" + (e5.message || e5));\n }\n\n return null;\n}\n\nfunction run(argv) {\n var p = JSON.parse(argv[0]);\n var M = Application(\"Messages\");\n\n var wasRunning = isMessagesRunning();\n if (!wasRunning && !p.allowLaunch) {\n return err(\"APP_NOT_RUNNING\", \"Messages is not running.\");\n }\n\n // The dictionary's direct parameter is file OR text, so one send sends one\n // thing. The client enforces this too; it is repeated here because a\n // malformed param set must fail before a target is addressed, not after.\n var hasFile = typeof p.file === \"string\" && p.file.length > 0;\n var hasText = typeof p.text === \"string\" && p.text.length > 0;\n if (hasFile === hasText) {\n return err(\"SEND_PAYLOAD_INVALID\", \"Expected exactly one of text or file.\");\n }\n // `p.file` is already resolved and already bounded by the client. This does\n // not build a path, join one, or read one — it forwards what it was handed.\n var payload = hasFile ? Path(p.file) : p.text;\n\n var tried = [];\n var resolved = resolveTarget(M, p, tried);\n if (!resolved) {\n return err(\n \"SEND_TARGET_NOT_FOUND\",\n \"Messages would not resolve a chat or participant for that recipient.\",\n tried\n );\n }\n\n try {\n M.send(payload, { to: resolved.target });\n } catch (e) {\n return err(\"SEND_FAILED\", e.message || e, tried);\n }\n\n return ok({\n sent: hasFile ? \"file\" : \"text\",\n strategy: resolved.strategy,\n targetKind: resolved.kind,\n // Best effort, and allowed to be null: reading the id back is itself a read.\n targetId: prop(function () { return String(resolved.target.id()); }, null),\n launched: !wasRunning,\n attempts: tried\n });\n}\n";
|
|
887
923
|
//#endregion
|
|
924
|
+
//#region src/client/watch.d.ts
|
|
925
|
+
/**
|
|
926
|
+
* Messages' arrival source over chat.db.
|
|
927
|
+
*
|
|
928
|
+
* Metadata only: a `MessageArrival` has no `text`, even when the row's column is
|
|
929
|
+
* set. The body stays behind `apple_messages_get_message`.
|
|
930
|
+
*
|
|
931
|
+
* `message` is AUTOINCREMENT, so the ROWID is the cursor and never repeats; `key`
|
|
932
|
+
* is the guid, which is also what the public ref carries.
|
|
933
|
+
*
|
|
934
|
+
* What the source drops for every watch (`excluded`): my own messages,
|
|
935
|
+
* tapbacks, and group events (`item_type` other than 0: a rename, someone added
|
|
936
|
+
* or leaving), which are rows from a handle that nobody typed. Messages has no
|
|
937
|
+
* per-watch scope, so `scope` stays undefined.
|
|
938
|
+
*/
|
|
939
|
+
/** No `text`: a message's text is its body, and watch reads carry metadata only. */
|
|
940
|
+
type MessageArrival = ArrivalRow & {
|
|
941
|
+
/** The message's public ref, as `get_message` and search emit it. */
|
|
942
|
+
id: string;
|
|
943
|
+
/** The chat's public ref, as listing tools emit it. */
|
|
944
|
+
chat: string | null;
|
|
945
|
+
handle: string | null;
|
|
946
|
+
/** As search and range render it; null when the row has no date. */
|
|
947
|
+
date: string | null;
|
|
948
|
+
};
|
|
949
|
+
//#endregion
|
|
950
|
+
//#region src/tools/watch.d.ts
|
|
951
|
+
type MessagesWatchServiceOptions = {
|
|
952
|
+
client: AppleMessagesClient;
|
|
953
|
+
logger?: Logger | undefined;
|
|
954
|
+
/**
|
|
955
|
+
* Who reads through the service: watches (the default), or the Events
|
|
956
|
+
* extension. Only the wording of a refusal depends on it, so an events client
|
|
957
|
+
* reads "subscription" where a watch's would read "watch".
|
|
958
|
+
*/
|
|
959
|
+
mode?: "watch" | "events" | undefined;
|
|
960
|
+
};
|
|
961
|
+
declare class MessagesWatchService {
|
|
962
|
+
#private;
|
|
963
|
+
/** What `createWatches` is given. Delegates to the source `ensure()` builds. */
|
|
964
|
+
readonly source: ArrivalSource<MessageArrival>;
|
|
965
|
+
constructor(opts: MessagesWatchServiceOptions);
|
|
966
|
+
/** How many watches this connection holds. */
|
|
967
|
+
get count(): number;
|
|
968
|
+
/** Called once, by `createServer`, after `createWatches` returns. */
|
|
969
|
+
bind(registry: WatchRegistry<MessageArrival>): void;
|
|
970
|
+
/**
|
|
971
|
+
* Open the store and build the source, once. Concurrency-safe: two simultaneous
|
|
972
|
+
* `watch` calls share the promise. A failure is not kept, so a later call runs
|
|
973
|
+
* setup again.
|
|
974
|
+
*/
|
|
975
|
+
ensure(): Promise<void>;
|
|
976
|
+
/**
|
|
977
|
+
* `ensure`, then reopen chat.db if it was replaced since. What an events poll
|
|
978
|
+
* or tick runs before it reads: a poll has no trigger to notice a rebuild for
|
|
979
|
+
* it, and without this would read the deleted file for as long as the
|
|
980
|
+
* connection lives. `ensure` itself does not reopen, so watches are unchanged.
|
|
981
|
+
*/
|
|
982
|
+
refresh(): Promise<void>;
|
|
983
|
+
/** What `createWatches` is given as `startTrigger`. Reads the directory now, not at construction. */
|
|
984
|
+
startTrigger: (onChange: () => void) => {
|
|
985
|
+
stop(): void;
|
|
986
|
+
};
|
|
987
|
+
/** The trigger's callback: reopen a replaced store, then tick. */
|
|
988
|
+
onTrigger(tick: () => void): void;
|
|
989
|
+
/** The file identity of the chat.db the service reads now, or null when it is unknown or unreadable. */
|
|
990
|
+
identity(): FileIdentity | null;
|
|
991
|
+
/** Close the service's own handle. Called when the connection ends. */
|
|
992
|
+
close(): void;
|
|
993
|
+
remove(id: string): boolean;
|
|
994
|
+
/**
|
|
995
|
+
* Turn what the caller typed into exact store spellings.
|
|
996
|
+
*
|
|
997
|
+
* `storeHandlesFor` is a candidate list (a local "06 12 ..." reaches "+33 6 12 ..."),
|
|
998
|
+
* so a typed value counts as resolved only when a candidate equals it after
|
|
999
|
+
* normalisation. A short "+336" therefore resolves nothing, rather than every
|
|
1000
|
+
* French number. What does not resolve is kept, normalised and lowercased, and
|
|
1001
|
+
* reported (with `suggestions` when the store holds near misses).
|
|
1002
|
+
*/
|
|
1003
|
+
resolveFrom(typed: readonly string[]): {
|
|
1004
|
+
from: string[];
|
|
1005
|
+
unresolved: string[];
|
|
1006
|
+
suggestions: {
|
|
1007
|
+
typed: string;
|
|
1008
|
+
candidates: string[];
|
|
1009
|
+
}[];
|
|
1010
|
+
};
|
|
1011
|
+
add(input: {
|
|
1012
|
+
from?: string[] | undefined;
|
|
1013
|
+
domains?: string[] | undefined;
|
|
1014
|
+
}): Promise<{
|
|
1015
|
+
watch: Watch;
|
|
1016
|
+
unresolved: string[];
|
|
1017
|
+
suggestions: {
|
|
1018
|
+
typed: string;
|
|
1019
|
+
candidates: string[];
|
|
1020
|
+
}[];
|
|
1021
|
+
}>;
|
|
1022
|
+
}
|
|
1023
|
+
//#endregion
|
|
888
1024
|
//#region src/server.d.ts
|
|
889
1025
|
declare const SERVER_NAME: string;
|
|
890
1026
|
declare const SERVER_VERSION: string;
|
|
@@ -897,10 +1033,16 @@ type CreateServerOptions = {
|
|
|
897
1033
|
home?: string;
|
|
898
1034
|
/** Injected by tests, so no test reaches the developer's real address book. */
|
|
899
1035
|
contacts?: ConstructorParameters<typeof AppleMessagesClient>[0]["contacts"];
|
|
1036
|
+
/** Which protocol era this instance serves. Only watches care. Default `legacy`. */
|
|
1037
|
+
era?: Era;
|
|
900
1038
|
};
|
|
901
1039
|
type CreatedServer = {
|
|
902
1040
|
server: McpServer;
|
|
903
1041
|
client: AppleMessagesClient;
|
|
1042
|
+
/** Present only with `allowWatches` on. */
|
|
1043
|
+
watches?: MessagesWatchService | undefined;
|
|
1044
|
+
/** Present only with `allowEvents` on. Its own service, never the watches'. */
|
|
1045
|
+
events?: MessagesWatchService | undefined;
|
|
904
1046
|
};
|
|
905
1047
|
/**
|
|
906
1048
|
* Build the server. Side-effect free: it opens no database and reads no file,
|
|
@@ -939,6 +1081,11 @@ type ToolContext = {
|
|
|
939
1081
|
* model will keep filling in.
|
|
940
1082
|
*/
|
|
941
1083
|
allowFileSend: boolean;
|
|
1084
|
+
/**
|
|
1085
|
+
* Present only when `allowWatches` is on. Independent of `allowWrites`: both
|
|
1086
|
+
* watch tools are reads, so they register on read-only servers too.
|
|
1087
|
+
*/
|
|
1088
|
+
watches?: MessagesWatchService | undefined;
|
|
942
1089
|
};
|
|
943
1090
|
/**
|
|
944
1091
|
* Register the Apple Messages tools.
|
|
@@ -947,7 +1094,8 @@ type ToolContext = {
|
|
|
947
1094
|
* surface the flag carries a permission claim as well as a safety one: with it
|
|
948
1095
|
* off no Apple Event is ever sent, so no Automation grant is ever requested.
|
|
949
1096
|
* What is needed either way is Full Disk Access, absolutely — see `diagnostics`.
|
|
950
|
-
* One further read, `find_codes`, only when `allowCodes` is on.
|
|
1097
|
+
* One further read, `find_codes`, only when `allowCodes` is on. Two more, the watch
|
|
1098
|
+
* tools, only when `allowWatches` is on (`ctx.watches`).
|
|
951
1099
|
*
|
|
952
1100
|
* The registered set is a pure function of STATIC CONFIGURATION, never of
|
|
953
1101
|
* runtime state. In particular it does NOT vary with whether the store is
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/build-info.ts","../src/config.ts","../src/client/locate.ts","../src/client/store.ts","../src/client/messages.ts","../src/client/dates.ts","../src/client/errors.ts","../src/client/ref.ts","../src/client/typedstream.ts","../src/client/jxa/core.ts","../src/client/jxa/write.ts","../src/server.ts","../src/tools/index.ts"],"mappings":";;;;;;cAkBa,YAAY;;;;;;;;;;;;;;;;;;cCSnB,cAAY,EAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/build-info.ts","../src/config.ts","../src/client/locate.ts","../src/client/store.ts","../src/client/messages.ts","../src/client/dates.ts","../src/client/errors.ts","../src/client/ref.ts","../src/client/typedstream.ts","../src/client/jxa/core.ts","../src/client/jxa/write.ts","../src/client/watch.ts","../src/tools/watch.ts","../src/server.ts","../src/tools/index.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as MESSAGES_SURFACE, B as toAppleSeconds, C as locateStore, D as ChatNotFoundError, E as AppleMessagesError, F as SendTargetNotFoundError, I as CORE_DATA_EPOCH_OFFSET, L as appleSecondsSql, M as MessagesUnavailableError, N as SchemaDriftError, O as IndexUnavailableError, P as SendFailedError, R as fromAppleSeconds, S as defaultStorePath, T as PRELUDE, V as BUILD_INFO, _ as decodeMessageRef, a as loadConfig, b as ATTACHMENTS_RELATIVE, c as introspect, d as decodeAttributedBody, f as outline, g as decodeChatRef, h as MESSAGE_REF_VERSION, i as registerTools, j as MessageNotFoundError, k as MESSAGES_BUNDLE_ID, l as openStore, m as InvalidMessageRefError, n as SERVER_VERSION, o as AppleMessagesClient, p as CHAT_REF_VERSION, r as createServer, s as MessagesStore, t as SERVER_NAME, u as reactionLabel, v as encodeChatRef, w as SEND_MESSAGE, x as STORE_RELATIVE, y as encodeMessageRef, z as renderInstant } from "./server-
|
|
1
|
+
import { A as MESSAGES_SURFACE, B as toAppleSeconds, C as locateStore, D as ChatNotFoundError, E as AppleMessagesError, F as SendTargetNotFoundError, I as CORE_DATA_EPOCH_OFFSET, L as appleSecondsSql, M as MessagesUnavailableError, N as SchemaDriftError, O as IndexUnavailableError, P as SendFailedError, R as fromAppleSeconds, S as defaultStorePath, T as PRELUDE, V as BUILD_INFO, _ as decodeMessageRef, a as loadConfig, b as ATTACHMENTS_RELATIVE, c as introspect, d as decodeAttributedBody, f as outline, g as decodeChatRef, h as MESSAGE_REF_VERSION, i as registerTools, j as MessageNotFoundError, k as MESSAGES_BUNDLE_ID, l as openStore, m as InvalidMessageRefError, n as SERVER_VERSION, o as AppleMessagesClient, p as CHAT_REF_VERSION, r as createServer, s as MessagesStore, t as SERVER_NAME, u as reactionLabel, v as encodeChatRef, w as SEND_MESSAGE, x as STORE_RELATIVE, y as encodeMessageRef, z as renderInstant } from "./server-D-2jgV-B.js";
|
|
2
2
|
export { ATTACHMENTS_RELATIVE, AppleMessagesClient, AppleMessagesError, BUILD_INFO, CHAT_REF_VERSION, CORE_DATA_EPOCH_OFFSET, ChatNotFoundError, IndexUnavailableError, InvalidMessageRefError, MESSAGES_BUNDLE_ID, MESSAGES_SURFACE, MESSAGE_REF_VERSION, MessageNotFoundError, MessagesStore, MessagesUnavailableError, PRELUDE, SEND_MESSAGE, SERVER_NAME, SERVER_VERSION, STORE_RELATIVE, SchemaDriftError, SendFailedError, SendTargetNotFoundError, appleSecondsSql, createServer, decodeAttributedBody, decodeChatRef, decodeMessageRef, defaultStorePath, encodeChatRef, encodeMessageRef, fromAppleSeconds, introspect, loadConfig, locateStore, openStore, outline, reactionLabel, registerTools, renderInstant, toAppleSeconds };
|