apple-notes-mcp 2.8.29 → 2.8.30

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.
Files changed (3) hide show
  1. package/README.md +35 -2
  2. package/build/index.js +547 -124
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -56,7 +56,7 @@ Install as a Claude Code plugin for automatic configuration and enhanced AI beha
56
56
 
57
57
  This method also installs a **skill** that teaches Claude when and how to use Apple Notes effectively.
58
58
 
59
- On the first tool call, macOS shows an Automation permission prompt ("Claude" wants access to control "Notes") — click **OK**. Optionally, grant **Full Disk Access** to the app that launches the server to enable the database-backed tools (`get-checklist-state`, `get-note-metadata`, `get-note-link`, checklist annotations in `get-note-markdown`, and full `get-sync-status` detail); see the [Full Disk Access Setup Guide](https://github.com/sweetrb/apple-notes-mcp/blob/main/docs/FULL-DISK-ACCESS.md). The rest of the server is pure AppleScript and works without it.
59
+ On the first tool call, macOS shows an Automation permission prompt ("Claude" wants access to control "Notes") — click **OK**. Optionally, grant **Full Disk Access** to the app that launches the server to enable the database-backed tools (`get-checklist-state`, `get-note-metadata`, `get-audio-transcripts`, `get-note-link`, checklist annotations in `get-note-markdown`, and full `get-sync-status` detail); see the [Full Disk Access Setup Guide](https://github.com/sweetrb/apple-notes-mcp/blob/main/docs/FULL-DISK-ACCESS.md). The rest of the server is pure AppleScript and works without it.
60
60
 
61
61
  Native tag, checklist, table, pin, and rich append operations use two packaged
62
62
  Apple Shortcuts. A third, `Apple Notes MCP - Create Markdown Note`, is optional:
@@ -169,6 +169,7 @@ On first use, macOS will ask for permission to automate Notes.app. Click "OK" to
169
169
  | **Multi-Account** | Work with iCloud, Gmail, Exchange, or any configured account, including account IDs and default folders |
170
170
  | **Batch Operations** | Delete or move multiple notes at once |
171
171
  | **Checklist State** | Read checklist done/undone state directly from the Notes database (requires Full Disk Access) |
172
+ | **Audio Transcripts** | Read the transcripts and summaries Notes stored for audio recordings (requires Full Disk Access) |
172
173
  | **Export** | Export all notes as JSON or get individual notes as Markdown |
173
174
  | **Attachments** | List attachments, save them to disk, or fetch their bytes as base64 |
174
175
  | **Notes.app UI State** | Reveal a note in Notes.app or read the current Notes.app selection |
@@ -1274,6 +1275,38 @@ Reads note metadata that AppleScript cannot expose, by querying the NoteStore SQ
1274
1275
 
1275
1276
  ---
1276
1277
 
1278
+ #### `get-audio-transcripts`
1279
+
1280
+ Reads the transcript, and the summary when one exists, that Notes already computed for the audio recordings in a note. It does not transcribe anything itself. Notes keeps each recording's transcript as word-level mergeable data on the audio attachment's database row, and this tool decodes it read-only.
1281
+
1282
+ **Requires:** Full Disk Access for the MCP host process (see [Full Disk Access Setup](#full-disk-access)). Password-protected notes are refused.
1283
+
1284
+ | Parameter | Type | Required | Description |
1285
+ |-----------|------|----------|-------------|
1286
+ | `id` | string | Yes | Note ID (use `search-notes` to find it first) |
1287
+ | `includeSegments` | boolean | No | Also return word-level segments: `text`, `start` and `duration` in seconds, and `speaker`. Default `false` |
1288
+ | `maxSegments` | number | No | Cap on segments per attachment when `includeSegments` is set (default 2000, at most 20000) |
1289
+
1290
+ **Example:**
1291
+ ```json
1292
+ {
1293
+ "id": "x-coredata://ABC123/ICNote/p456",
1294
+ "includeSegments": true,
1295
+ "maxSegments": 500
1296
+ }
1297
+ ```
1298
+
1299
+ **Returns:** `structuredContent.attachments` holds one entry per top-level audio attachment, in the order the recordings appear in the note. Each entry has:
1300
+
1301
+ - `attachmentId` (usable with `save-attachment`), `identifier`, `typeUti`, and `durationSeconds` when known
1302
+ - `status`: `ok` (a transcript is stored), `none` (no transcript is stored, for example a recording Notes has not transcribed, or an audio file attached from elsewhere), or `undecodable` with a `reason`
1303
+ - `text` (the words joined into readable text), `wordCount`, `fragmentCount`, `speakers`, `summary` and `topLineSummary` when stored, and `needsTranscription` when the database records it
1304
+ - `segments`, only when `includeSegments` is set, with `segmentsTruncated` when the cap cut them short
1305
+
1306
+ A recording extended with more takes has several fragments. Their transcripts are joined in stored order, separated by a blank line, and each segment carries a `fragment` index. Recordings with more than one fragment have been verified against synthetic fixtures only. A response that would exceed `APPLE_NOTES_MCP_EXPORT_MAX_BYTES` (default 8 MB) drops segments first and then shortens `text`, marking `truncated`, `segmentsTruncated` and `textTruncated`. When the note body cannot be parsed, attachments come back in database order with `bodyOrder: false`.
1307
+
1308
+ ---
1309
+
1277
1310
  #### `add-attachment`
1278
1311
 
1279
1312
  Adds one nonempty local file of at most 64 MiB to an exact note using `id`, the
@@ -1691,7 +1724,7 @@ MCP stores no secrets, but as a general rule keep only non-secret config here.
1691
1724
 
1692
1725
  ## Full Disk Access
1693
1726
 
1694
- Several tools read directly from the Apple Notes SQLite database, which lives in a macOS-protected directory. Those tools require **Full Disk Access** for the process running the MCP server: `get-checklist-state`, `get-note-metadata`, `get-note-blocks`, `get-note-link`, the checklist annotations in `get-note-markdown`, and the database half of `get-sync-status`.
1727
+ Several tools read directly from the Apple Notes SQLite database, which lives in a macOS-protected directory. Those tools require **Full Disk Access** for the process running the MCP server: `get-checklist-state`, `get-note-metadata`, `get-note-blocks`, `get-audio-transcripts`, `get-note-link`, the checklist annotations in `get-note-markdown`, and the database half of `get-sync-status`.
1695
1728
 
1696
1729
  > 📘 **For the full why-and-how walkthrough (which app to grant, verifying with `doctor`, graceful degradation), see the [Full Disk Access Setup Guide](https://github.com/sweetrb/apple-notes-mcp/blob/main/docs/FULL-DISK-ACCESS.md).** The summary below is the quick version.
1697
1730
 
package/build/index.js CHANGED
@@ -24465,14 +24465,14 @@ var require_turndown_cjs = __commonJS({
24465
24465
  } else if (node.nodeType === 1) {
24466
24466
  replacement = replacementForNode.call(self, node);
24467
24467
  }
24468
- return join15(output, replacement);
24468
+ return join16(output, replacement);
24469
24469
  }, "");
24470
24470
  }
24471
24471
  function postProcess(output) {
24472
24472
  var self = this;
24473
24473
  this.rules.forEach(function(rule) {
24474
24474
  if (typeof rule.append === "function") {
24475
- output = join15(output, rule.append(self.options));
24475
+ output = join16(output, rule.append(self.options));
24476
24476
  }
24477
24477
  });
24478
24478
  return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
@@ -24484,7 +24484,7 @@ var require_turndown_cjs = __commonJS({
24484
24484
  if (whitespace.leading || whitespace.trailing) content = content.trim();
24485
24485
  return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
24486
24486
  }
24487
- function join15(output, replacement) {
24487
+ function join16(output, replacement) {
24488
24488
  var s1 = trimTrailingNewlines(output);
24489
24489
  var s2 = trimLeadingNewlines(replacement);
24490
24490
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
@@ -24777,8 +24777,8 @@ var ZodError = class _ZodError extends Error {
24777
24777
  constructor(issues) {
24778
24778
  super();
24779
24779
  this.issues = [];
24780
- this.addIssue = (sub3) => {
24781
- this.issues = [...this.issues, sub3];
24780
+ this.addIssue = (sub4) => {
24781
+ this.issues = [...this.issues, sub4];
24782
24782
  };
24783
24783
  this.addIssues = (subs = []) => {
24784
24784
  this.issues = [...this.issues, ...subs];
@@ -24845,13 +24845,13 @@ var ZodError = class _ZodError extends Error {
24845
24845
  flatten(mapper = (issue2) => issue2.message) {
24846
24846
  const fieldErrors = {};
24847
24847
  const formErrors = [];
24848
- for (const sub3 of this.issues) {
24849
- if (sub3.path.length > 0) {
24850
- const firstEl = sub3.path[0];
24848
+ for (const sub4 of this.issues) {
24849
+ if (sub4.path.length > 0) {
24850
+ const firstEl = sub4.path[0];
24851
24851
  fieldErrors[firstEl] = fieldErrors[firstEl] || [];
24852
- fieldErrors[firstEl].push(mapper(sub3));
24852
+ fieldErrors[firstEl].push(mapper(sub4));
24853
24853
  } else {
24854
- formErrors.push(mapper(sub3));
24854
+ formErrors.push(mapper(sub4));
24855
24855
  }
24856
24856
  }
24857
24857
  return { formErrors, fieldErrors };
@@ -29150,12 +29150,12 @@ var $ZodRealError = $constructor("$ZodError", initializer, { Parent: Error });
29150
29150
  function flattenError(error2, mapper = (issue2) => issue2.message) {
29151
29151
  const fieldErrors = {};
29152
29152
  const formErrors = [];
29153
- for (const sub3 of error2.issues) {
29154
- if (sub3.path.length > 0) {
29155
- fieldErrors[sub3.path[0]] = fieldErrors[sub3.path[0]] || [];
29156
- fieldErrors[sub3.path[0]].push(mapper(sub3));
29153
+ for (const sub4 of error2.issues) {
29154
+ if (sub4.path.length > 0) {
29155
+ fieldErrors[sub4.path[0]] = fieldErrors[sub4.path[0]] || [];
29156
+ fieldErrors[sub4.path[0]].push(mapper(sub4));
29157
29157
  } else {
29158
- formErrors.push(mapper(sub3));
29158
+ formErrors.push(mapper(sub4));
29159
29159
  }
29160
29160
  }
29161
29161
  return { formErrors, fieldErrors };
@@ -39275,7 +39275,7 @@ function decodeVarint(buf, offset) {
39275
39275
  }
39276
39276
  throw new Error(`Unexpected end of buffer reading varint at offset ${offset}`);
39277
39277
  }
39278
- function decodeMessage(buf) {
39278
+ function decodeMessage(buf, options = {}) {
39279
39279
  const fields = [];
39280
39280
  let offset = 0;
39281
39281
  while (offset < buf.length) {
@@ -39296,10 +39296,13 @@ function decodeMessage(buf) {
39296
39296
  const value = buf.slice(offset, offset + length);
39297
39297
  fields.push({ fieldNumber, wireType, value });
39298
39298
  offset += length;
39299
- } else if (wireType === 5) {
39300
- offset += 4;
39301
- } else if (wireType === 1) {
39302
- offset += 8;
39299
+ } else if (wireType === 5 || wireType === 1) {
39300
+ const width = wireType === 5 ? 4 : 8;
39301
+ if (offset + width > buf.length) break;
39302
+ if (options.keepFixed) {
39303
+ fields.push({ fieldNumber, wireType, value: buf.slice(offset, offset + width) });
39304
+ }
39305
+ offset += width;
39303
39306
  } else {
39304
39307
  break;
39305
39308
  }
@@ -39330,6 +39333,11 @@ function embeddedMessage(field) {
39330
39333
  if (!bytes) return void 0;
39331
39334
  return decodeMessage(bytes);
39332
39335
  }
39336
+ function fixed64Double(field) {
39337
+ if (!field || field.wireType !== 1 || !(field.value instanceof Uint8Array)) return void 0;
39338
+ if (field.value.length !== 8) return void 0;
39339
+ return new DataView(field.value.buffer, field.value.byteOffset, 8).getFloat64(0, true);
39340
+ }
39333
39341
  var ProtobufDecodeError = class extends Error {
39334
39342
  constructor(message) {
39335
39343
  super(message);
@@ -39901,9 +39909,373 @@ function assertLinkedWrite(rich, content, format, allowLinkChanges = false) {
39901
39909
  }
39902
39910
  }
39903
39911
 
39904
- // src/utils/noteTables.ts
39912
+ // src/utils/audioTranscripts.ts
39913
+ import { execFileSync as execFileSync4 } from "node:child_process";
39914
+ import { homedir as homedir3 } from "node:os";
39915
+ import { join as join3 } from "node:path";
39905
39916
  import { gunzipSync as gunzipSync3 } from "node:zlib";
39917
+ var NOTES_DB_PATH2 = join3(
39918
+ homedir3(),
39919
+ "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
39920
+ );
39921
+ var DEFAULT_MAX_SEGMENTS = 2e3;
39922
+ var MAX_SEGMENTS_LIMIT = 2e4;
39923
+ var AudioTranscriptError = class extends Error {
39924
+ constructor(kind, message) {
39925
+ super(message);
39926
+ this.kind = kind;
39927
+ this.name = "AudioTranscriptError";
39928
+ }
39929
+ kind;
39930
+ };
39931
+ var decode = (bytes) => decodeMessage(bytes, { keepFixed: true });
39906
39932
  var sub = (f, n) => {
39933
+ const bytes = getField(f, n)?.value;
39934
+ return bytes instanceof Uint8Array ? decode(bytes) : void 0;
39935
+ };
39936
+ var need = (value, what) => {
39937
+ if (value === void 0) throw new Error(`Missing ${what}`);
39938
+ return value;
39939
+ };
39940
+ var many = (f, n) => getFields(f, n).map((field) => {
39941
+ if (!(field.value instanceof Uint8Array)) throw new Error("Invalid nested entry");
39942
+ return decode(field.value);
39943
+ });
39944
+ function parseAudioRecording(data) {
39945
+ const root = decode(data);
39946
+ const entries = many(root, 3);
39947
+ if (entries.length === 0) throw new Error("No mergeable objects");
39948
+ if (entries.length > 2e6) throw new Error("Recording too large");
39949
+ const keys = getFields(root, 4).map((f) => stringValue(f) ?? "");
39950
+ const types = getFields(root, 5).map((f) => stringValue(f) ?? "");
39951
+ const uuids = getFields(root, 6).map(
39952
+ (f) => f.value instanceof Uint8Array ? Buffer.from(f.value).toString("hex") : ""
39953
+ );
39954
+ const uuidSlot = /* @__PURE__ */ new Map();
39955
+ uuids.forEach((uuid2, i) => {
39956
+ if (uuid2 && !uuidSlot.has(uuid2)) uuidSlot.set(uuid2, i);
39957
+ });
39958
+ const entry = (index) => {
39959
+ const value = entries[index];
39960
+ if (!value) throw new Error("Invalid object reference");
39961
+ return value;
39962
+ };
39963
+ const customMap = (e) => {
39964
+ const map = sub(e, 13);
39965
+ if (!map) return void 0;
39966
+ const values = /* @__PURE__ */ new Map();
39967
+ for (const item of many(map, 3)) {
39968
+ const key = keys[need(varintValue(getField(item, 1)), "map key")];
39969
+ const value = sub(item, 2);
39970
+ if (key !== void 0 && value) values.set(key, value);
39971
+ }
39972
+ return { type: types[need(varintValue(getField(map, 1)), "map type")] ?? "", values };
39973
+ };
39974
+ const objectIndex = (id2) => id2 ? varintValue(getField(id2, 6)) : void 0;
39975
+ const registerTarget = (id2) => {
39976
+ const index = objectIndex(id2);
39977
+ if (index === void 0) return void 0;
39978
+ const register = sub(entry(index), 1);
39979
+ if (!register) return void 0;
39980
+ const target = objectIndex(sub(register, 2));
39981
+ return target === void 0 ? void 0 : entry(target);
39982
+ };
39983
+ const registerPrimitive = (id2) => {
39984
+ const target = registerTarget(id2);
39985
+ const map = target && customMap(target);
39986
+ if (!map) return void 0;
39987
+ const self = map.values.get("self");
39988
+ if (self) return stringValue(getField(self, 4));
39989
+ const double = map.values.get("doubleValue");
39990
+ if (double) return fixed64Double(getField(double, 3));
39991
+ const integer2 = map.values.get("integerValue");
39992
+ if (integer2) return varintValue(getField(integer2, 2));
39993
+ return void 0;
39994
+ };
39995
+ const registerNoteText = (id2) => {
39996
+ const target = registerTarget(id2);
39997
+ const note = target && sub(target, 10);
39998
+ const text = note && stringValue(getField(note, 2));
39999
+ return text?.replace(/\n+$/u, "") || void 0;
40000
+ };
40001
+ const recordings = entries.map((e) => customMap(e)).filter((m) => m?.type === "com.apple.notes.ICTTAudioRecording");
40002
+ if (recordings.length !== 1) throw new Error("Expected exactly one audio recording object");
40003
+ const recording = recordings[0];
40004
+ const fragments = [];
40005
+ const fragmentList = objectIndex(recording.values.get("fragments"));
40006
+ if (fragmentList !== void 0) {
40007
+ const list = need(sub(entry(fragmentList), 5), "fragment list");
40008
+ for (const item of many(list, 1)) {
40009
+ const fragment = customMap(entry(need(objectIndex(sub(item, 2)), "fragment reference")));
40010
+ if (fragment?.type !== "com.apple.notes.ICTTAudioRecording.Fragment")
40011
+ throw new Error("Unexpected fragment type");
40012
+ const identity = stringValue(getField(fragment.values.get("identity") ?? [], 4));
40013
+ const segments = [];
40014
+ const transcriptRef = objectIndex(fragment.values.get("transcript"));
40015
+ if (transcriptRef !== void 0) {
40016
+ const set = sub(entry(transcriptRef), 15);
40017
+ if (!set) throw new Error("Unsupported transcript container");
40018
+ const bySlot = /* @__PURE__ */ new Map();
40019
+ for (const element of many(sub(set, 2) ?? [], 1)) {
40020
+ const keyMap = customMap(entry(need(objectIndex(sub(element, 1)), "segment key")));
40021
+ const slot = varintValue(getField(keyMap?.values.get("UUIDIndex") ?? [], 2));
40022
+ const value = objectIndex(sub(element, 2));
40023
+ if (slot === void 0 || value === void 0) throw new Error("Invalid segment key");
40024
+ bySlot.set(slot, value);
40025
+ }
40026
+ const ordering = many(need(sub(set, 1), "transcript ordering"), 2).map((pair) => ({
40027
+ index: need(varintValue(getField(pair, 1)), "segment index"),
40028
+ uuid: getField(pair, 2)?.value
40029
+ })).sort((a, b) => a.index - b.index);
40030
+ for (const { uuid: uuid2 } of ordering) {
40031
+ if (!(uuid2 instanceof Uint8Array)) throw new Error("Invalid segment UUID");
40032
+ const slot = uuidSlot.get(Buffer.from(uuid2).toString("hex"));
40033
+ const segmentIndex = slot === void 0 ? void 0 : bySlot.get(slot);
40034
+ if (segmentIndex === void 0) throw new Error("Unresolved transcript segment");
40035
+ const segment = customMap(entry(segmentIndex));
40036
+ if (segment?.type !== "com.apple.notes.ICTTTranscriptSegment")
40037
+ throw new Error("Unexpected segment type");
40038
+ const text = registerPrimitive(segment.values.get("text"));
40039
+ if (typeof text !== "string") throw new Error("Segment without text");
40040
+ const start = registerPrimitive(segment.values.get("timestamp"));
40041
+ const duration3 = registerPrimitive(segment.values.get("duration"));
40042
+ const speaker = registerPrimitive(segment.values.get("speaker"));
40043
+ segments.push({
40044
+ text,
40045
+ ...typeof start === "number" ? { start } : {},
40046
+ ...typeof duration3 === "number" ? { duration: duration3 } : {},
40047
+ ...typeof speaker === "string" && speaker ? { speaker } : {}
40048
+ });
40049
+ }
40050
+ }
40051
+ fragments.push({ ...identity ? { identity } : {}, segments });
40052
+ }
40053
+ }
40054
+ const summary = registerNoteText(recording.values.get("summary"));
40055
+ const topLineSummary = registerNoteText(recording.values.get("topLineSummary"));
40056
+ return {
40057
+ fragments,
40058
+ ...summary ? { summary } : {},
40059
+ ...topLineSummary ? { topLineSummary } : {}
40060
+ };
40061
+ }
40062
+ function joinSegments(segments) {
40063
+ let out = "";
40064
+ for (const { text } of segments) {
40065
+ if (!text) continue;
40066
+ const glue = !out || /\s$/u.test(out) || /^\s/u.test(text) || /^[.,!?;:%)\]}…'’]/u.test(text) ? "" : " ";
40067
+ out += glue + text;
40068
+ }
40069
+ return out.trim();
40070
+ }
40071
+ function parseNoteId(noteId3) {
40072
+ const match = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/ICNote\/p(\d+)$/.exec(noteId3);
40073
+ if (!match) {
40074
+ throw new AudioTranscriptError(
40075
+ "invalid_id",
40076
+ `Invalid note ID format: "${noteId3}". Expected format: x-coredata://UUID/ICNote/pNNN`
40077
+ );
40078
+ }
40079
+ return { store: match[1], pk: match[2] };
40080
+ }
40081
+ var AUDIO_UTI_SQL = "(a.ZTYPEUTI LIKE '%audio%' OR a.ZTYPEUTI IN ('public.mp3'))";
40082
+ function buildTranscriptSql(pk, available) {
40083
+ if (!/^\d+$/.test(pk)) throw new Error("Invalid primary key");
40084
+ const optional2 = (column, key, alias = "a") => available.has(column) ? `, '${key}', ${alias}.${column}` : "";
40085
+ const locked = available.has("ZISPASSWORDPROTECTED") ? "COALESCE(n.ZISPASSWORDPROTECTED, 0)" : "0";
40086
+ return [
40087
+ "BEGIN;",
40088
+ // Every statement yields exactly one row, so output lines stay positional.
40089
+ `SELECT json_object('found', (SELECT count(*) FROM ZICCLOUDSYNCINGOBJECT n WHERE n.Z_PK = ${pk}), 'locked', (SELECT ${locked} FROM ZICCLOUDSYNCINGOBJECT n WHERE n.Z_PK = ${pk}));`,
40090
+ `SELECT COALESCE((SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE = ${pk} LIMIT 1), '');`,
40091
+ "SELECT json_group_array(json_object('pk', a.Z_PK, 'identifier', a.ZIDENTIFIER, 'uti', a.ZTYPEUTI, 'data', hex(a.ZMERGEABLEDATA1)" + optional2("ZDURATION", "duration") + optional2("ZNEEDSTRANSCRIPTION", "needsTranscription") + ", 'fragments', json((SELECT json_group_array(json_object('identifier', c.ZIDENTIFIER" + optional2("ZDURATION", "duration", "c") + `)) FROM ZICCLOUDSYNCINGOBJECT c WHERE c.ZPARENTATTACHMENT = a.Z_PK)))) FROM ZICCLOUDSYNCINGOBJECT a WHERE a.ZNOTE = ${pk} AND a.ZPARENTATTACHMENT IS NULL AND ${AUDIO_UTI_SQL};`,
40092
+ "COMMIT;"
40093
+ ].join("\n");
40094
+ }
40095
+ var REQUIRED_COLUMNS = [
40096
+ "ZMERGEABLEDATA1",
40097
+ "ZPARENTATTACHMENT",
40098
+ "ZTYPEUTI",
40099
+ "ZNOTE",
40100
+ "ZIDENTIFIER"
40101
+ ];
40102
+ function runSqlite(dbPath2, sql) {
40103
+ return execFileSync4("sqlite3", ["-readonly", dbPath2, sql], {
40104
+ encoding: "utf8",
40105
+ timeout: 1e4,
40106
+ maxBuffer: 256 * 1024 * 1024,
40107
+ stdio: ["pipe", "pipe", "pipe"]
40108
+ });
40109
+ }
40110
+ function bodyAttachmentOrder(gzipped) {
40111
+ const doc = decodeMessage(gunzipSync3(gzipped, { maxOutputLength: 64 * 1024 * 1024 }));
40112
+ const body = embeddedMessage(getField(embeddedMessage(getField(doc, 2)) ?? [], 3));
40113
+ if (!body) throw new Error("Unsupported Notes document structure");
40114
+ const ids = [];
40115
+ for (const run of getFields(body, 5)) {
40116
+ const attachment = embeddedMessage(getField(embeddedMessage(run) ?? [], 12));
40117
+ const id2 = attachment && stringValue(getField(attachment, 1));
40118
+ if (id2 && !ids.includes(id2)) ids.push(id2);
40119
+ }
40120
+ return ids;
40121
+ }
40122
+ function readAudioTranscripts(noteId3, options = {}) {
40123
+ const { store, pk } = parseNoteId(noteId3);
40124
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH2;
40125
+ const maxSegments = Math.min(
40126
+ Math.max(1, Math.floor(options.maxSegments ?? DEFAULT_MAX_SEGMENTS)),
40127
+ MAX_SEGMENTS_LIMIT
40128
+ );
40129
+ let lines;
40130
+ try {
40131
+ const available = new Set(
40132
+ runSqlite(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").split("\n").map((line) => line.trim()).filter(Boolean)
40133
+ );
40134
+ const missing = REQUIRED_COLUMNS.filter((c) => !available.has(c));
40135
+ if (missing.length)
40136
+ throw new AudioTranscriptError(
40137
+ "query_error",
40138
+ `This macOS version's Notes database lacks ${missing.join(", ")}; stored transcripts cannot be read.`
40139
+ );
40140
+ lines = runSqlite(dbPath2, buildTranscriptSql(pk, available)).split("\n");
40141
+ } catch (error2) {
40142
+ if (error2 instanceof AudioTranscriptError) throw error2;
40143
+ const message = error2 instanceof Error ? error2.message : String(error2);
40144
+ if (/authorization denied|unable to open database/i.test(message))
40145
+ throw new AudioTranscriptError(
40146
+ "no_fda",
40147
+ `Full Disk Access is required to read stored transcripts. Grant it to the app that launches this server, then fully quit and relaunch it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL}`
40148
+ );
40149
+ throw new AudioTranscriptError("query_error", "Failed to read the Notes database.");
40150
+ }
40151
+ const [noteLine = "{}", bodyLine = "", rowsLine = "[]"] = lines;
40152
+ const note = JSON.parse(noteLine || "{}");
40153
+ if (!note.found)
40154
+ throw new AudioTranscriptError(
40155
+ "not_found",
40156
+ `No note found in the database for ID "${noteId3}".`
40157
+ );
40158
+ if (note.locked)
40159
+ throw new AudioTranscriptError(
40160
+ "locked",
40161
+ "This note is password-protected; its transcripts are encrypted and cannot be read."
40162
+ );
40163
+ const rows = JSON.parse(rowsLine || "[]");
40164
+ let order;
40165
+ try {
40166
+ if (/^[0-9A-F]+$/i.test(bodyLine.trim()))
40167
+ order = bodyAttachmentOrder(Buffer.from(bodyLine.trim(), "hex"));
40168
+ } catch {
40169
+ order = void 0;
40170
+ }
40171
+ const ordered = order ? order.flatMap((id2) => rows.filter((row) => row.identifier === id2)) : [...rows].sort((a, b) => a.pk - b.pk);
40172
+ const attachments = ordered.map((row) => describeRow(row, store, options, maxSegments));
40173
+ return { id: noteId3, attachments, bodyOrder: Boolean(order), truncated: false };
40174
+ }
40175
+ function describeRow(row, store, options, maxSegments) {
40176
+ const childDurations = (row.fragments ?? []).map((f) => f.duration).filter((d) => typeof d === "number" && d > 0);
40177
+ const durationSeconds = typeof row.duration === "number" && row.duration > 0 ? row.duration : childDurations.length ? childDurations.reduce((a, b) => a + b, 0) : void 0;
40178
+ const base = {
40179
+ attachmentId: `x-coredata://${store}/ICAttachment/p${row.pk}`,
40180
+ identifier: row.identifier,
40181
+ typeUti: row.uti,
40182
+ status: "none",
40183
+ ...durationSeconds !== void 0 ? { durationSeconds } : {},
40184
+ ...typeof row.needsTranscription === "number" ? { needsTranscription: row.needsTranscription === 1 } : {}
40185
+ };
40186
+ if (!row.data) return base;
40187
+ let recording;
40188
+ try {
40189
+ recording = parseAudioRecording(Buffer.from(row.data, "hex"));
40190
+ } catch (error2) {
40191
+ return {
40192
+ ...base,
40193
+ status: "undecodable",
40194
+ reason: error2 instanceof Error ? error2.message : String(error2)
40195
+ };
40196
+ }
40197
+ const multi = recording.fragments.length > 1;
40198
+ const all = recording.fragments.flatMap(
40199
+ (fragment, i) => fragment.segments.map((s) => multi ? { ...s, fragment: i } : s)
40200
+ );
40201
+ const text = recording.fragments.map((f) => joinSegments(f.segments)).filter(Boolean).join("\n\n");
40202
+ const speakers = [...new Set(all.flatMap((s) => s.speaker ? [s.speaker] : []))];
40203
+ return {
40204
+ ...base,
40205
+ status: all.length ? "ok" : "none",
40206
+ fragmentCount: recording.fragments.length,
40207
+ wordCount: all.length,
40208
+ ...text ? { text } : {},
40209
+ ...speakers.length ? { speakers } : {},
40210
+ ...recording.summary ? { summary: recording.summary } : {},
40211
+ ...recording.topLineSummary ? { topLineSummary: recording.topLineSummary } : {},
40212
+ ...options.includeSegments && all.length ? {
40213
+ segments: all.slice(0, maxSegments),
40214
+ ...all.length > maxSegments ? { segmentsTruncated: true } : {}
40215
+ } : {}
40216
+ };
40217
+ }
40218
+ function fitTranscriptsToBudget(result, maxBytes, measure) {
40219
+ if (measure(result) <= maxBytes) return result;
40220
+ let next = {
40221
+ ...result,
40222
+ truncated: true,
40223
+ attachments: result.attachments.map((a) => {
40224
+ if (!a.segments) return a;
40225
+ const rest = { ...a, segmentsTruncated: true };
40226
+ delete rest.segments;
40227
+ return rest;
40228
+ })
40229
+ };
40230
+ for (let share = 0.5; measure(next) > maxBytes && share > 1e-4; share /= 2) {
40231
+ next = {
40232
+ ...next,
40233
+ attachments: next.attachments.map((a) => {
40234
+ const original = result.attachments.find((o) => o.attachmentId === a.attachmentId);
40235
+ if (!original?.text) return a;
40236
+ const keep = Math.floor(original.text.length * share);
40237
+ return { ...a, text: original.text.slice(0, keep), textTruncated: true };
40238
+ })
40239
+ };
40240
+ }
40241
+ return next;
40242
+ }
40243
+ var clock = (seconds) => {
40244
+ const total = Math.round(seconds);
40245
+ const h = Math.floor(total / 3600);
40246
+ const m = Math.floor(total % 3600 / 60);
40247
+ const s = total % 60;
40248
+ const mm = h ? String(m).padStart(2, "0") : String(m);
40249
+ return `${h ? `${h}:` : ""}${mm}:${String(s).padStart(2, "0")}`;
40250
+ };
40251
+ function formatTranscriptsText(result) {
40252
+ const count = result.attachments.length;
40253
+ if (count === 0) return `No audio attachments found in note ${result.id}.`;
40254
+ const lines = [`${count} audio attachment${count === 1 ? "" : "s"} in note ${result.id}:`];
40255
+ result.attachments.forEach((a, i) => {
40256
+ const facts = [
40257
+ `status ${a.status}`,
40258
+ ...a.durationSeconds !== void 0 ? [clock(a.durationSeconds)] : [],
40259
+ ...a.wordCount !== void 0 ? [`${a.wordCount} words`] : [],
40260
+ ...a.fragmentCount && a.fragmentCount > 1 ? [`${a.fragmentCount} fragments`] : [],
40261
+ ...a.speakers?.length ? [`${a.speakers.length} speakers`] : [],
40262
+ ...a.reason ? [`reason: ${a.reason}`] : []
40263
+ ];
40264
+ lines.push("", `[${i + 1}] ${a.attachmentId} (${facts.join(", ")})`);
40265
+ if (a.summary) lines.push(`Summary: ${a.summary}`);
40266
+ if (a.text) lines.push(a.textTruncated ? `${a.text} [truncated]` : a.text);
40267
+ });
40268
+ if (result.truncated)
40269
+ lines.push(
40270
+ "",
40271
+ "Some segments or text were left out to stay under the response size limit (APPLE_NOTES_MCP_EXPORT_MAX_BYTES)."
40272
+ );
40273
+ return lines.join("\n");
40274
+ }
40275
+
40276
+ // src/utils/noteTables.ts
40277
+ import { gunzipSync as gunzipSync4 } from "node:zlib";
40278
+ var sub2 = (f, n) => {
39907
40279
  const value = embeddedMessage(getField(f, n));
39908
40280
  if (!value) throw new Error(`Missing table field ${n}`);
39909
40281
  return value;
@@ -39913,7 +40285,7 @@ var num = (f, n) => {
39913
40285
  if (v === void 0) throw new Error(`Missing table index ${n}`);
39914
40286
  return v;
39915
40287
  };
39916
- var many = (f, n) => getFields(f, n).map((v) => {
40288
+ var many2 = (f, n) => getFields(f, n).map((v) => {
39917
40289
  const m = embeddedMessage(v);
39918
40290
  if (!m) throw new Error("Invalid table entry");
39919
40291
  return m;
@@ -39930,37 +40302,37 @@ function parseNoteTableCells(compressed) {
39930
40302
  return decodeTable(compressed, false);
39931
40303
  }
39932
40304
  function decodeTable(compressed, strict) {
39933
- const root = decodeMessage(gunzipSync3(compressed, { maxOutputLength: 16 * 1024 * 1024 }));
39934
- const data = sub(sub(root, 2), 3), entries = many(data, 3);
40305
+ const root = decodeMessage(gunzipSync4(compressed, { maxOutputLength: 16 * 1024 * 1024 }));
40306
+ const data = sub2(sub2(root, 2), 3), entries = many2(data, 3);
39935
40307
  if (entries.length > 1e5) throw new Error("Table too large");
39936
40308
  const keys = getFields(data, 4).map(stringValue), types = getFields(data, 5).map(stringValue), uuids = getFields(data, 6).map(hex);
39937
40309
  const entry = (index) => {
39938
40310
  if (!entries[index]) throw new Error("Invalid table reference");
39939
40311
  return entries[index];
39940
40312
  };
39941
- const uuidIndex = (index) => num(sub(many(sub(entry(index), 13), 3)[0], 2), 2);
40313
+ const uuidIndex = (index) => num(sub2(many2(sub2(entry(index), 13), 3)[0], 2), 2);
39942
40314
  const roots = entries.filter((e) => {
39943
40315
  const map = embeddedMessage(getField(e, 13));
39944
40316
  return map && types[num(map, 1)] === "com.apple.notes.ICTable";
39945
40317
  });
39946
40318
  if (roots.length !== 1) throw new Error("Ambiguous native table root");
39947
40319
  const refs = new Map(
39948
- many(sub(roots[0], 13), 3).filter((m) => ["crRows", "crColumns", "cellColumns"].includes(keys[num(m, 1)] || "")).map((m) => [keys[num(m, 1)], num(sub(m, 2), 6)])
40320
+ many2(sub2(roots[0], 13), 3).filter((m) => ["crRows", "crColumns", "cellColumns"].includes(keys[num(m, 1)] || "")).map((m) => [keys[num(m, 1)], num(sub2(m, 2), 6)])
39949
40321
  );
39950
40322
  const ordered = (key) => {
39951
40323
  const ref = refs.get(key);
39952
40324
  if (ref === void 0) throw new Error("Missing table dimension");
39953
- const ordering = sub(sub(entry(ref), 16), 1), array2 = sub(ordering, 1);
39954
- const ids = many(array2, 2).map((a) => hex(getField(a, 2)));
40325
+ const ordering = sub2(sub2(entry(ref), 16), 1), array2 = sub2(ordering, 1);
40326
+ const ids = many2(array2, 2).map((a) => hex(getField(a, 2)));
39955
40327
  const map = /* @__PURE__ */ new Map();
39956
40328
  ids.forEach((id2, i) => {
39957
40329
  const index = uuids.indexOf(id2);
39958
40330
  if (index < 0) throw new Error("Missing dimension UUID");
39959
40331
  map.set(index, i);
39960
40332
  });
39961
- const aliases = many(sub(ordering, 2), 1).map((pair) => [
39962
- uuidIndex(num(sub(pair, 1), 6)),
39963
- uuidIndex(num(sub(pair, 2), 6))
40333
+ const aliases = many2(sub2(ordering, 2), 1).map((pair) => [
40334
+ uuidIndex(num(sub2(pair, 1), 6)),
40335
+ uuidIndex(num(sub2(pair, 2), 6))
39964
40336
  ]);
39965
40337
  for (let pass = 0; pass < aliases.length + 1; pass++) {
39966
40338
  let changed = false;
@@ -39980,13 +40352,13 @@ function decodeTable(compressed, strict) {
39980
40352
  const incomplete = [];
39981
40353
  const cellRef = refs.get("cellColumns");
39982
40354
  if (cellRef === void 0) throw new Error("Missing table cells");
39983
- for (const column of many(sub(entry(cellRef), 6), 1)) {
39984
- const ci = columns.map.get(uuidIndex(num(sub(column, 1), 6)));
39985
- const cells = entry(num(sub(column, 2), 6));
39986
- for (const row of many(sub(cells, 6), 1)) {
39987
- const ri = rows.map.get(uuidIndex(num(sub(row, 1), 6)));
40355
+ for (const column of many2(sub2(entry(cellRef), 6), 1)) {
40356
+ const ci = columns.map.get(uuidIndex(num(sub2(column, 1), 6)));
40357
+ const cells = entry(num(sub2(column, 2), 6));
40358
+ for (const row of many2(sub2(cells, 6), 1)) {
40359
+ const ri = rows.map.get(uuidIndex(num(sub2(row, 1), 6)));
39988
40360
  if (ri === void 0 || ci === void 0) continue;
39989
- const note = sub(entry(num(sub(row, 2), 6)), 10);
40361
+ const note = sub2(entry(num(sub2(row, 2), 6)), 10);
39990
40362
  const text = stringValue(getField(note, 2));
39991
40363
  if (text === void 0 || text.includes("\uFFFC")) {
39992
40364
  if (strict) throw new Error("Embedded or unsupported table cell");
@@ -40003,8 +40375,8 @@ function decodeTable(compressed, strict) {
40003
40375
  }
40004
40376
  const rtl = entries.some((e) => {
40005
40377
  const map = embeddedMessage(getField(e, 13));
40006
- return map && many(map, 3).some(
40007
- (m) => stringValue(getField(sub(m, 2), 4)) === "CRTableColumnDirectionRightToLeft"
40378
+ return map && many2(map, 3).some(
40379
+ (m) => stringValue(getField(sub2(m, 2), 4)) === "CRTableColumnDirectionRightToLeft"
40008
40380
  );
40009
40381
  });
40010
40382
  const width = columns.ids.length;
@@ -40081,11 +40453,11 @@ function collectNoteTables(rich, noteId3) {
40081
40453
  }
40082
40454
 
40083
40455
  // src/utils/smartFolders.ts
40084
- import { execFileSync as execFileSync4 } from "child_process";
40456
+ import { execFileSync as execFileSync5 } from "child_process";
40085
40457
  import * as fs2 from "fs";
40086
40458
  import * as os2 from "os";
40087
40459
  import * as path2 from "path";
40088
- var NOTES_DB_PATH2 = path2.join(
40460
+ var NOTES_DB_PATH3 = path2.join(
40089
40461
  os2.homedir(),
40090
40462
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
40091
40463
  );
@@ -40376,7 +40748,7 @@ WHERE Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICHashtag')
40376
40748
  AND ZSTANDARDIZEDCONTENT IS NOT NULL
40377
40749
  AND COALESCE(ZMARKEDFORDELETION, 0) = 0;
40378
40750
  COMMIT;`;
40379
- var REQUIRED_COLUMNS = [
40751
+ var REQUIRED_COLUMNS2 = [
40380
40752
  "ZIDENTIFIER",
40381
40753
  "ZTITLE2",
40382
40754
  "ZFOLDERTYPE",
@@ -40430,21 +40802,21 @@ function buildSmartFolders(output) {
40430
40802
  (a, b) => (a.account ?? "").localeCompare(b.account ?? "") || (a.name ?? "").localeCompare(b.name ?? "") || a.id.localeCompare(b.id)
40431
40803
  );
40432
40804
  }
40433
- function runSqlite(dbPath2, sql) {
40434
- return execFileSync4("sqlite3", ["-readonly", dbPath2, sql], {
40805
+ function runSqlite2(dbPath2, sql) {
40806
+ return execFileSync5("sqlite3", ["-readonly", dbPath2, sql], {
40435
40807
  encoding: "utf8",
40436
40808
  timeout: 5e3,
40437
40809
  maxBuffer: 16 * 1024 * 1024,
40438
40810
  stdio: ["pipe", "pipe", "pipe"]
40439
40811
  });
40440
40812
  }
40441
- function readSmartFolders(dbPath2 = NOTES_DB_PATH2) {
40813
+ function readSmartFolders(dbPath2 = NOTES_DB_PATH3) {
40442
40814
  if (!fs2.existsSync(dbPath2)) return { folders: null, error: "no_fda", message: FDA_MESSAGE };
40443
40815
  try {
40444
40816
  const columns = new Set(
40445
- runSqlite(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").trim().split("\n")
40817
+ runSqlite2(dbPath2, "SELECT name FROM pragma_table_info('ZICCLOUDSYNCINGOBJECT');").trim().split("\n")
40446
40818
  );
40447
- const missing = REQUIRED_COLUMNS.filter((column) => !columns.has(column));
40819
+ const missing = REQUIRED_COLUMNS2.filter((column) => !columns.has(column));
40448
40820
  if (missing.length) {
40449
40821
  return {
40450
40822
  folders: null,
@@ -40452,7 +40824,7 @@ function readSmartFolders(dbPath2 = NOTES_DB_PATH2) {
40452
40824
  message: `This Notes database does not have the columns smart folders need (${missing.join(", ")}).`
40453
40825
  };
40454
40826
  }
40455
- return { folders: buildSmartFolders(runSqlite(dbPath2, SMART_FOLDERS_SQL)) };
40827
+ return { folders: buildSmartFolders(runSqlite2(dbPath2, SMART_FOLDERS_SQL)) };
40456
40828
  } catch (error2) {
40457
40829
  const message = error2 instanceof Error ? error2.message : String(error2);
40458
40830
  if (message.includes("authorization denied") || message.includes("unable to open database")) {
@@ -40474,11 +40846,11 @@ import {
40474
40846
  rmSync,
40475
40847
  statSync
40476
40848
  } from "fs";
40477
- import { dirname, isAbsolute, join as join4, relative, resolve, sep } from "path";
40478
- import { homedir as homedir4, tmpdir } from "os";
40849
+ import { dirname, isAbsolute, join as join5, relative, resolve, sep } from "path";
40850
+ import { homedir as homedir5, tmpdir } from "os";
40479
40851
  function allowedSaveRoots() {
40480
40852
  return [
40481
- resolve(homedir4()),
40853
+ resolve(homedir5()),
40482
40854
  resolve(tmpdir()),
40483
40855
  "/Volumes",
40484
40856
  "/private/var/folders",
@@ -40552,7 +40924,7 @@ function assertSafeSavePath(p, roots = allowedSaveRoots()) {
40552
40924
  if (suffix.split(sep).includes("..")) {
40553
40925
  throw new Error(`Refusing to write outside allowed locations (home, temp, /Volumes): "${abs}"`);
40554
40926
  }
40555
- const canonicalDest = suffix ? join4(canonicalAncestor, suffix) : canonicalAncestor;
40927
+ const canonicalDest = suffix ? join5(canonicalAncestor, suffix) : canonicalAncestor;
40556
40928
  const allowed = canonicalRoots(roots);
40557
40929
  if (!isWithinRoots(canonicalAncestor, allowed) || !isWithinRoots(canonicalDest, allowed)) {
40558
40930
  throw new Error(
@@ -40602,8 +40974,8 @@ function cleanupTempDir(dir) {
40602
40974
  // src/services/appleNotesManager.ts
40603
40975
  var import_turndown = __toESM(require_turndown_cjs(), 1);
40604
40976
  import { existsSync as existsSync4 } from "fs";
40605
- import { homedir as homedir5 } from "os";
40606
- import { join as join5 } from "path";
40977
+ import { homedir as homedir6 } from "os";
40978
+ import { join as join6 } from "path";
40607
40979
  var FIELD_SEP = "";
40608
40980
  var RECORD_SEP = "";
40609
40981
  var AS_FIELD_SEP = "(character id 31)";
@@ -40831,7 +41203,7 @@ function getNoteLinkFromDB(coreDataId2) {
40831
41203
  const match = coreDataId2.match(/\/p(\d+)$/);
40832
41204
  if (!match) return null;
40833
41205
  const pk = parseInt(match[1], 10);
40834
- const dbPath2 = join5(homedir5(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
41206
+ const dbPath2 = join6(homedir6(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
40835
41207
  if (!existsSync4(dbPath2)) return null;
40836
41208
  try {
40837
41209
  const { DatabaseSync } = __require("node:sqlite");
@@ -43140,18 +43512,31 @@ var AppleNotesManager = class {
43140
43512
  }
43141
43513
  return markdown;
43142
43514
  }
43515
+ /**
43516
+ * Reads the transcripts Notes has stored for a note's top-level audio
43517
+ * recordings, one entry per attachment in body order. Read-only: queries the
43518
+ * NoteStore database with `sqlite3 -readonly` and needs Full Disk Access.
43519
+ *
43520
+ * @param id - CoreData URL identifier for the note
43521
+ * @param options - word-level segment inclusion and cap
43522
+ * @throws AudioTranscriptError for an invalid id, a missing or locked note,
43523
+ * missing Full Disk Access, or a database read failure
43524
+ */
43525
+ getAudioTranscripts(id2, options = {}) {
43526
+ return readAudioTranscripts(id2, options);
43527
+ }
43143
43528
  };
43144
43529
 
43145
43530
  // src/utils/syncDetection.ts
43146
- import { execFileSync as execFileSync5 } from "child_process";
43531
+ import { execFileSync as execFileSync6 } from "child_process";
43147
43532
  import * as fs3 from "fs";
43148
43533
  import * as path3 from "path";
43149
43534
  import * as os3 from "os";
43150
- var NOTES_DB_PATH3 = path3.join(
43535
+ var NOTES_DB_PATH4 = path3.join(
43151
43536
  os3.homedir(),
43152
43537
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
43153
43538
  );
43154
- var WAL_PATH = `${NOTES_DB_PATH3}-wal`;
43539
+ var WAL_PATH = `${NOTES_DB_PATH4}-wal`;
43155
43540
  var RECENT_ACTIVITY_THRESHOLD_SECONDS = 5;
43156
43541
  var SYNC_STATUS_CACHE_TTL_MS = 2e3;
43157
43542
  var cachedSyncStatus = null;
@@ -43167,7 +43552,7 @@ function getSyncStatus(useCache = true) {
43167
43552
  recentActivity: false
43168
43553
  };
43169
43554
  try {
43170
- if (!fs3.existsSync(NOTES_DB_PATH3)) {
43555
+ if (!fs3.existsSync(NOTES_DB_PATH4)) {
43171
43556
  status.error = "Notes database not found";
43172
43557
  cachedSyncStatus = status;
43173
43558
  cacheTimestamp = Date.now();
@@ -43188,9 +43573,9 @@ function getSyncStatus(useCache = true) {
43188
43573
  WHERE object.ZCLOUDSTATE = state.Z_PK
43189
43574
  );
43190
43575
  `;
43191
- const result = execFileSync5(
43576
+ const result = execFileSync6(
43192
43577
  "sqlite3",
43193
- ["-readonly", NOTES_DB_PATH3, query.replace(/\n/g, " ")],
43578
+ ["-readonly", NOTES_DB_PATH4, query.replace(/\n/g, " ")],
43194
43579
  {
43195
43580
  encoding: "utf8",
43196
43581
  timeout: 5e3,
@@ -43247,11 +43632,11 @@ function withSyncAwarenessSync(operation, fn) {
43247
43632
  }
43248
43633
 
43249
43634
  // src/utils/noteMetadata.ts
43250
- import { execFileSync as execFileSync6 } from "child_process";
43635
+ import { execFileSync as execFileSync7 } from "child_process";
43251
43636
  import * as fs4 from "fs";
43252
43637
  import * as path4 from "path";
43253
43638
  import * as os4 from "os";
43254
- var NOTES_DB_PATH4 = path4.join(
43639
+ var NOTES_DB_PATH5 = path4.join(
43255
43640
  os4.homedir(),
43256
43641
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
43257
43642
  );
@@ -43267,15 +43652,15 @@ var COLUMN_MAP = [
43267
43652
  { key: "widgetSnippet", column: "ZWIDGETSNIPPET", type: "text" },
43268
43653
  { key: "smartFolderQuery", column: "ZSMARTFOLDERQUERYJSON", type: "text" }
43269
43654
  ];
43270
- function runSqlite2(query) {
43271
- return execFileSync6("sqlite3", ["-readonly", NOTES_DB_PATH4, query], {
43655
+ function runSqlite3(query) {
43656
+ return execFileSync7("sqlite3", ["-readonly", NOTES_DB_PATH5, query], {
43272
43657
  encoding: "utf8",
43273
43658
  timeout: 5e3,
43274
43659
  stdio: ["pipe", "pipe", "pipe"]
43275
43660
  }).trim();
43276
43661
  }
43277
43662
  function presentColumns() {
43278
- const out = runSqlite2("PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);");
43663
+ const out = runSqlite3("PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);");
43279
43664
  const cols = /* @__PURE__ */ new Set();
43280
43665
  for (const line of out.split("\n")) {
43281
43666
  const name = line.split("|")[1];
@@ -43293,7 +43678,7 @@ function getNoteMetadata(noteId3) {
43293
43678
  };
43294
43679
  }
43295
43680
  const pk = pkMatch[1];
43296
- if (!fs4.existsSync(NOTES_DB_PATH4)) {
43681
+ if (!fs4.existsSync(NOTES_DB_PATH5)) {
43297
43682
  return { metadata: null, error: "no_fda", message: FDA_MESSAGE2 };
43298
43683
  }
43299
43684
  try {
@@ -43303,7 +43688,7 @@ function getNoteMetadata(noteId3) {
43303
43688
  return { metadata: {} };
43304
43689
  }
43305
43690
  const pairs = selected.map((c) => `'${c.key}', ${c.column}`).join(", ");
43306
- const row = runSqlite2(
43691
+ const row = runSqlite3(
43307
43692
  `SELECT json_object(${pairs}) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = ${pk};`
43308
43693
  );
43309
43694
  if (!row) {
@@ -43336,11 +43721,11 @@ function getNoteMetadata(noteId3) {
43336
43721
  }
43337
43722
 
43338
43723
  // src/utils/noteIdentifiers.ts
43339
- import { execFileSync as execFileSync7 } from "child_process";
43724
+ import { execFileSync as execFileSync8 } from "child_process";
43340
43725
  import * as fs5 from "fs";
43341
43726
  import * as os5 from "os";
43342
43727
  import * as path5 from "path";
43343
- var NOTES_DB_PATH5 = path5.join(
43728
+ var NOTES_DB_PATH6 = path5.join(
43344
43729
  os5.homedir(),
43345
43730
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
43346
43731
  );
@@ -43418,7 +43803,7 @@ function runJsonQuery(sql, dbPath2) {
43418
43803
  if (!fs5.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
43419
43804
  let out;
43420
43805
  try {
43421
- out = execFileSync7("sqlite3", ["-readonly", dbPath2, sql], {
43806
+ out = execFileSync8("sqlite3", ["-readonly", dbPath2, sql], {
43422
43807
  encoding: "utf8",
43423
43808
  timeout: 5e3,
43424
43809
  stdio: ["pipe", "pipe", "pipe"]
@@ -43438,7 +43823,7 @@ function runJsonQuery(sql, dbPath2) {
43438
43823
  function coreDataId(store, entity2, pk) {
43439
43824
  return `x-coredata://${store}/${entity2}/p${pk}`;
43440
43825
  }
43441
- function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH5) {
43826
+ function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH6) {
43442
43827
  const resolved = /* @__PURE__ */ new Map();
43443
43828
  const keys = [];
43444
43829
  const uuids = [];
@@ -43484,7 +43869,7 @@ function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH5) {
43484
43869
  return resolved;
43485
43870
  }
43486
43871
  var COREDATA_PARTS = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/(ICNote|ICFolder|ICAccount)\/p(\d{1,18})$/;
43487
- function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH5) {
43872
+ function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH6) {
43488
43873
  const result = /* @__PURE__ */ new Map();
43489
43874
  const wanted = /* @__PURE__ */ new Map();
43490
43875
  for (const id2 of ids) {
@@ -43520,7 +43905,7 @@ function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH5) {
43520
43905
  }
43521
43906
  return result;
43522
43907
  }
43523
- function withStableIdentifiers(items, entity2, dbPath2 = NOTES_DB_PATH5) {
43908
+ function withStableIdentifiers(items, entity2, dbPath2 = NOTES_DB_PATH6) {
43524
43909
  const ids = items.map((item) => item.id).filter((id2) => typeof id2 === "string");
43525
43910
  if (ids.length === 0) return items;
43526
43911
  const found = lookupStableIdentifiers(ids, entity2, dbPath2);
@@ -44108,12 +44493,12 @@ function positiveTextTerms(node, negated = false) {
44108
44493
  }
44109
44494
 
44110
44495
  // src/utils/noteQueryStore.ts
44111
- import { execFileSync as execFileSync8 } from "child_process";
44496
+ import { execFileSync as execFileSync9 } from "child_process";
44112
44497
  import * as fs6 from "fs";
44113
44498
  import * as os6 from "os";
44114
44499
  import * as path6 from "path";
44115
- import { gunzipSync as gunzipSync4 } from "zlib";
44116
- var NOTES_DB_PATH6 = path6.join(
44500
+ import { gunzipSync as gunzipSync5 } from "zlib";
44501
+ var NOTES_DB_PATH7 = path6.join(
44117
44502
  os6.homedir(),
44118
44503
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
44119
44504
  );
@@ -44201,13 +44586,13 @@ function countWords(text) {
44201
44586
  }
44202
44587
  return count;
44203
44588
  }
44204
- var REQUIRED_COLUMNS2 = ["Z_PK", "Z_ENT", "ZTITLE1", "ZFOLDER", "ZMODIFICATIONDATE1"];
44589
+ var REQUIRED_COLUMNS3 = ["Z_PK", "Z_ENT", "ZTITLE1", "ZFOLDER", "ZMODIFICATIONDATE1"];
44205
44590
  function col(available, alias, name) {
44206
44591
  return available.has(name) ? `${alias}.${name}` : "NULL";
44207
44592
  }
44208
44593
  var entity = (name) => `(SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME='${name}')`;
44209
44594
  function buildScanSql(available, options) {
44210
- const missing = REQUIRED_COLUMNS2.filter((c) => !available.has(c));
44595
+ const missing = REQUIRED_COLUMNS3.filter((c) => !available.has(c));
44211
44596
  if (missing.length) {
44212
44597
  throw new NoteQueryStoreError(
44213
44598
  `This macOS version's Notes database lacks columns query-notes needs (${missing.join(", ")}).`,
@@ -44248,8 +44633,8 @@ function buildScanSql(available, options) {
44248
44633
  "COMMIT;"
44249
44634
  ].join(" ");
44250
44635
  }
44251
- function runSqlite3(dbPath2, query) {
44252
- return execFileSync8("sqlite3", ["-readonly", dbPath2, query], {
44636
+ function runSqlite4(dbPath2, query) {
44637
+ return execFileSync9("sqlite3", ["-readonly", dbPath2, query], {
44253
44638
  encoding: "utf8",
44254
44639
  timeout: 3e4,
44255
44640
  maxBuffer: 512 * 1024 * 1024,
@@ -44258,7 +44643,7 @@ function runSqlite3(dbPath2, query) {
44258
44643
  }
44259
44644
  function presentColumns2(dbPath2) {
44260
44645
  const cols = /* @__PURE__ */ new Set();
44261
- for (const line of runSqlite3(dbPath2, "PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);").split("\n")) {
44646
+ for (const line of runSqlite4(dbPath2, "PRAGMA table_info(ZICCLOUDSYNCINGOBJECT);").split("\n")) {
44262
44647
  const name = line.split("|")[1];
44263
44648
  if (name) cols.add(name);
44264
44649
  }
@@ -44313,12 +44698,12 @@ function queryNotes(expression, options = {}) {
44313
44698
  const includeDeleted = options.includeDeleted ?? false;
44314
44699
  const withBodies = needsContent(ast);
44315
44700
  const withTags = needsTags(ast);
44316
- const dbPath2 = options.dbPath ?? NOTES_DB_PATH6;
44701
+ const dbPath2 = options.dbPath ?? NOTES_DB_PATH7;
44317
44702
  if (!fs6.existsSync(dbPath2)) throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
44318
44703
  let output;
44319
44704
  try {
44320
44705
  const available = presentColumns2(dbPath2);
44321
- output = runSqlite3(
44706
+ output = runSqlite4(
44322
44707
  dbPath2,
44323
44708
  buildScanSql(available, { scanLimit, includeDeleted, withBodies, withTags })
44324
44709
  );
@@ -44363,7 +44748,7 @@ function queryNotes(expression, options = {}) {
44363
44748
  const locked = Boolean(row.locked);
44364
44749
  let decoded;
44365
44750
  let content;
44366
- const decode = () => {
44751
+ const decode2 = () => {
44367
44752
  if (decoded !== void 0) return decoded;
44368
44753
  decoded = null;
44369
44754
  if (locked) return decoded;
@@ -44374,7 +44759,7 @@ function queryNotes(expression, options = {}) {
44374
44759
  try {
44375
44760
  decoded = decodeNoteBody(
44376
44761
  new Uint8Array(
44377
- gunzipSync4(Buffer.from(row.data, "hex"), { maxOutputLength: 32 * 1024 * 1024 })
44762
+ gunzipSync5(Buffer.from(row.data, "hex"), { maxOutputLength: 32 * 1024 * 1024 })
44378
44763
  )
44379
44764
  );
44380
44765
  } catch {
@@ -44394,7 +44779,7 @@ function queryNotes(expression, options = {}) {
44394
44779
  modified: coreDataMs(row.modified),
44395
44780
  content: () => {
44396
44781
  if (content !== void 0) return content;
44397
- const body2 = decode();
44782
+ const body2 = decode2();
44398
44783
  if (!body2) return content = null;
44399
44784
  const text = body2.text;
44400
44785
  const firstBreak = text.indexOf("\n");
@@ -44419,7 +44804,7 @@ function queryNotes(expression, options = {}) {
44419
44804
  if (!evaluateNoteQuery(ast, note)) continue;
44420
44805
  matched++;
44421
44806
  if (hits.length >= limit) continue;
44422
- const body = locked ? null : decode();
44807
+ const body = locked ? null : decode2();
44423
44808
  hits.push({
44424
44809
  id: `x-coredata://${uuid2}/ICNote/p${row.pk}`,
44425
44810
  title: row.title ?? "",
@@ -44449,10 +44834,10 @@ function queryNotes(expression, options = {}) {
44449
44834
  import { spawnSync } from "child_process";
44450
44835
 
44451
44836
  // src/services/nativeTags.ts
44452
- import { execFileSync as execFileSync9 } from "node:child_process";
44837
+ import { execFileSync as execFileSync10 } from "node:child_process";
44453
44838
  import { mkdtempSync as mkdtempSync2, writeFileSync, rmSync as rmSync2 } from "node:fs";
44454
44839
  import { tmpdir as tmpdir2 } from "node:os";
44455
- import { join as join10 } from "node:path";
44840
+ import { join as join11 } from "node:path";
44456
44841
 
44457
44842
  // src/services/shortcutConsent.ts
44458
44843
  function shortcutConsentHint(shortcut) {
@@ -44528,7 +44913,7 @@ function addNativeTags(request, deps) {
44528
44913
  };
44529
44914
  }
44530
44915
  function listInstalledShortcuts() {
44531
- return execFileSync9("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
44916
+ return execFileSync10("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
44532
44917
  encoding: "utf8",
44533
44918
  timeout: 15e3,
44534
44919
  maxBuffer: 1024 * 1024,
@@ -44554,11 +44939,11 @@ function runNativeTagsShortcut(input) {
44554
44939
  const status = nativeTagsStatus();
44555
44940
  if (!status.installed)
44556
44941
  throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
44557
- const directory = mkdtempSync2(join10(tmpdir2(), "apple-notes-native-tags-"));
44942
+ const directory = mkdtempSync2(join11(tmpdir2(), "apple-notes-native-tags-"));
44558
44943
  try {
44559
- const path7 = join10(directory, "request.json");
44944
+ const path7 = join11(directory, "request.json");
44560
44945
  writeFileSync(path7, JSON.stringify(input), { mode: 384 });
44561
- execFileSync9("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path7], {
44946
+ execFileSync10("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path7], {
44562
44947
  encoding: "utf8",
44563
44948
  timeout: 6e4,
44564
44949
  maxBuffer: 1024 * 1024,
@@ -44577,10 +44962,10 @@ function runNativeTagsShortcut(input) {
44577
44962
  }
44578
44963
 
44579
44964
  // src/services/backgroundNotes.ts
44580
- import { execFileSync as execFileSync10 } from "node:child_process";
44965
+ import { execFileSync as execFileSync11 } from "node:child_process";
44581
44966
  import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
44582
44967
  import { tmpdir as tmpdir3 } from "node:os";
44583
- import { join as join11 } from "node:path";
44968
+ import { join as join12 } from "node:path";
44584
44969
 
44585
44970
  // src/utils/appendMarkdown.ts
44586
44971
  var escape2 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -44769,9 +45154,9 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
44769
45154
  throw new Error(
44770
45155
  `Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
44771
45156
  );
44772
- const directory = mkdtempSync3(join11(tmpdir3(), "apple-notes-background-"));
45157
+ const directory = mkdtempSync3(join12(tmpdir3(), "apple-notes-background-"));
44773
45158
  try {
44774
- const file = join11(directory, "request.json");
45159
+ const file = join12(directory, "request.json");
44775
45160
  writeFileSync2(
44776
45161
  file,
44777
45162
  JSON.stringify({
@@ -44791,7 +45176,7 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
44791
45176
  { mode: 384 }
44792
45177
  );
44793
45178
  try {
44794
- execFileSync10("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
45179
+ execFileSync11("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
44795
45180
  encoding: "utf8",
44796
45181
  timeout: 6e4,
44797
45182
  maxBuffer: 1024 * 1024,
@@ -45083,7 +45468,7 @@ ${request.content}`
45083
45468
  }
45084
45469
 
45085
45470
  // src/services/capabilityMatrix.ts
45086
- import { execFileSync as execFileSync11 } from "node:child_process";
45471
+ import { execFileSync as execFileSync12 } from "node:child_process";
45087
45472
  import { release } from "node:os";
45088
45473
  var MACOS_SHORTCUTS_CLI = "12.0";
45089
45474
  var MACOS_MARKDOWN_IMPORT = "26.0";
@@ -45290,7 +45675,7 @@ function evaluateFeatures(env, features = FEATURES) {
45290
45675
  function readMacOSVersion() {
45291
45676
  if (process.platform !== "darwin") return null;
45292
45677
  try {
45293
- const out = execFileSync11("/usr/bin/sw_vers", ["-productVersion"], {
45678
+ const out = execFileSync12("/usr/bin/sw_vers", ["-productVersion"], {
45294
45679
  encoding: "utf8",
45295
45680
  timeout: 3e3,
45296
45681
  stdio: ["ignore", "pipe", "ignore"]
@@ -45444,12 +45829,12 @@ function formatDoctorReport(r) {
45444
45829
 
45445
45830
  // src/services/fileConfig.ts
45446
45831
  import { existsSync as existsSync9, readFileSync as readFileSync2 } from "fs";
45447
- import { join as join12 } from "path";
45448
- import { homedir as homedir10 } from "os";
45832
+ import { join as join13 } from "path";
45833
+ import { homedir as homedir11 } from "os";
45449
45834
  function fileConfigPath(env = process.env) {
45450
45835
  const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
45451
45836
  if (override && override.trim()) return override.trim();
45452
- return join12(homedir10(), "Library", "Application Support", "apple-notes-mcp", "config.json");
45837
+ return join13(homedir11(), "Library", "Application Support", "apple-notes-mcp", "config.json");
45453
45838
  }
45454
45839
  function loadFileConfig(env = process.env, path7 = fileConfigPath(env)) {
45455
45840
  const applied = [];
@@ -45752,11 +46137,11 @@ function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS) {
45752
46137
  }
45753
46138
 
45754
46139
  // src/utils/noteBlocks.ts
45755
- import { execFileSync as execFileSync12 } from "node:child_process";
46140
+ import { execFileSync as execFileSync13 } from "node:child_process";
45756
46141
  import { existsSync as existsSync10 } from "node:fs";
45757
- import { homedir as homedir11 } from "node:os";
45758
- import { join as join13 } from "node:path";
45759
- import { gunzipSync as gunzipSync5 } from "node:zlib";
46142
+ import { homedir as homedir12 } from "node:os";
46143
+ import { join as join14 } from "node:path";
46144
+ import { gunzipSync as gunzipSync6 } from "node:zlib";
45760
46145
  var NoteBlocksError = class extends Error {
45761
46146
  code;
45762
46147
  constructor(code, message) {
@@ -45805,7 +46190,7 @@ var stringOf = (fields, n) => {
45805
46190
  const b = bytesOf(fields, n);
45806
46191
  return b ? utf8.decode(b) : void 0;
45807
46192
  };
45808
- var sub2 = (fields, n) => {
46193
+ var sub3 = (fields, n) => {
45809
46194
  const b = bytesOf(fields, n);
45810
46195
  return b ? decodeWireFields(b) : void 0;
45811
46196
  };
@@ -45838,7 +46223,7 @@ function decodeParagraph(fields, tally) {
45838
46223
  blockQuote: (varintOf(fields, 8) ?? 0) !== 0
45839
46224
  };
45840
46225
  if (attrs.styleType === -1) attrs.styleType = null;
45841
- const checklist = sub2(fields, 5);
46226
+ const checklist = sub3(fields, 5);
45842
46227
  const checklistId = checklist && bytesOf(checklist, 1);
45843
46228
  if (attrs.styleType === 103 && checklistId)
45844
46229
  attrs.checklist = { id: hex2(checklistId), done: varintOf(checklist, 2) === 1 };
@@ -45848,7 +46233,7 @@ function decodeParagraph(fields, tally) {
45848
46233
  }
45849
46234
  function decodeInline(fields) {
45850
46235
  const inline = {};
45851
- const font = sub2(fields, 3);
46236
+ const font = sub3(fields, 3);
45852
46237
  const hints = font ? varintOf(font, 3) ?? 0 : 0;
45853
46238
  const weight = varintOf(fields, 5) ?? 0;
45854
46239
  if (weight === 1 || weight === 3 || hints & 1) inline.bold = true;
@@ -45858,7 +46243,7 @@ function decodeInline(fields) {
45858
46243
  const baseline = varintOf(fields, 8) ?? 0;
45859
46244
  if (baseline > 0) inline.superscript = true;
45860
46245
  if (baseline < 0) inline.subscript = true;
45861
- const color = sub2(fields, 10);
46246
+ const color = sub3(fields, 10);
45862
46247
  const colorValue = color && colorOf(color);
45863
46248
  if (colorValue) inline.color = colorValue;
45864
46249
  const emphasis = varintOf(fields, 14);
@@ -45880,7 +46265,7 @@ function decodeInline(fields) {
45880
46265
  ...size !== void 0 && Number.isFinite(size) ? { size } : {}
45881
46266
  };
45882
46267
  }
45883
- const attachment = sub2(fields, 12);
46268
+ const attachment = sub3(fields, 12);
45884
46269
  const attachmentId = attachment && stringOf(attachment, 1);
45885
46270
  if (attachmentId)
45886
46271
  inline.attachment = { id: attachmentId, uti: stringOf(attachment, 2) || "unknown" };
@@ -45895,8 +46280,8 @@ function wrap(error2) {
45895
46280
  function decodeNoteBlocks(data) {
45896
46281
  let note;
45897
46282
  try {
45898
- const document = sub2(decodeWireFields(data), 2);
45899
- note = document && sub2(document, 3);
46283
+ const document = sub3(decodeWireFields(data), 2);
46284
+ note = document && sub3(document, 3);
45900
46285
  } catch (error2) {
45901
46286
  wrap(error2);
45902
46287
  }
@@ -45921,7 +46306,7 @@ function decodeNoteBlocks(data) {
45921
46306
  runs.push({
45922
46307
  start: position,
45923
46308
  length,
45924
- paragraph: decodeParagraph(sub2(fields, 2), paragraphTally),
46309
+ paragraph: decodeParagraph(sub3(fields, 2), paragraphTally),
45925
46310
  inline: decodeInline(fields)
45926
46311
  });
45927
46312
  position += length;
@@ -46044,7 +46429,7 @@ function summarize(blocks, attachments) {
46044
46429
  function decodeCompressedNoteBlocks(compressed) {
46045
46430
  let data;
46046
46431
  try {
46047
- data = gunzipSync5(compressed, { maxOutputLength: 32 * 1024 * 1024 });
46432
+ data = gunzipSync6(compressed, { maxOutputLength: 32 * 1024 * 1024 });
46048
46433
  } catch (error2) {
46049
46434
  throw new NoteBlocksError(
46050
46435
  "decompress-failed",
@@ -46098,11 +46483,11 @@ function pageNoteBlocks(doc, { offset = 0, limit = 500, maxBytes = blocksMaxResp
46098
46483
  undecodedFields: doc.undecodedFields
46099
46484
  };
46100
46485
  }
46101
- var NOTES_DB_PATH7 = join13(
46102
- homedir11(),
46486
+ var NOTES_DB_PATH8 = join14(
46487
+ homedir12(),
46103
46488
  "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
46104
46489
  );
46105
- function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH7 } = {}) {
46490
+ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH8 } = {}) {
46106
46491
  const pk = /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p([0-9]{1,18})$/i.exec(id2)?.[1];
46107
46492
  if (!pk)
46108
46493
  throw new NoteBlocksError(
@@ -46114,7 +46499,7 @@ function readNoteBlocks(id2, { dbPath: dbPath2 = NOTES_DB_PATH7 } = {}) {
46114
46499
  const sql = "SELECT json_object('exists', (SELECT count(*) FROM ZICCLOUDSYNCINGOBJECT WHERE Z_PK = @pk AND Z_ENT = (SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = 'ICNote')), 'data', (SELECT hex(ZDATA) FROM ZICNOTEDATA WHERE ZNOTE = @pk), 'encrypted', (SELECT ZCRYPTOINITIALIZATIONVECTOR IS NOT NULL FROM ZICNOTEDATA WHERE ZNOTE = @pk));";
46115
46500
  let output;
46116
46501
  try {
46117
- output = execFileSync12(
46502
+ output = execFileSync13(
46118
46503
  "/usr/bin/sqlite3",
46119
46504
  ["-readonly", "-cmd", ".parameter init", "-cmd", `.parameter set @pk ${pk}`, dbPath2, sql],
46120
46505
  {
@@ -46275,7 +46660,7 @@ import {
46275
46660
  writeFileSync as writeFileSync3
46276
46661
  } from "node:fs";
46277
46662
  import { tmpdir as tmpdir4 } from "node:os";
46278
- import { basename, isAbsolute as isAbsolute2, join as join14 } from "node:path";
46663
+ import { basename, isAbsolute as isAbsolute2, join as join15 } from "node:path";
46279
46664
  var noteId = exactIdInput(
46280
46665
  "ICNote",
46281
46666
  /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
@@ -46389,8 +46774,8 @@ function registerDirectOperations(server2, manager) {
46389
46774
  if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
46390
46775
  const bytes = localAttachment(path7);
46391
46776
  const beforeAttachments = manager.listAttachmentsById(id2);
46392
- const directory = mkdtempSync4(join14(tmpdir4(), "notes-attachment-add-"));
46393
- const temporaryFile = join14(directory, basename(path7));
46777
+ const directory = mkdtempSync4(join15(tmpdir4(), "notes-attachment-add-"));
46778
+ const temporaryFile = join15(directory, basename(path7));
46394
46779
  try {
46395
46780
  writeFileSync3(temporaryFile, bytes, { mode: 384 });
46396
46781
  if (readSnapshot(manager, id2).hash !== before.hash)
@@ -49098,6 +49483,44 @@ ${summary}`,
49098
49483
  );
49099
49484
  }, "Error reading checklist state")
49100
49485
  );
49486
+ registerTool(
49487
+ "get-audio-transcripts",
49488
+ {
49489
+ description: "Use when: reading the transcript (and summary, if any) that Notes already computed for the audio recordings in one note, by id.\nReturns: one entry per top-level audio attachment in body order, with attachmentId, durationSeconds, status (ok, none when no transcript is stored, or undecodable with a reason), joined transcript text, wordCount, speakers and summary when stored, and optional word-level segments.\nDo not use when: you need the audio file itself (save-attachment) or the note body (get-note-content). This tool does not transcribe; it only reads what Notes stored.\nSafety: read-only; requires Full Disk Access and reads the NoteStore database. Password-protected notes are refused. Large responses drop segments first, then shorten text, to stay under APPLE_NOTES_MCP_EXPORT_MAX_BYTES.",
49490
+ inputSchema: {
49491
+ id: noteIdInput,
49492
+ includeSegments: external_exports.boolean().optional().describe(
49493
+ "Also return word-level segments (text, start and duration in seconds, speaker). Default false."
49494
+ ),
49495
+ maxSegments: external_exports.number().int().min(1).max(MAX_SEGMENTS_LIMIT).optional().describe(
49496
+ `Cap on segments returned per attachment when includeSegments is true (default ${DEFAULT_MAX_SEGMENTS}).`
49497
+ )
49498
+ },
49499
+ outputSchema: {
49500
+ id: external_exports.string().optional(),
49501
+ attachments: external_exports.array(external_exports.object({}).passthrough()).optional(),
49502
+ bodyOrder: external_exports.boolean().optional(),
49503
+ truncated: external_exports.boolean().optional()
49504
+ },
49505
+ annotations: { readOnlyHint: true }
49506
+ },
49507
+ withErrorHandling(({ id: id2, includeSegments, maxSegments }) => {
49508
+ let result;
49509
+ try {
49510
+ result = notesManager.getAudioTranscripts(id2, { includeSegments, maxSegments });
49511
+ } catch (error2) {
49512
+ if (error2 instanceof AudioTranscriptError) return errorResponse(error2.message);
49513
+ throw error2;
49514
+ }
49515
+ const toResponse = (r) => successResponse(formatTranscriptsText(r), { ...r });
49516
+ const fitted = fitTranscriptsToBudget(
49517
+ result,
49518
+ exportMaxResponseBytes(),
49519
+ (r) => Buffer.byteLength(JSON.stringify(toResponse(r)))
49520
+ );
49521
+ return toResponse(fitted);
49522
+ }, "Error reading audio transcripts")
49523
+ );
49101
49524
  registerTool(
49102
49525
  "get-note-metadata",
49103
49526
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-notes-mcp",
3
- "version": "2.8.29",
3
+ "version": "2.8.30",
4
4
  "description": "MCP server for Apple Notes - create, search, update, and manage notes via Claude and other AI assistants",
5
5
  "type": "module",
6
6
  "main": "build/index.js",