apple-notes-mcp 2.8.5 → 2.8.6

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 +20 -0
  2. package/build/index.js +231 -40
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -303,6 +303,26 @@ attachment-bearing notes; edit them in Notes.app.
303
303
 
304
304
  ---
305
305
 
306
+ #### `get-native-objects`
307
+
308
+ Reads native object identities and ranges, checklist item IDs and state, actual
309
+ native tags, and native table data from one exact note ID. Table output includes
310
+ stable row and column identifiers. `tableCellsComplete` is false when Notes
311
+ metadata cannot be decoded completely. This tool is read-only and requires Full
312
+ Disk Access.
313
+
314
+ ---
315
+
316
+ #### `list-native-tags`
317
+
318
+ Lists actual native Notes tags used within one explicit `account` and `folder`,
319
+ mapping each tag to its matching note IDs. This differs from textual hashtag
320
+ search. The response reports `complete: false` and per-note errors when some
321
+ native metadata is unavailable. This tool is read-only and requires Full Disk
322
+ Access.
323
+
324
+ ---
325
+
306
326
  #### `get-note-plaintext`
307
327
 
308
328
  Retrieves a note's body as plain text, with no HTML markup.
package/build/index.js CHANGED
@@ -3358,9 +3358,9 @@ var require_utils = __commonJS({
3358
3358
  let output = "";
3359
3359
  for (let i = 0; i < input.length; i++) {
3360
3360
  if (input[i] === "%" && i + 2 < input.length) {
3361
- const hex = input.slice(i + 1, i + 3);
3362
- if (isHexPair(hex)) {
3363
- const normalizedHex = hex.toUpperCase();
3361
+ const hex2 = input.slice(i + 1, i + 3);
3362
+ if (isHexPair(hex2)) {
3363
+ const normalizedHex = hex2.toUpperCase();
3364
3364
  const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3365
3365
  if (decodeUnreserved && isUnreserved(decoded)) {
3366
3366
  output += decoded;
@@ -3380,9 +3380,9 @@ var require_utils = __commonJS({
3380
3380
  for (let i = 0; i < input.length; i++) {
3381
3381
  const ch = input[i];
3382
3382
  if (ch === "%" && i + 2 < input.length) {
3383
- const hex = input.slice(i + 1, i + 3);
3384
- if (isHexPair(hex)) {
3385
- const normalizedHex = hex.toUpperCase();
3383
+ const hex2 = input.slice(i + 1, i + 3);
3384
+ if (isHexPair(hex2)) {
3385
+ const normalizedHex = hex2.toUpperCase();
3386
3386
  const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3387
3387
  if (decoded !== "." && isUnreserved(decoded)) {
3388
3388
  output += decoded;
@@ -3422,9 +3422,9 @@ var require_utils = __commonJS({
3422
3422
  for (let i = 0; i < input.length; i++) {
3423
3423
  const ch = input[i];
3424
3424
  if (ch === "%" && i + 2 < input.length) {
3425
- const hex = input.slice(i + 1, i + 3);
3426
- if (isHexPair(hex)) {
3427
- output += "%" + hex.toUpperCase();
3425
+ const hex2 = input.slice(i + 1, i + 3);
3426
+ if (isHexPair(hex2)) {
3427
+ output += "%" + hex2.toUpperCase();
3428
3428
  i += 2;
3429
3429
  continue;
3430
3430
  }
@@ -3460,9 +3460,9 @@ var require_utils = __commonJS({
3460
3460
  for (let i = 0; i < input.length; i++) {
3461
3461
  const ch = input[i];
3462
3462
  if (ch === "%" && i + 2 < input.length) {
3463
- const hex = input.slice(i + 1, i + 3);
3464
- if (isHexPair(hex)) {
3465
- output += "%" + hex.toUpperCase();
3463
+ const hex2 = input.slice(i + 1, i + 3);
3464
+ if (isHexPair(hex2)) {
3465
+ output += "%" + hex2.toUpperCase();
3466
3466
  i += 2;
3467
3467
  continue;
3468
3468
  }
@@ -3507,9 +3507,9 @@ var require_utils = __commonJS({
3507
3507
  for (let i = 0; i < input.length; i++) {
3508
3508
  const ch = input[i];
3509
3509
  if (ch === "%" && i + 2 < input.length) {
3510
- const hex = input.slice(i + 1, i + 3);
3511
- if (isHexPair(hex)) {
3512
- const normalizedHex = hex.toUpperCase();
3510
+ const hex2 = input.slice(i + 1, i + 3);
3511
+ if (isHexPair(hex2)) {
3512
+ const normalizedHex = hex2.toUpperCase();
3513
3513
  const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3514
3514
  if (isUnreserved(decoded)) {
3515
3515
  output += decoded;
@@ -3547,9 +3547,9 @@ var require_utils = __commonJS({
3547
3547
  let output = "";
3548
3548
  for (let i = 0; i < input.length; i++) {
3549
3549
  if (input[i] === "%" && i + 2 < input.length) {
3550
- const hex = input.slice(i + 1, i + 3);
3551
- if (isHexPair(hex)) {
3552
- output += "%" + hex.toUpperCase();
3550
+ const hex2 = input.slice(i + 1, i + 3);
3551
+ if (isHexPair(hex2)) {
3552
+ output += "%" + hex2.toUpperCase();
3553
3553
  i += 2;
3554
3554
  continue;
3555
3555
  }
@@ -9329,8 +9329,8 @@ var require_select = __commonJS({
9329
9329
  regex = regex.replace(name, val.source || val);
9330
9330
  return new RegExp(regex);
9331
9331
  };
9332
- var truncateUrl = function(url, num) {
9333
- return url.replace(/^(?:\w+:\/\/|\/+)/, "").replace(/(?:\/+|\/*#.*?)$/, "").split("/", num).join("/");
9332
+ var truncateUrl = function(url, num2) {
9333
+ return url.replace(/^(?:\w+:\/\/|\/+)/, "").replace(/(?:\/+|\/*#.*?)$/, "").split("/", num2).join("/");
9334
9334
  };
9335
9335
  var parseNth = function(param_, test) {
9336
9336
  var param = param_.replace(/\s+/g, ""), cap;
@@ -24777,8 +24777,8 @@ var ZodError = class _ZodError extends Error {
24777
24777
  constructor(issues) {
24778
24778
  super();
24779
24779
  this.issues = [];
24780
- this.addIssue = (sub) => {
24781
- this.issues = [...this.issues, sub];
24780
+ this.addIssue = (sub2) => {
24781
+ this.issues = [...this.issues, sub2];
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 sub of this.issues) {
24849
- if (sub.path.length > 0) {
24850
- const firstEl = sub.path[0];
24848
+ for (const sub2 of this.issues) {
24849
+ if (sub2.path.length > 0) {
24850
+ const firstEl = sub2.path[0];
24851
24851
  fieldErrors[firstEl] = fieldErrors[firstEl] || [];
24852
- fieldErrors[firstEl].push(mapper(sub));
24852
+ fieldErrors[firstEl].push(mapper(sub2));
24853
24853
  } else {
24854
- formErrors.push(mapper(sub));
24854
+ formErrors.push(mapper(sub2));
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 sub of error2.issues) {
29154
- if (sub.path.length > 0) {
29155
- fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];
29156
- fieldErrors[sub.path[0]].push(mapper(sub));
29153
+ for (const sub2 of error2.issues) {
29154
+ if (sub2.path.length > 0) {
29155
+ fieldErrors[sub2.path[0]] = fieldErrors[sub2.path[0]] || [];
29156
+ fieldErrors[sub2.path[0]].push(mapper(sub2));
29157
29157
  } else {
29158
- formErrors.push(mapper(sub));
29158
+ formErrors.push(mapper(sub2));
29159
29159
  }
29160
29160
  }
29161
29161
  return { formErrors, fieldErrors };
@@ -39364,9 +39364,9 @@ function queryNoteData(noteId) {
39364
39364
  timeout: 5e3,
39365
39365
  stdio: ["pipe", "pipe", "pipe"]
39366
39366
  });
39367
- const hex = result.trim();
39368
- if (!hex) return { hex: null };
39369
- return { hex };
39367
+ const hex2 = result.trim();
39368
+ if (!hex2) return { hex: null };
39369
+ return { hex: hex2 };
39370
39370
  } catch (error2) {
39371
39371
  const message = error2 instanceof Error ? error2.message : String(error2);
39372
39372
  console.error(`Failed to query NoteStore database: ${message}`);
@@ -39376,10 +39376,10 @@ function queryNoteData(noteId) {
39376
39376
  return { hex: null };
39377
39377
  }
39378
39378
  }
39379
- function hexToBytes(hex) {
39380
- const bytes = new Uint8Array(hex.length / 2);
39381
- for (let i = 0; i < hex.length; i += 2) {
39382
- bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
39379
+ function hexToBytes(hex2) {
39380
+ const bytes = new Uint8Array(hex2.length / 2);
39381
+ for (let i = 0; i < hex2.length; i += 2) {
39382
+ bytes[i / 2] = parseInt(hex2.substring(i, i + 2), 16);
39383
39383
  }
39384
39384
  return bytes;
39385
39385
  }
@@ -42511,7 +42511,7 @@ function decodeHtmlEntities(text) {
42511
42511
  }
42512
42512
  return String.fromCodePoint(codePoint);
42513
42513
  };
42514
- return text.replace(/&#x([0-9a-f]+);?/gi, (match, hex) => decodeCodePoint(match, hex, 16)).replace(/&#([0-9]+);?/g, (match, decimal) => decodeCodePoint(match, decimal, 10)).replace(/&nbsp(?:;|(?![0-9a-z]))/gi, " ").replace(/&quot(?:;|(?![0-9a-z]))/gi, '"').replace(/&apos(?:;|(?![0-9a-z]))/gi, "'").replace(/&lt(?:;|(?![0-9a-z]))/gi, "<").replace(/&gt(?:;|(?![0-9a-z]))/gi, ">").replace(/&amp(?:;|(?![0-9a-z]))/gi, "&");
42514
+ return text.replace(/&#x([0-9a-f]+);?/gi, (match, hex2) => decodeCodePoint(match, hex2, 16)).replace(/&#([0-9]+);?/g, (match, decimal) => decodeCodePoint(match, decimal, 10)).replace(/&nbsp(?:;|(?![0-9a-z]))/gi, " ").replace(/&quot(?:;|(?![0-9a-z]))/gi, '"').replace(/&apos(?:;|(?![0-9a-z]))/gi, "'").replace(/&lt(?:;|(?![0-9a-z]))/gi, "<").replace(/&gt(?:;|(?![0-9a-z]))/gi, ">").replace(/&amp(?:;|(?![0-9a-z]))/gi, "&");
42515
42515
  }
42516
42516
  function firstVisibleHtmlLine(html) {
42517
42517
  let text = html;
@@ -42852,6 +42852,103 @@ function comparableVisibleText(html) {
42852
42852
  ).replace(/\s+/g, " ").trim();
42853
42853
  }
42854
42854
 
42855
+ // src/utils/noteTables.ts
42856
+ import { gunzipSync as gunzipSync3 } from "node:zlib";
42857
+ var sub = (f, n) => {
42858
+ const value = embeddedMessage(getField(f, n));
42859
+ if (!value) throw new Error(`Missing table field ${n}`);
42860
+ return value;
42861
+ };
42862
+ var num = (f, n) => {
42863
+ const v = varintValue(getField(f, n));
42864
+ if (v === void 0) throw new Error(`Missing table index ${n}`);
42865
+ return v;
42866
+ };
42867
+ var many = (f, n) => getFields(f, n).map((v) => {
42868
+ const m = embeddedMessage(v);
42869
+ if (!m) throw new Error("Invalid table entry");
42870
+ return m;
42871
+ });
42872
+ var hex = (f) => {
42873
+ if (!(f?.value instanceof Uint8Array)) throw new Error("Missing table UUID");
42874
+ return Buffer.from(f.value).toString("hex");
42875
+ };
42876
+ function parseNoteTable(compressed) {
42877
+ const root = decodeMessage(gunzipSync3(compressed, { maxOutputLength: 16 * 1024 * 1024 }));
42878
+ const data = sub(sub(root, 2), 3), entries = many(data, 3);
42879
+ if (entries.length > 1e5) throw new Error("Table too large");
42880
+ const keys = getFields(data, 4).map(stringValue), types = getFields(data, 5).map(stringValue), uuids = getFields(data, 6).map(hex);
42881
+ const entry = (index) => {
42882
+ if (!entries[index]) throw new Error("Invalid table reference");
42883
+ return entries[index];
42884
+ };
42885
+ const uuidIndex = (index) => num(sub(many(sub(entry(index), 13), 3)[0], 2), 2);
42886
+ const roots = entries.filter((e) => {
42887
+ const map = embeddedMessage(getField(e, 13));
42888
+ return map && types[num(map, 1)] === "com.apple.notes.ICTable";
42889
+ });
42890
+ if (roots.length !== 1) throw new Error("Ambiguous native table root");
42891
+ const refs = new Map(
42892
+ 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)])
42893
+ );
42894
+ const ordered = (key) => {
42895
+ const ref = refs.get(key);
42896
+ if (ref === void 0) throw new Error("Missing table dimension");
42897
+ const ordering = sub(sub(entry(ref), 16), 1), array2 = sub(ordering, 1);
42898
+ const ids = many(array2, 2).map((a) => hex(getField(a, 2)));
42899
+ const map = /* @__PURE__ */ new Map();
42900
+ ids.forEach((id, i) => {
42901
+ const index = uuids.indexOf(id);
42902
+ if (index < 0) throw new Error("Missing dimension UUID");
42903
+ map.set(index, i);
42904
+ });
42905
+ const aliases = many(sub(ordering, 2), 1).map((pair) => [
42906
+ uuidIndex(num(sub(pair, 1), 6)),
42907
+ uuidIndex(num(sub(pair, 2), 6))
42908
+ ]);
42909
+ for (let pass = 0; pass < aliases.length + 1; pass++) {
42910
+ let changed = false;
42911
+ for (const [key2, value] of aliases)
42912
+ if (map.has(key2) && !map.has(value)) {
42913
+ map.set(value, map.get(key2));
42914
+ changed = true;
42915
+ }
42916
+ if (!changed) break;
42917
+ }
42918
+ return { ids, map };
42919
+ };
42920
+ const rows = ordered("crRows"), columns = ordered("crColumns");
42921
+ if (!rows.ids.length || !columns.ids.length || rows.ids.length * columns.ids.length > 1e5)
42922
+ throw new Error("Unsupported table size");
42923
+ const values = rows.ids.map(() => columns.ids.map(() => ""));
42924
+ const cellRef = refs.get("cellColumns");
42925
+ if (cellRef === void 0) throw new Error("Missing table cells");
42926
+ for (const column of many(sub(entry(cellRef), 6), 1)) {
42927
+ const ci = columns.map.get(uuidIndex(num(sub(column, 1), 6)));
42928
+ const cells = entry(num(sub(column, 2), 6));
42929
+ for (const row of many(sub(cells, 6), 1)) {
42930
+ const ri = rows.map.get(uuidIndex(num(sub(row, 1), 6)));
42931
+ if (ri === void 0 || ci === void 0) continue;
42932
+ const note = sub(entry(num(sub(row, 2), 6)), 10);
42933
+ const text = stringValue(getField(note, 2));
42934
+ if (text === void 0 || text.includes("\uFFFC"))
42935
+ throw new Error("Embedded or unsupported table cell");
42936
+ values[ri][ci] = text.replace(/\n$/u, "");
42937
+ }
42938
+ }
42939
+ const rtl = entries.some((e) => {
42940
+ const map = embeddedMessage(getField(e, 13));
42941
+ return map && many(map, 3).some(
42942
+ (m) => stringValue(getField(sub(m, 2), 4)) === "CRTableColumnDirectionRightToLeft"
42943
+ );
42944
+ });
42945
+ if (rtl) {
42946
+ for (const row of values) row.reverse();
42947
+ columns.ids.reverse();
42948
+ }
42949
+ return { rows: values, rowIds: rows.ids, columnIds: columns.ids };
42950
+ }
42951
+
42855
42952
  // src/index.ts
42856
42953
  loadFileConfig();
42857
42954
  var require2 = createRequire(import.meta.url);
@@ -43406,6 +43503,100 @@ registerTool(
43406
43503
  return successResponse(`Shown account with ID "${id}" in Notes.app`, { id, separately });
43407
43504
  }, "Error showing account")
43408
43505
  );
43506
+ registerTool(
43507
+ "get-native-objects",
43508
+ {
43509
+ description: "Use when: inspecting native objects, checklist identities, or tables in one exact note.\nReturns: native object IDs and ranges, checklist IDs and state, actual native tags, decoded tables, and the current rich content hash.\nDo not use when: you only need the note body (get-note-content) or AppleScript attachment metadata (list-attachments).\nSafety: read-only; requires Full Disk Access and reports incomplete table metadata instead of guessing.",
43510
+ inputSchema: { id: noteIdInput },
43511
+ outputSchema: {
43512
+ id: external_exports.string().optional(),
43513
+ contentHash: external_exports.string().optional(),
43514
+ objects: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
43515
+ checklistItems: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
43516
+ nativeTags: external_exports.array(external_exports.string()).optional(),
43517
+ tables: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
43518
+ tableCellsComplete: external_exports.boolean().optional()
43519
+ },
43520
+ annotations: { readOnlyHint: true }
43521
+ },
43522
+ withErrorHandling(({ id }) => {
43523
+ const note = notesManager.getNoteById(id);
43524
+ if (!note) return errorResponse(`Note with ID "${id}" not found`);
43525
+ const body = notesManager.getNoteContentById(id);
43526
+ if (!body) return errorResponse(`Failed to read content of note "${note.title}"`);
43527
+ const rich = readRichNote(id);
43528
+ const tables = (rich.objectData || []).filter((object3) => object3.type?.includes("table")).map((object3) => {
43529
+ try {
43530
+ return {
43531
+ id: object3.id,
43532
+ attachmentId: id.replace(/ICNote\/p\d+$/, `ICAttachment/p${object3.pk}`),
43533
+ complete: true,
43534
+ ...parseNoteTable(Buffer.from(object3.mergeable, "hex"))
43535
+ };
43536
+ } catch (error2) {
43537
+ return { id: object3.id, complete: false, reason: String(error2) };
43538
+ }
43539
+ });
43540
+ for (const object3 of rich.objects || []) {
43541
+ if (object3.type.includes("table") && !tables.some((table) => table.id === object3.id)) {
43542
+ tables.push({
43543
+ id: object3.id,
43544
+ complete: false,
43545
+ reason: "Native table metadata is unavailable"
43546
+ });
43547
+ }
43548
+ }
43549
+ const richRead = {
43550
+ content: body,
43551
+ links: rich.links,
43552
+ nativeTags: rich.nativeTags,
43553
+ complete: true,
43554
+ writable: !rich.hasNativeObjects && !rich.hasChecklist,
43555
+ revision: rich.revision
43556
+ };
43557
+ return successResponse("Native objects read from the exact note", {
43558
+ id,
43559
+ contentHash: richContentHash(body, richRead),
43560
+ objects: rich.objects,
43561
+ checklistItems: rich.checklistItems,
43562
+ nativeTags: rich.nativeTags,
43563
+ tables,
43564
+ tableCellsComplete: tables.every((table) => table.complete)
43565
+ });
43566
+ }, "Error reading native objects")
43567
+ );
43568
+ registerTool(
43569
+ "list-native-tags",
43570
+ {
43571
+ description: "Use when: listing actual native Notes tags used in one explicit account and folder.\nReturns: each native tag mapped to exact matching note IDs, plus completeness and per-note errors.\nDo not use when: searching textual #hashtags in note bodies (search-notes).\nSafety: read-only; requires Full Disk Access and discloses partial reads.",
43572
+ inputSchema: {
43573
+ account: external_exports.string().min(1).max(MAX.ACCOUNT),
43574
+ folder: external_exports.string().min(1).max(MAX.FOLDER)
43575
+ },
43576
+ outputSchema: {
43577
+ tags: external_exports.record(external_exports.array(external_exports.string())).optional(),
43578
+ complete: external_exports.boolean().optional(),
43579
+ errors: external_exports.record(external_exports.string()).optional()
43580
+ },
43581
+ annotations: { readOnlyHint: true }
43582
+ },
43583
+ withErrorHandling(({ account, folder }) => {
43584
+ const tags = {};
43585
+ const errors = {};
43586
+ for (const note of notesManager.listNoteRefs(account, folder)) {
43587
+ try {
43588
+ for (const tag of readRichNote(note.id).nativeTags) (tags[tag] ||= []).push(note.id);
43589
+ } catch {
43590
+ errors[note.id] = "Native metadata unavailable";
43591
+ }
43592
+ }
43593
+ return successResponse("Native tags read from the requested folder", {
43594
+ tags,
43595
+ complete: Object.keys(errors).length === 0,
43596
+ errors
43597
+ });
43598
+ }, "Error listing native tags")
43599
+ );
43409
43600
  registerTool(
43410
43601
  "update-note",
43411
43602
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-notes-mcp",
3
- "version": "2.8.5",
3
+ "version": "2.8.6",
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",