@mgcrea/mcp-apple-maps 0.0.0-bootstrap

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.
@@ -0,0 +1,633 @@
1
+ import { AppleAutomationError, AppleAutomationError as AppleMapsError, BuildInfo, CORE_DATA_EPOCH_OFFSET, FileFacts, IndexUnavailableError, Logger, ReadOnlyMode, SchemaDriftError, StoreFacts, SurfaceContext } from "@mgcrea/mcp-apple-core";
2
+ import { DatabaseSync } from "node:sqlite";
3
+ import { z } from "zod";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ //#region src/build-info.d.ts
6
+ declare const BUILD_INFO: BuildInfo;
7
+ //#endregion
8
+ //#region src/client/dates.d.ts
9
+ /**
10
+ * How this store's timestamps map onto real time.
11
+ *
12
+ * `confident` is the field that matters. `detectEpoch` always returns an offset
13
+ * — it falls back to unix when nothing fits — so the offset alone cannot
14
+ * distinguish "measured as unix" from "gave up and assumed unix". Rendering the
15
+ * second as though it were the first is the failure this module exists to
16
+ * prevent, so the two are kept apart.
17
+ */
18
+ type Epoch = {
19
+ offset: number;
20
+ reason: string;
21
+ confident: boolean;
22
+ };
23
+ declare const resolveEpoch: (maxTimestamp: number | null, now?: number) => Epoch;
24
+ /** The expectation docs/maps.md carries, used only where no store is open. */
25
+ declare const APPLE_SECONDS: Epoch;
26
+ /** A stored timestamp to a JS Date, or null when it cannot be placed. */
27
+ declare const fromStoreTime: (value: number | null, epoch: Epoch) => Date | null;
28
+ /** ISO-8601, or null. What every date field on a result carries. */
29
+ declare const renderInstant: (value: number | null, epoch: Epoch) => string | null;
30
+ //#endregion
31
+ //#region src/client/errors.d.ts
32
+ declare const MAPS_SURFACE: SurfaceContext;
33
+ /**
34
+ * Maps is one of the few Apple apps whose bundle id matches its display name —
35
+ * unlike `com.apple.iCal`, `com.apple.AddressBook` and `com.apple.MobileSMS`.
36
+ * Stated rather than assumed, because this repo has been caught by the opposite
37
+ * three times.
38
+ *
39
+ * It is recorded here and used by almost nothing. Maps ships **no scripting
40
+ * dictionary** — `/System/Applications/Maps.app` contains no `.sdef`, checked
41
+ * directly rather than inferred from `NSAppleScriptEnabled` — so there is no
42
+ * Apple Events lane to address, and this server never sends one.
43
+ */
44
+ declare const MAPS_BUNDLE_ID = "com.apple.Maps";
45
+ /**
46
+ * The store could not be read.
47
+ *
48
+ * This surface has **one lane and no fallback**, which puts it with Messages
49
+ * rather than with Safari: without Full Disk Access there is no degraded Maps
50
+ * server, there is no Maps server. So every read throws this rather than
51
+ * returning an empty list — an empty `favorites` reads exactly like a person
52
+ * who has saved no places, and that is the failure this error exists to
53
+ * prevent.
54
+ *
55
+ * The hint names the trap that actually catches people: the store lives at a
56
+ * path with **no file extension**, inside the one directory in Maps' container
57
+ * that Full Disk Access gates. A sweep for `*.db` finds nothing and concludes
58
+ * the data is not on disk. It is.
59
+ */
60
+ declare class MapsStoreUnavailableError extends AppleAutomationError {
61
+ readonly name = "MapsStoreUnavailableError";
62
+ constructor(reason: string);
63
+ }
64
+ /** A ref no longer resolves — the place was removed, or the store was re-synced. */
65
+ declare class PlaceNotFoundError extends AppleAutomationError {
66
+ readonly name = "PlaceNotFoundError";
67
+ constructor(ref: string);
68
+ }
69
+ /**
70
+ * The epoch could not be identified from the data.
71
+ *
72
+ * The same discipline `packages/safari` applies, for the same measured reason:
73
+ * a wrong epoch produces dates that are well-formed and wrong by 31 years.
74
+ * `docs/maps.md` records that this store's `ZCREATETIME` read as apple-seconds
75
+ * and that a unix-seconds reading of the same value lands in 1995 while still
76
+ * looking entirely plausible. So the offset is detected from the store at open
77
+ * time and dates are WITHHELD when it cannot be.
78
+ */
79
+ declare class UndatableStoreError extends AppleAutomationError {
80
+ readonly name = "UndatableStoreError";
81
+ constructor(reason: string);
82
+ }
83
+ //#endregion
84
+ //#region src/client/locate.d.ts
85
+ type LocatedFile = FileFacts & {
86
+ path: string;
87
+ };
88
+ type LocateResult = StoreFacts & {
89
+ directory: string;
90
+ storePath: string | null;
91
+ /** Reported so a reader can see it was considered. Never opened. */
92
+ localCache: LocatedFile;
93
+ /** True when the name came from a directory scan rather than the constant. */
94
+ resolvedByScan: boolean;
95
+ reason: string | null;
96
+ };
97
+ declare const defaultDirectory: (home?: string) => string;
98
+ declare const defaultStorePath: (home?: string) => string;
99
+ declare const locateStore: (opts?: {
100
+ storePath?: string | undefined;
101
+ home?: string;
102
+ /** Injected by tests so a scan never reaches a real directory. */
103
+ readdir?: (path: string) => string[];
104
+ }) => LocateResult;
105
+ //#endregion
106
+ //#region src/config.d.ts
107
+ /**
108
+ * Configuration is environment-only — this server holds no secret at all. Its
109
+ * access is the macOS permission the user granted, which is the whole point.
110
+ *
111
+ * `allowWrites` gates two mutating tools, and gates them harder than elsewhere.
112
+ * Maps has no scripting dictionary and no registered App Intents, so a write is
113
+ * SQL straight into a Core Data store that `NSPersistentCloudKitContainer`
114
+ * mirrors — which means it reaches every device on the account, not just this
115
+ * Mac. That was measured, along with the rule that keeps it safe: never
116
+ * fabricate a place record, only ever copy one Maps wrote itself. See
117
+ * `docs/maps.md` and `client/write.ts`.
118
+ */
119
+ declare const ConfigSchema: z.ZodObject<{
120
+ allowWrites: z.ZodDefault<z.ZodBoolean>;
121
+ exposePrompts: z.ZodDefault<z.ZodBoolean>;
122
+ debug: z.ZodDefault<z.ZodBoolean>;
123
+ osascriptPath: z.ZodDefault<z.ZodString>;
124
+ osascriptTimeoutMs: z.ZodDefault<z.ZodNumber>;
125
+ maxResults: z.ZodDefault<z.ZodNumber>;
126
+ storePath: z.ZodOptional<z.ZodString>;
127
+ indexMode: z.ZodDefault<z.ZodEnum<{
128
+ auto: "auto";
129
+ immutable: "immutable";
130
+ off: "off";
131
+ ro: "ro";
132
+ }>>;
133
+ }, z.core.$strict>;
134
+ type Config = z.infer<typeof ConfigSchema>;
135
+ declare const loadConfig: (env?: NodeJS.ProcessEnv) => Config;
136
+ //#endregion
137
+ //#region src/client/store.d.ts
138
+ type FieldMap = {
139
+ /**
140
+ * The column holding a stable UUID, or null when this store has none good
141
+ * enough to address rows by. See `resolveIdentifier`.
142
+ */
143
+ identifier: string | null;
144
+ name: string | null;
145
+ customName: string | null;
146
+ latitude: string | null;
147
+ longitude: string | null;
148
+ address: string | null;
149
+ muid: string | null;
150
+ mapItem: string | null;
151
+ created: string | null;
152
+ modified: string | null;
153
+ };
154
+ type EntityFacts = {
155
+ table: string;
156
+ present: boolean;
157
+ rows: number;
158
+ columns: Set<string>;
159
+ fields: FieldMap;
160
+ };
161
+ /**
162
+ * How an item says which collection it is in, once proved against the oracle.
163
+ *
164
+ * Two shapes because Core Data has two: a scalar foreign key named after the
165
+ * relationship, and a `Z_<ordinal><RELATIONSHIP>` join table for a
166
+ * many-to-many. THIS store uses the second. Both are carried because the
167
+ * resolver proves whichever is there rather than assuming, and a store that
168
+ * changes shape should degrade rather than read the wrong column.
169
+ */
170
+ type CollectionMembership = {
171
+ kind: "column";
172
+ column: string;
173
+ } | {
174
+ kind: "joinTable";
175
+ table: string;
176
+ collectionColumn: string;
177
+ itemColumn: string;
178
+ };
179
+ type StoreCapabilities = {
180
+ fingerprint: string;
181
+ tables: string[];
182
+ favorites: EntityFacts;
183
+ collections: EntityFacts;
184
+ collectionItems: EntityFacts;
185
+ history: EntityFacts;
186
+ mapItems: EntityFacts;
187
+ /**
188
+ * The proved membership mechanism, or null when nothing reproduced
189
+ * `ZPLACESCOUNT` and collections must therefore list without their places.
190
+ */
191
+ membership: CollectionMembership | null;
192
+ /**
193
+ * The membership mechanism as one short string, for diagnostics.
194
+ *
195
+ * Kept because `apple_maps_diagnostics` reported `collectionFk` before this
196
+ * was understood, and a field that silently changes meaning is worse than one
197
+ * that widens: it now names a join table as well as a column.
198
+ */
199
+ collectionFk: string | null;
200
+ epoch: Epoch;
201
+ };
202
+ type PlaceRow = {
203
+ id: number;
204
+ /**
205
+ * The stable identifier, when the store has one. Null means refs on this
206
+ * entity fall back to the row id and are only good for the session.
207
+ */
208
+ uuid: string | null;
209
+ name: string | null;
210
+ customName: string | null;
211
+ latitude: number | null;
212
+ longitude: number | null;
213
+ address: string | null;
214
+ /**
215
+ * Apple's place id, carried as a STRING.
216
+ *
217
+ * MEASURED on a real store: `-2679868148951248105`. It is a 64-bit integer and
218
+ * `node:sqlite` THROWS on one past `Number.MAX_SAFE_INTEGER` rather than
219
+ * truncating, so reading it as a number failed the whole listing with
220
+ * "Value is too large to be represented as a JavaScript number" — one column
221
+ * taking down every favourite. `docs/surfaces.md` states the rule this broke:
222
+ * read such columns as BigInt or `CAST(... AS TEXT)`.
223
+ *
224
+ * The fixture used a small id, so the offline suite passed. Only the real
225
+ * store has ids of this size.
226
+ */
227
+ muid: string | null;
228
+ createdRaw: number | null;
229
+ modifiedRaw: number | null;
230
+ /**
231
+ * False when the row has no linked `ZMIXINMAPITEM`.
232
+ *
233
+ * MEASURED: 3 of 23 favourites, and they carry no name and no coordinate
234
+ * either. Almost certainly the unconfigured Home / Work / School slots that
235
+ * Maps creates whether or not anyone fills them in. They are RETURNED rather
236
+ * than filtered, with this flag, because silently dropping rows is how a
237
+ * caller concludes a favourite was deleted.
238
+ */
239
+ linked: boolean;
240
+ };
241
+ type CollectionRow = {
242
+ id: number;
243
+ uuid: string | null;
244
+ title: string | null;
245
+ placesCount: number | null;
246
+ createdRaw: number | null;
247
+ modifiedRaw: number | null;
248
+ };
249
+ /**
250
+ * How a caller addresses one row.
251
+ *
252
+ * Two shapes rather than one because the store decides which is available, not
253
+ * the caller: a store whose `ZIDENTIFIER` is complete gets durable refs, one
254
+ * without falls back to row ids. Making the difference explicit in the type
255
+ * means a resolver cannot quietly treat a row id as a uuid when the store
256
+ * changed underneath it.
257
+ */
258
+ type EntityKey = {
259
+ uuid: string;
260
+ } | {
261
+ rowId: number;
262
+ };
263
+ declare const introspect: (db: DatabaseSync, now?: number) => StoreCapabilities;
264
+ declare class MapsStore {
265
+ #private;
266
+ readonly db: DatabaseSync;
267
+ readonly caps: StoreCapabilities;
268
+ readonly path: string;
269
+ readonly mode: ReadOnlyMode;
270
+ constructor(opts: {
271
+ db: DatabaseSync;
272
+ caps: StoreCapabilities;
273
+ path: string;
274
+ mode: ReadOnlyMode;
275
+ });
276
+ /** How many collection items are filed in no collection; null when unknown. */
277
+ unfiledCount(): number | null;
278
+ /** One entity's places, newest first when a date is available. */
279
+ places(kind: "favorite" | "collection-item" | "history", opts: {
280
+ limit: number;
281
+ collectionId?: number | undefined;
282
+ query?: string | undefined;
283
+ /** Only items filed in NO collection. See `#unfiledClause`. */
284
+ unfiled?: boolean | undefined;
285
+ }): {
286
+ rows: PlaceRow[];
287
+ truncated: boolean;
288
+ };
289
+ /** One place by entity and key. */
290
+ place(kind: "favorite" | "collection-item" | "history", key: EntityKey): PlaceRow | null;
291
+ /**
292
+ * A collection key to the row id its items point at.
293
+ *
294
+ * Collection membership is a Core Data foreign key, so it holds `Z_PK` values
295
+ * whatever the ref carries. A uuid ref has to be translated before it can
296
+ * filter items, and this is the one place that happens.
297
+ */
298
+ collectionRowId(key: EntityKey): number | null;
299
+ collections(opts: {
300
+ limit: number;
301
+ }): {
302
+ rows: CollectionRow[];
303
+ truncated: boolean;
304
+ };
305
+ close(): void;
306
+ }
307
+ declare const openStore: (opts: {
308
+ path: string;
309
+ mode?: ReadOnlyMode;
310
+ logger?: Logger;
311
+ now?: number;
312
+ hint?: string;
313
+ }) => MapsStore;
314
+ //#endregion
315
+ //#region src/client/ref.d.ts
316
+ /**
317
+ * Refs for places and collections.
318
+ *
319
+ * ## Why the ref is not `ZMUID`
320
+ *
321
+ * `ZMUID` is Apple's own place identifier and looks like the better key — it is
322
+ * the same number for the same restaurant across every device. It is not used,
323
+ * for two measured reasons recorded in `docs/maps.md`:
324
+ *
325
+ * * It is populated **20 of 23** favourites. The three without it are the
326
+ * rows that have no linked place at all, but a ref scheme that cannot
327
+ * address three rows in twenty-three is not a ref scheme.
328
+ * * It identifies a PLACE, not an entry. The same café saved as a favourite
329
+ * AND filed in a collection carries one `ZMUID` across both, so a ref built
330
+ * on it could not say which of the two a caller meant.
331
+ *
332
+ * The keys below address the ENTRY, which is what every tool here returns. The
333
+ * kind is carried in the prefix so a favourite ref and a collection-item ref
334
+ * can never be confused for one another.
335
+ *
336
+ * ## What the ref carries: a uuid when the store has one, a row id when it does not
337
+ *
338
+ * `ZIDENTIFIER` — a 16-byte Core Data UUID — is set on every favourite,
339
+ * collection, collection item and recent on a real store, and is distinct on
340
+ * every one. It addresses the ENTRY, survives a delete elsewhere in the table,
341
+ * and survives an iCloud re-sync renumbering rows. It is the ref whenever
342
+ * `store.ts` can confirm total coverage.
343
+ *
344
+ * It was found by watching Maps write, not by reading the schema — see
345
+ * `docs/maps.md`. The read probe never reported it, because a probe can only
346
+ * report columns it thought to look for.
347
+ *
348
+ * When a store has no usable identifier the ref falls back to the Core Data row
349
+ * id, and then the old caveat applies in full: `Z_PK` is reused after a delete
350
+ * and renumbered by a re-sync, so such a ref is only good for the session.
351
+ * `packages/messages` rejected row ids outright for that reason; here they are
352
+ * the degraded mode rather than the design.
353
+ *
354
+ * The two are told apart BY SHAPE — a uuid is 32 hex characters, a row id is
355
+ * decimal — and the resolver refuses to try a uuid against a store with no
356
+ * identifier column rather than reinterpreting it as a number. Silently
357
+ * resolving one key space in the other would find a real but wrong place, which
358
+ * is worse than finding nothing.
359
+ *
360
+ * ## Why `p1:` and `pc1:`
361
+ *
362
+ * `c1:` is Calendar's, `r1:` Reminders', `k1:` Contacts', `n1:` Notes',
363
+ * `m1:`/`mc1:` Messages', `s1:`/`sb1:` Safari's. A ref that decodes under two
364
+ * surfaces is worse than one that decodes under none, so each prefix is claimed
365
+ * once and the version digit keeps a future change additive rather than a
366
+ * silent reinterpretation of refs already sitting in a conversation.
367
+ */
368
+ declare const PLACE_REF_VERSION = "p1";
369
+ declare const COLLECTION_REF_VERSION = "pc1";
370
+ /** The entity a place ref points into. Carried in the ref, never guessed. */
371
+ type PlaceKind = "favorite" | "collection-item" | "history";
372
+ declare class InvalidMapsRefError extends AppleAutomationError {
373
+ readonly name = "InvalidMapsRefError";
374
+ constructor(raw: string, want: "place" | "collection");
375
+ }
376
+ declare const encodePlaceRef: (kind: PlaceKind, key: EntityKey) => string;
377
+ declare const encodeCollectionRef: (key: EntityKey) => string;
378
+ declare const decodePlaceRef: (raw: string) => {
379
+ kind: PlaceKind;
380
+ key: EntityKey;
381
+ };
382
+ declare const decodeCollectionRef: (raw: string) => EntityKey;
383
+ //#endregion
384
+ //#region src/client/write.d.ts
385
+ /** Injected so tests never launch an application. */
386
+ type OpenUrl = (url: string) => void;
387
+ type AddFavoriteInput = {
388
+ /** What to search for. A name; a bare coordinate does NOT open a place card. */
389
+ query: string;
390
+ latitude?: number | undefined;
391
+ longitude?: number | undefined;
392
+ /** The label to store. Defaults to `query`. */
393
+ name?: string | undefined;
394
+ };
395
+ type AddFavoriteResult = {
396
+ rowId: number;
397
+ uuid: string;
398
+ name: string | null;
399
+ latitude: number | null;
400
+ longitude: number | null;
401
+ /** False when an equivalent favourite already existed and was returned as-is. */
402
+ created: boolean;
403
+ /** True when Maps had to be asked to resolve the place, leaving a Recents entry. */
404
+ seeded: boolean;
405
+ };
406
+ declare class MapsWriter {
407
+ #private;
408
+ constructor(opts: {
409
+ storePath: string;
410
+ openUrl?: OpenUrl;
411
+ seedTimeoutMs?: number;
412
+ });
413
+ /**
414
+ * Add a favourite, in three phases with a connection open for as little of it
415
+ * as possible.
416
+ *
417
+ * THE PHASES ARE THE POINT. A first version opened one read-write handle at the
418
+ * top and held it across the whole seed — including the wait for Maps to
419
+ * resolve a place, which can run to tens of seconds. That hung: Maps is being
420
+ * asked to WRITE the very record being waited for, into a store this process is
421
+ * holding open for writing. Whatever the precise interaction, the shape was a
422
+ * departure from the sequence that had been proven by hand, and the proven
423
+ * sequence never holds a write handle while waiting on the app.
424
+ *
425
+ * 1. READ — look for an existing favourite and an existing place record.
426
+ * 2. SEED — ask Maps to mint a record, holding NO connection at all, and
427
+ * poll with a short-lived read-only handle each time.
428
+ * 3. WRITE — open read-write, insert, close.
429
+ */
430
+ addFavorite(input: AddFavoriteInput): AddFavoriteResult;
431
+ /**
432
+ * Remove a favourite and the place record it owns.
433
+ *
434
+ * `Z_MAX` is deliberately NOT rolled back. Core Data never reuses a primary
435
+ * key, and decrementing the counter would hand the next insert one that is
436
+ * already spoken for by a row still referenced elsewhere.
437
+ */
438
+ removeFavorite(key: EntityKey): boolean;
439
+ }
440
+ //#endregion
441
+ //#region src/client/maps.d.ts
442
+ /**
443
+ * The client.
444
+ *
445
+ * ## One lane, and the consequence of that
446
+ *
447
+ * Safari's orchestrator exists to keep two lanes from being mistaken for one
448
+ * another. Maps has no second lane at all — the app ships no scripting
449
+ * dictionary — so this class has the opposite job: making sure the ABSENCE of
450
+ * the one lane is never mistaken for an absence of data.
451
+ *
452
+ * Without Full Disk Access every list would naturally come back `[]`, and an
453
+ * empty `favorites` reads exactly like a person who has saved no places. So a
454
+ * read with no store THROWS a named error; it never returns an empty array.
455
+ * That is the same rule `packages/messages` follows, and for the same reason:
456
+ * both are surfaces where the grant is not an optimisation.
457
+ */
458
+ type RenderedPlace = {
459
+ ref: string;
460
+ kind: PlaceKind;
461
+ /** The user's own label when they set one, otherwise the place's name. */
462
+ name: string | null;
463
+ /** The place's own name, kept separate so a rename is visible as one. */
464
+ placeName: string | null;
465
+ address: string | null;
466
+ latitude: number | null;
467
+ longitude: number | null;
468
+ /**
469
+ * Apple's place identifier. Stable for the same place across devices, and
470
+ * shared between a favourite and a collection entry for the same place — so
471
+ * it is reported, and never used as the ref. See `ref.ts`.
472
+ *
473
+ * A STRING: it is a 64-bit integer that does not fit in a JS number. A real
474
+ * store returned `-2679868148951248105`.
475
+ */
476
+ muid: string | null;
477
+ created: string | null;
478
+ modified: string | null;
479
+ /**
480
+ * False for a row with no linked place record. MEASURED: 3 of 23 favourites,
481
+ * carrying no name and no coordinate — almost certainly the unconfigured
482
+ * Home / Work / School slots Maps creates whether or not anyone fills them in.
483
+ */
484
+ linked: boolean;
485
+ };
486
+ type RenderedCollection = {
487
+ ref: string;
488
+ title: string | null;
489
+ /** Maps' own count, which may include items this server cannot enumerate. */
490
+ placesCount: number | null;
491
+ created: string | null;
492
+ modified: string | null;
493
+ };
494
+ type CreateClientOptions = {
495
+ config: Config;
496
+ logger?: Logger;
497
+ /** Injected by tests so discovery never reaches the developer's real home. */
498
+ home?: string;
499
+ };
500
+ declare class AppleMapsClient {
501
+ #private;
502
+ constructor(opts: CreateClientOptions);
503
+ get config(): Config;
504
+ located(): LocateResult;
505
+ /**
506
+ * A writer bound to the located store, or an error explaining why not.
507
+ *
508
+ * Writes go through their own read-write connection rather than the shared
509
+ * read-only one: `store()` opens with `query_only` and an `immutable` fallback
510
+ * precisely so a read can never mutate by accident, and widening it would
511
+ * throw that guarantee away for every read on the surface.
512
+ */
513
+ writer(openUrl?: OpenUrl): MapsWriter;
514
+ /** Open the store, once, lazily. Every read goes through here. */
515
+ store(): MapsStore;
516
+ get epoch(): Epoch;
517
+ places(kind: PlaceKind, opts: {
518
+ limit: number;
519
+ collectionId?: number | undefined;
520
+ query?: string | undefined;
521
+ unfiled?: boolean | undefined;
522
+ }): {
523
+ places: RenderedPlace[];
524
+ truncated: boolean;
525
+ datesAvailable: boolean;
526
+ };
527
+ place(kind: PlaceKind, key: EntityKey): RenderedPlace | null;
528
+ /**
529
+ * A collection ref to the row id its items point at, or null when the ref
530
+ * addresses nothing in this store.
531
+ *
532
+ * Collection membership is a Core Data foreign key and always holds `Z_PK`,
533
+ * so a uuid ref has to be translated before it can filter items.
534
+ */
535
+ collectionRowId(key: EntityKey): number | null;
536
+ collections(opts: {
537
+ limit: number;
538
+ }): {
539
+ collections: RenderedCollection[];
540
+ truncated: boolean;
541
+ /** Null when the membership key was not found — see `store.ts`. */
542
+ itemsEnumerable: boolean;
543
+ /**
544
+ * Saved places filed in no collection, or null when unanswerable.
545
+ *
546
+ * Reported beside the collections because the counts otherwise do not add
547
+ * up and nothing says so: the guides account for 18 places while the store
548
+ * holds 30, and a caller comparing the two has no way to learn where the
549
+ * rest went.
550
+ */
551
+ unfiled: number | null;
552
+ };
553
+ /**
554
+ * Search every place-bearing entity at once.
555
+ *
556
+ * Three separate queries rather than a UNION, because the three tables have
557
+ * different resolved columns and a UNION would have to flatten them to the
558
+ * narrowest — which on this store means losing history's coordinates, since
559
+ * they live in a differently named column from the other two.
560
+ */
561
+ search(opts: {
562
+ query: string;
563
+ limit: number;
564
+ }): {
565
+ places: RenderedPlace[];
566
+ truncated: boolean;
567
+ datesAvailable: boolean;
568
+ };
569
+ /** Everything diagnostics needs, with no failure allowed to fail the call. */
570
+ status(): {
571
+ located: LocateResult;
572
+ store: {
573
+ opened: boolean;
574
+ mode: string | null;
575
+ reason: string | null;
576
+ };
577
+ capabilities: Record<string, unknown> | null;
578
+ };
579
+ close(): void;
580
+ }
581
+ //#endregion
582
+ //#region src/server.d.ts
583
+ declare const SERVER_NAME: string;
584
+ declare const SERVER_VERSION: string;
585
+ type CreateServerOptions = {
586
+ config: Config;
587
+ logger?: Logger;
588
+ /** Injected by tests so discovery never reaches the developer's real home. */
589
+ home?: string;
590
+ };
591
+ type CreatedServer = {
592
+ server: McpServer;
593
+ client: AppleMapsClient;
594
+ };
595
+ /**
596
+ * Build the server. Side-effect free: it opens no database and reads no file,
597
+ * so a test can construct it freely and every external dependency arrives
598
+ * through an option.
599
+ *
600
+ * There is no `osascript` seam here, unlike every other surface. This server
601
+ * never spawns one — Maps is not scriptable, so there is no Apple Events lane
602
+ * to inject a fake for. Its absence is the point.
603
+ */
604
+ declare const createServer: (opts: CreateServerOptions) => CreatedServer;
605
+ //#endregion
606
+ //#region src/tools/index.d.ts
607
+ type ToolContext = {
608
+ /**
609
+ * Gates the two mutating tools, which are NOT registered when it is false —
610
+ * so a host that has not opted in is never told they exist.
611
+ *
612
+ * This surface writes SQL directly into Maps' Core Data store, because Maps
613
+ * has no scripting dictionary and no App Intents to write through. The store
614
+ * is mirrored to iCloud, so a write here reaches every device on the account.
615
+ * `docs/maps.md` carries the measurements; `client/write.ts` carries the rule
616
+ * that makes it safe — never fabricate a place record, only copy one Maps
617
+ * wrote itself.
618
+ */
619
+ allowWrites: boolean;
620
+ };
621
+ /**
622
+ * Register the Apple Maps tools.
623
+ *
624
+ * The registered set does NOT vary with whether the store opened. That is a
625
+ * runtime condition and MCP clients cache the tool list, so a list that shrank
626
+ * without Full Disk Access would stay shrunk after the grant was given. Every
627
+ * tool is registered on a machine with no grant at all; each fails with an
628
+ * error that says which permission is missing.
629
+ */
630
+ declare const registerTools: (server: McpServer, client: AppleMapsClient, ctx: ToolContext) => void;
631
+ //#endregion
632
+ export { APPLE_SECONDS, AppleMapsClient, AppleMapsError, BUILD_INFO, type BuildInfo, COLLECTION_REF_VERSION, CORE_DATA_EPOCH_OFFSET, type CollectionRow, type Config, type CreateClientOptions, type CreateServerOptions, type EntityFacts, type Epoch, IndexUnavailableError, InvalidMapsRefError, type LocateResult, MAPS_BUNDLE_ID, MAPS_SURFACE, MapsStore, MapsStoreUnavailableError, PLACE_REF_VERSION, type PlaceKind, PlaceNotFoundError, type PlaceRow, type RenderedCollection, type RenderedPlace, SERVER_NAME, SERVER_VERSION, SchemaDriftError, type StoreCapabilities, type ToolContext, UndatableStoreError, createServer, decodeCollectionRef, decodePlaceRef, defaultDirectory, defaultStorePath, encodeCollectionRef, encodePlaceRef, fromStoreTime, introspect, loadConfig, locateStore, openStore, registerTools, renderInstant, resolveEpoch };
633
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/build-info.ts","../src/client/dates.ts","../src/client/errors.ts","../src/client/locate.ts","../src/config.ts","../src/client/store.ts","../src/client/ref.ts","../src/client/write.ts","../src/client/maps.ts","../src/server.ts","../src/tools/index.ts"],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import { A as fromStoreTime, C as MAPS_SURFACE, D as UndatableStoreError, E as SchemaDriftError, M as resolveEpoch, N as BUILD_INFO, O as APPLE_SECONDS, S as MAPS_BUNDLE_ID, T as PlaceNotFoundError, _ as defaultDirectory, a as loadConfig, b as AppleMapsError, c as introspect, d as InvalidMapsRefError, f as PLACE_REF_VERSION, g as encodePlaceRef, h as encodeCollectionRef, i as registerTools, j as renderInstant, k as CORE_DATA_EPOCH_OFFSET, l as openStore, m as decodePlaceRef, n as SERVER_VERSION, o as AppleMapsClient, p as decodeCollectionRef, r as createServer, s as MapsStore, t as SERVER_NAME, u as COLLECTION_REF_VERSION, v as defaultStorePath, w as MapsStoreUnavailableError, x as IndexUnavailableError, y as locateStore } from "./server-DgIy0w0S.js";
2
+ export { APPLE_SECONDS, AppleMapsClient, AppleMapsError, BUILD_INFO, COLLECTION_REF_VERSION, CORE_DATA_EPOCH_OFFSET, IndexUnavailableError, InvalidMapsRefError, MAPS_BUNDLE_ID, MAPS_SURFACE, MapsStore, MapsStoreUnavailableError, PLACE_REF_VERSION, PlaceNotFoundError, SERVER_NAME, SERVER_VERSION, SchemaDriftError, UndatableStoreError, createServer, decodeCollectionRef, decodePlaceRef, defaultDirectory, defaultStorePath, encodeCollectionRef, encodePlaceRef, fromStoreTime, introspect, loadConfig, locateStore, openStore, registerTools, renderInstant, resolveEpoch };