apple-notes-mcp 2.8.32 → 2.8.33

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 -4
  2. package/build/index.js +181 -22
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -332,13 +332,42 @@ title line, with no seed line.
332
332
  It also refuses Markdown that Notes would rewrite and the server could not
333
333
  verify: `_` emphasis (underscores inside a word, as in `snake_case`, and
334
334
  inside a link destination, as in `[docs](https://example.com/_next/static)`,
335
- are fine), backslash escapes, character references such as `&`, `---` or
336
- `===` lines, indented headings or list items, `1)` lists, closing `#`s, and
335
+ are fine), backslash escapes, character references such as `&`, `===` lines
336
+ and rule lines other than the `---` divider described below, indented headings or list items, `1)` lists, closing `#`s, and
337
337
  formatting inside link labels. Content that needs one of these literally, such
338
338
  as a `/_next` path or a literal `\*`, has no Markdown form here: use
339
339
  `format: "html"` for that note (or `append-native` with `format: "html"` on an
340
340
  existing one), which keeps the characters but not the Heading and Subheading
341
341
  styles. Markdown punctuation in `title` is escaped, so the title stays literal.
342
+ - Notes' importer also maps these block constructs to native styles. They are
343
+ gated separately, and `get-capabilities` reports them as
344
+ `create-note-markdown-blocks`. Each mapping below was live-verified on
345
+ macOS 27.2 by reading the created note's stored styles.
346
+
347
+ | Markdown | Native result |
348
+ |----------|---------------|
349
+ | `- [ ] item` / `- [x] item` | Checklist item, unchecked / checked |
350
+ | `> text` (consecutive lines form one quote) | Body paragraph with a block quote |
351
+ | A fence of bare ```` ``` ```` lines (no language) | Monospaced paragraphs; the code text is kept literally |
352
+ | `---` on its own line, after a blank line | Divider line |
353
+ | `` `inline code` `` | **Highlighted** text, not monospace |
354
+
355
+ Constructs Notes would not render faithfully, or that this server cannot yet
356
+ verify, are refused before anything is created: `~~~` fences, a language
357
+ after the opening fence, nested (`>>`) or indented quotes, lists or headings
358
+ inside a quote, a quote followed directly by text (Markdown would join that
359
+ text to the quote), `---` directly under text (Markdown would make that text
360
+ a heading), `***`/`___`/`----` rules, `[X]`, `* [ ]` or `+ [ ]` items, checklist
361
+ items directly next to ordinary list items, inline code padded with spaces or
362
+ inside a link label, tables, and `~~strikethrough~~`. The readback checks the
363
+ block-quote text, the Monospaced text, each checklist item's text and done
364
+ state, the divider count, and the highlighted text. These mappings apply to
365
+ the default Shortcut route only; see
366
+ [Markdown through HTML](#markdown-through-html) for `markdownRoute: "html"`.
367
+ - [`append-native`](#append-native)'s `format: "markdown"` refuses all of the
368
+ constructs above: its Shortcuts converter is a different one, which renders
369
+ a quote and a fenced block as plain body text, `- [ ]` as a bullet with
370
+ literal brackets, and inline code as plain text, and drops `---`.
342
371
  - The server finds the new note among the notes added to the default folder
343
372
  during the run by verifying each one's visible text, heading levels and links
344
373
  by exact-ID readback, and moves it only after exactly one verifies. On any
@@ -366,7 +395,9 @@ On this route, bullet task items (`- [ ] item`, `- [x] item`) become ordinary
366
395
  list rows that start with a visible `☐` or `☑` character, and the response
367
396
  reports how many as `taskItemsRendered`. They are text, not native checkable
368
397
  checklist items. Use this route as the glyph fallback when the Shortcut is not
369
- installed or the note is outside iCloud.
398
+ installed or the note is outside iCloud. Block quotes, fenced code and inline code, which
399
+ the Shortcut route maps natively, are refused on this route, and a `---` line
400
+ stays literal text rather than becoming a divider.
370
401
 
371
402
  ```json
372
403
  {
@@ -1870,7 +1901,7 @@ Apple Notes stores checklists as a paragraph style (`style_type=103`) inside a g
1870
1901
  **Workarounds:**
1871
1902
 
1872
1903
  1. **Create the note with bulleted list items, then convert manually in Notes.app.** Select the items and press <kbd>⇧⌘L</kbd> (or **Format → Checklist**). This converts the list in place and the resulting checklist will be readable by `get-checklist-state` and annotated by `get-note-markdown`.
1873
- 2. **Use the Apple Shortcuts app** to script the checklist creation, since Shortcuts can manipulate Notes content at a higher level than AppleScript.
1904
+ 2. **Use the Apple Shortcuts app** to script the checklist creation, since Shortcuts can manipulate Notes content at a higher level than AppleScript. This server does that for you in two ways: [`create-checklist-item`](#create-checklist-item) appends one unchecked item to an existing note, and [`create-note`](#create-note) with `format: "markdown"` turns `- [ ]` / `- [x]` lines into checklist items with that done state through Notes' own Markdown importer, (see [Markdown notes](#markdown-notes)).
1874
1905
  3. **Read-only checklist support is fully implemented** — once a checklist exists (created manually or by another app), `get-checklist-state` and `get-note-markdown` will read its done/undone state correctly (with Full Disk Access).
1875
1906
 
1876
1907
  If you need to *track* todos programmatically and don't strictly need them rendered as Apple Notes checklist UI, plain markdown-style `- [ ] item` / `- [x] item` lines in a `plaintext` note are a reasonable alternative — they are searchable, human-readable, and can be parsed by downstream tooling.
package/build/index.js CHANGED
@@ -39675,7 +39675,11 @@ function parseRichNote(data, nativeTags = []) {
39675
39675
  const length = varintValue(getField(fields, 1));
39676
39676
  if (length === void 0 || length < 0 || position + length > text.length)
39677
39677
  throw new Error("Invalid Notes run length");
39678
+ const paragraph = embeddedMessage(getField(fields, 2));
39678
39679
  styleRuns.push({
39680
+ paragraphStyle: paragraph ? varintValue(getField(paragraph, 1)) ?? 3 : 3,
39681
+ blockQuote: Boolean(paragraph && varintValue(getField(paragraph, 8))),
39682
+ highlight: Boolean(varintValue(getField(fields, 14))),
39679
39683
  start: position,
39680
39684
  length,
39681
39685
  signature: JSON.stringify(
@@ -39703,7 +39707,6 @@ function parseRichNote(data, nativeTags = []) {
39703
39707
  start: position,
39704
39708
  length
39705
39709
  });
39706
- const paragraph = embeddedMessage(getField(fields, 2));
39707
39710
  hasChecklist ||= Boolean(paragraph && varintValue(getField(paragraph, 1)) === 103);
39708
39711
  if (paragraph && varintValue(getField(paragraph, 1)) === 103) {
39709
39712
  const checklist = embeddedMessage(getField(paragraph, 5));
@@ -45056,40 +45059,152 @@ function countTaskItems(markdown) {
45056
45059
  return item !== null && TASK_ITEM.test(item[1]);
45057
45060
  }).length;
45058
45061
  }
45059
- function appendMarkdownHtml(markdown, options = {}) {
45060
- if (markdown.includes("\uE000") || markdown.includes("\uE001"))
45062
+ var FENCE = "```";
45063
+ var CHECKLIST_ITEM = /^- \[( |x)\] (.+)$/;
45064
+ function withoutMarkdownCode(markdown) {
45065
+ let inCode = false;
45066
+ return markdown.replace(/\r\n/g, "\n").split("\n").map((line) => {
45067
+ if (line === FENCE) {
45068
+ inCode = !inCode;
45069
+ return "";
45070
+ }
45071
+ return inCode ? "" : line.replace(/`[^`\n]+`/g, "x");
45072
+ }).join("\n");
45073
+ }
45074
+ function usesMarkdownBlocks(markdown) {
45075
+ return /`/.test(markdown) || /^>/m.test(markdown) || /^---[ \t]*$/m.test(markdown) || /^- \[[ x]\] /m.test(markdown);
45076
+ }
45077
+ function renderMarkdown(markdown, options = {}) {
45078
+ const blocks = options.blocks === true;
45079
+ if (blocks ? /[-]/u.test(markdown) : /[]/u.test(markdown))
45061
45080
  throw new Error("Unsupported reserved characters");
45062
45081
  if (Array.from(markdown).some((c) => c.charCodeAt(0) < 32 && !["\n", "\r", " "].includes(c)))
45063
45082
  throw new Error("Unsupported control characters");
45064
- if (/[`~|]|^#{4,}\s|^\s*>|^\s*\[.+\]:|!\[|<\/?[a-z]/im.test(markdown))
45083
+ const checked = blocks ? withoutMarkdownCode(markdown) : markdown;
45084
+ if (blocks ? /[`~|]|^#{4,}\s|^[ \t]+>|^>[ \t]*>|^\s*\[.+\]:|!\[|<\/?[a-z]/im.test(checked) : /[`~|]|^#{4,}\s|^\s*>|^\s*\[.+\]:|!\[|<\/?[a-z]/im.test(checked))
45065
45085
  throw new Error(
45066
- "Markdown append supports paragraphs, headings, flat lists, emphasis and inline links; use semantic HTML for other formatting"
45086
+ blocks ? "Markdown import supports paragraphs, headings, flat lists, checklist items, block quotes, fenced code, dividers, emphasis, inline code and inline links; use semantic HTML for other formatting" : "Markdown append supports paragraphs, headings, flat lists, emphasis and inline links; use semantic HTML for other formatting"
45067
45087
  );
45088
+ const expect = {
45089
+ quotes: [],
45090
+ code: [],
45091
+ checklist: [],
45092
+ dividers: 0,
45093
+ highlights: []
45094
+ };
45068
45095
  const inline = (text) => {
45096
+ const spans = [];
45097
+ let value = text;
45098
+ if (blocks)
45099
+ value = value.replace(/`([^`\n]+)`/g, (_s, code2) => {
45100
+ if (/^\s|\s$/.test(code2)) throw new Error("Inline code cannot start or end with a space");
45101
+ expect.highlights.push(code2);
45102
+ spans.push(`<code>${escape2(code2)}</code>`);
45103
+ return `\uE002${spans.length - 1}\uE003`;
45104
+ });
45069
45105
  const links = [];
45070
- let value = text.replace(/\[([^\]\n]+)\]\(([^()\s]+)\)/g, (_s, label, url) => {
45106
+ value = value.replace(/\[([^\]\n]+)\]\(([^()\s]+)\)/g, (_s, label, url) => {
45071
45107
  if (!/^(https?:\/\/|notes:\/\/|applenotes:|mailto:)/i.test(url))
45072
45108
  throw new Error("Unsupported Markdown link URL");
45109
+ if (/[]/u.test(label)) throw new Error("Inline code inside a link is unsupported");
45073
45110
  links.push(`<a href="${escape2(url)}">${escape2(label)}</a>`);
45074
45111
  return `\uE000${links.length - 1}\uE001`;
45075
45112
  });
45076
45113
  value = escape2(value).replace(/\*\*([^*\n]+)\*\*/g, "<b>$1</b>").replace(/\*([^*\n]+)\*/g, "<i>$1</i>");
45077
45114
  if (value.includes("[") || value.includes("]") || value.includes("*"))
45078
45115
  throw new Error("Unsupported or unbalanced Markdown inline syntax");
45079
- return value.replace(/\uE000(\d+)\uE001/g, (_s, i) => links[Number(i)]);
45116
+ return value.replace(/(\d+)/g, (_s, i) => links[Number(i)]).replace(/(\d+)/g, (_s, i) => spans[Number(i)]);
45117
+ };
45118
+ const withoutTags = (html2) => {
45119
+ let text = "", inTag = false;
45120
+ for (const ch of html2) {
45121
+ if (ch === "<") inTag = true;
45122
+ else if (ch === ">" && inTag) inTag = false;
45123
+ else if (!inTag) text += ch;
45124
+ }
45125
+ return text;
45080
45126
  };
45081
- let html = "", list;
45127
+ const visible = (html2) => withoutTags(html2).replace(/&quot;/g, '"').replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
45128
+ let html = "", list, quote = false, code, previousBlank = true;
45082
45129
  const close = () => {
45083
45130
  if (list) {
45084
- html += `</${list}>`;
45131
+ html += list === "checklist" ? "</ul>" : `</${list}>`;
45085
45132
  list = void 0;
45086
45133
  }
45134
+ if (quote) {
45135
+ html += "</blockquote>";
45136
+ quote = false;
45137
+ }
45087
45138
  };
45088
45139
  for (const line of markdown.replace(/\r\n/g, "\n").split("\n")) {
45140
+ if (code) {
45141
+ if (line === FENCE) {
45142
+ if (!code.length) throw new Error("Empty fenced code blocks are unsupported");
45143
+ html += `<pre>${escape2(code.join("\n"))}</pre>`;
45144
+ expect.code.push(code.join("\n"));
45145
+ code = void 0;
45146
+ previousBlank = false;
45147
+ } else code.push(line);
45148
+ continue;
45149
+ }
45150
+ const blank = !line.trim();
45151
+ if (blocks) {
45152
+ if (quote && !blank && !line.startsWith(">"))
45153
+ throw new Error("End a block quote with a blank line before other text");
45154
+ if (line.startsWith(FENCE)) {
45155
+ if (line !== FENCE)
45156
+ throw new Error("Fenced code must open and close with a bare ``` line (no language)");
45157
+ close();
45158
+ code = [];
45159
+ continue;
45160
+ }
45161
+ if (line.startsWith(">")) {
45162
+ const text = line.replace(/^> ?/, "");
45163
+ if (!text.trim()) throw new Error("Empty block quote lines are unsupported");
45164
+ if (/^(?:#|[-+*][ \t]|\d+[.)][ \t]|>|```)/.test(text))
45165
+ throw new Error("Block quotes support text and inline formatting only");
45166
+ if (!quote) {
45167
+ close();
45168
+ html += "<blockquote>";
45169
+ quote = true;
45170
+ expect.quotes.push("");
45171
+ }
45172
+ const rendered = inline(text);
45173
+ html += `<div>${rendered}</div>`;
45174
+ expect.quotes[expect.quotes.length - 1] += ` ${visible(rendered)}`;
45175
+ previousBlank = false;
45176
+ continue;
45177
+ }
45178
+ if (/^---[ \t]*$/.test(line)) {
45179
+ if (!previousBlank) throw new Error("Put a blank line before a --- divider");
45180
+ close();
45181
+ html += "<hr>";
45182
+ expect.dividers++;
45183
+ previousBlank = false;
45184
+ continue;
45185
+ }
45186
+ const task = CHECKLIST_ITEM.exec(line);
45187
+ if (task) {
45188
+ if (list && list !== "checklist")
45189
+ throw new Error("Separate checklist items from other list items with a blank line");
45190
+ if (list !== "checklist") {
45191
+ close();
45192
+ list = "checklist";
45193
+ html += '<ul class="checklist">';
45194
+ }
45195
+ const rendered = inline(task[2]);
45196
+ html += `<li>${rendered}</li>`;
45197
+ expect.checklist.push({ text: visible(rendered).trim(), done: task[1] === "x" });
45198
+ previousBlank = false;
45199
+ continue;
45200
+ }
45201
+ }
45089
45202
  if (/^\s{2,}\S/.test(line)) throw new Error("Nested lists and indented code are unsupported");
45090
45203
  const heading = /^(#{1,3})\s+(.+)$/.exec(line), item = /^(?:([-+*])|\d+\.)\s+(.+)$/.exec(line);
45091
45204
  if (item) {
45092
45205
  const kind = item[1] ? "ul" : "ol";
45206
+ if (list === "checklist")
45207
+ throw new Error("Separate checklist items from other list items with a blank line");
45093
45208
  if (list !== kind) {
45094
45209
  close();
45095
45210
  list = kind;
@@ -45097,13 +45212,20 @@ function appendMarkdownHtml(markdown, options = {}) {
45097
45212
  }
45098
45213
  const task = options.taskGlyphs && item[1] ? TASK_ITEM.exec(item[2]) : null;
45099
45214
  html += task ? `<li>${task[1] === " " ? TASK_GLYPHS.open : TASK_GLYPHS.done} ${inline(task[2])}</li>` : `<li>${inline(item[2])}</li>`;
45215
+ previousBlank = false;
45100
45216
  continue;
45101
45217
  }
45102
45218
  close();
45103
- html += heading ? `<h${heading[1].length}>${inline(heading[2])}</h${heading[1].length}>` : line.trim() ? `<div>${inline(line)}</div>` : "<div><br></div>";
45219
+ html += heading ? `<h${heading[1].length}>${inline(heading[2])}</h${heading[1].length}>` : !blank ? `<div>${inline(line)}</div>` : "<div><br></div>";
45220
+ previousBlank = blank;
45104
45221
  }
45222
+ if (code) throw new Error("Unclosed fenced code block");
45105
45223
  close();
45106
- return html;
45224
+ expect.quotes = expect.quotes.map((q) => q.trim());
45225
+ return { html, expect };
45226
+ }
45227
+ function appendMarkdownHtml(markdown, options = {}) {
45228
+ return renderMarkdown(markdown, { taskGlyphs: options.taskGlyphs }).html;
45107
45229
  }
45108
45230
 
45109
45231
  // src/utils/noteRevision.ts
@@ -45197,7 +45319,7 @@ var UNMODELED_MARKDOWN = [
45197
45319
  [/^#{1,3}[ \t].*[ \t]#+[ \t]*$/m, "closing # sequences"],
45198
45320
  [/\[[^\]\n]*[*_][^\]\n]*\]\(/m, "formatting inside link labels"]
45199
45321
  ];
45200
- function validateAppendContent(content, format) {
45322
+ function validateAppendContent(content, format, options = {}) {
45201
45323
  if (!content || content.length > 1024 * 1024 || content.includes("\0"))
45202
45324
  throw new Error("Invalid append content (limit 1 MiB)");
45203
45325
  if (format === "html") {
@@ -45227,11 +45349,12 @@ function validateAppendContent(content, format) {
45227
45349
  if (/<!--|<!|<\?|<[^>]*$/u.test(content)) throw new Error("Unsupported HTML markup");
45228
45350
  }
45229
45351
  if (format === "markdown") {
45230
- if (/!\[|<\/?[a-z]|\]\(\s*(?:javascript|data|file):/i.test(content))
45352
+ const checked = options.blocks ? withoutMarkdownCode(content).replace(/^---[ \t]*$/gm, "") : content;
45353
+ if (/!\[|<\/?[a-z]|\]\(\s*(?:javascript|data|file):/i.test(checked))
45231
45354
  throw new Error("Markdown images, raw HTML and local/executable links are unsupported");
45232
- const withoutLinkDestinations = content.replace(/(\[[^\]\n]+\])\([^()\s]+\)/g, "$1()");
45355
+ const withoutLinkDestinations = checked.replace(/(\[[^\]\n]+\])\([^()\s]+\)/g, "$1()");
45233
45356
  for (const [pattern, name] of UNMODELED_MARKDOWN)
45234
- if (pattern.test(name === UNDERSCORES_OUTSIDE_A_WORD ? withoutLinkDestinations : content))
45357
+ if (pattern.test(name === UNDERSCORES_OUTSIDE_A_WORD ? withoutLinkDestinations : checked))
45235
45358
  throw new Error(
45236
45359
  `Markdown cannot use ${name}; Notes would change that text, so the result could not be verified`
45237
45360
  );
@@ -45455,10 +45578,42 @@ var literalMarkdown = (text) => text.replace(/[!-/:-@[-`{-~]/g, "\\$&");
45455
45578
  function headingLevels(html) {
45456
45579
  return [...html.matchAll(/<h([1-3])\b[^>]*>([\s\S]*?)<\/h\1>/gi)].filter((match) => comparableVisibleText(match[2])).map((match) => Number(match[1]));
45457
45580
  }
45581
+ var DIVIDER_UTI = "com.apple.notes.inlinetextattachment.dividerline";
45582
+ function assertMarkdownBlocks(rich, expected) {
45583
+ const tidy = (s) => s.replace(/[\s]+/gu, " ").trim();
45584
+ const runs = rich.styleRuns || [];
45585
+ const styledText = (matches) => {
45586
+ let out = "", previous = false;
45587
+ for (const run of runs) {
45588
+ const hit = matches(run);
45589
+ if (hit) out += (previous ? "" : " ") + rich.text.slice(run.start, run.start + run.length);
45590
+ previous = hit;
45591
+ }
45592
+ return tidy(out);
45593
+ };
45594
+ if (styledText((run) => run.blockQuote === true) !== tidy(expected.quotes.join(" ")))
45595
+ throw new Error("Block quotes not verified");
45596
+ if (styledText((run) => run.paragraphStyle === 4) !== tidy(expected.code.join(" ")))
45597
+ throw new Error("Monospaced code blocks not verified");
45598
+ if (styledText((run) => run.highlight === true) !== tidy(expected.highlights.join(" ")))
45599
+ throw new Error("Inline code highlights not verified");
45600
+ const items = (rich.checklistItems || []).map((item) => ({
45601
+ text: tidy(item.text),
45602
+ done: item.done
45603
+ }));
45604
+ const wanted = expected.checklist.map((item) => ({ text: tidy(item.text), done: item.done }));
45605
+ if (JSON.stringify(items) !== JSON.stringify(wanted))
45606
+ throw new Error("Checklist items or their done state not verified");
45607
+ if ((rich.objects || []).filter((o) => o.type === DIVIDER_UTI).length !== expected.dividers)
45608
+ throw new Error("Dividers not verified");
45609
+ }
45458
45610
  function createMarkdownNote(manager, request, run = runBackgroundShortcut) {
45459
45611
  if (/[\r\n\0]/u.test(request.title)) throw new Error("A Markdown note title must be one line");
45460
- validateAppendContent(request.content, "markdown");
45461
- const bodyHtml = appendMarkdownHtml(request.content);
45612
+ validateAppendContent(request.content, "markdown", { blocks: true });
45613
+ const { html: bodyHtml, expect: blockExpectations } = renderMarkdown(request.content, {
45614
+ blocks: true
45615
+ });
45616
+ const checkBlocks = usesMarkdownBlocks(request.content);
45462
45617
  if (request.folder) buildFolderReference(request.folder);
45463
45618
  const expectedText = `${request.title} ${comparableVisibleText(bodyHtml)}`.replace(/\s+/gu, " ").trim();
45464
45619
  const expectedLevels = [1, ...headingLevels(bodyHtml)].join();
@@ -45511,6 +45666,7 @@ ${request.content}`
45511
45666
  if (headingLevels(note2.html).join() !== expectedLevels)
45512
45667
  throw new Error("Heading styles not verified");
45513
45668
  assertAppendedHtmlLinks(0, note2.rich.links, bodyHtml);
45669
+ if (checkBlocks) assertMarkdownBlocks(note2.rich, blockExpectations);
45514
45670
  return [{ id: noteId3, account: account2, note: note2 }];
45515
45671
  } catch (error2) {
45516
45672
  failures.push(`${noteId3}: ${reason(error2)}`);
@@ -47084,7 +47240,8 @@ var VERIFIED_BACKGROUND = /* @__PURE__ */ new Set([
47084
47240
  "set-note-pinned",
47085
47241
  "remove-native-tags",
47086
47242
  "replace-native-tag",
47087
- "create-note-markdown"
47243
+ "create-note-markdown",
47244
+ "create-note-markdown-blocks"
47088
47245
  ]);
47089
47246
  var LIVE_VALIDATION_BLOCKERS = {};
47090
47247
  var signingRefusal = "Installed Shortcuts refuses to sign this Notes action (unsupported features); no background fallback is enabled";
@@ -47235,7 +47392,8 @@ function registerNativeOperations(server2, manager) {
47235
47392
  "remove-native-tags",
47236
47393
  "replace-native-tag",
47237
47394
  "insert-note-link",
47238
- "create-note-markdown"
47395
+ "create-note-markdown",
47396
+ "create-note-markdown-blocks"
47239
47397
  ];
47240
47398
  let tagBridgeInstalled = false;
47241
47399
  try {
@@ -47247,7 +47405,7 @@ function registerNativeOperations(server2, manager) {
47247
47405
  markdownBridgeInstalled = markdownNoteStatus().installed;
47248
47406
  } catch {
47249
47407
  }
47250
- const installed = (name) => name === "create-note-markdown" ? markdownBridgeInstalled : bridge.installed;
47408
+ const installed = (name) => name.startsWith("create-note-markdown") ? markdownBridgeInstalled : bridge.installed;
47251
47409
  return {
47252
47410
  bridge,
47253
47411
  nativeTagBridgeInstalled: tagBridgeInstalled,
@@ -47704,7 +47862,7 @@ registerTool(
47704
47862
  inputSchema: {
47705
47863
  title: external_exports.string().min(1, "Title is required").max(MAX.TITLE),
47706
47864
  content: external_exports.string().min(1, "Content is required").max(MAX.CONTENT).optional().describe(
47707
- 'Note body; required unless contentPath is given (pass exactly one). AppleScript cannot create true Apple Notes checklists \u2014 `<input type="checkbox">`, checklist CSS classes, and markdown `- [ ]` lines do not render as checkable items. To produce a checklist, create the note with a plain `<ul>` or `- ` list and convert it in Notes.app with \u21E7\u2318L.'
47865
+ 'Note body; required unless contentPath is given (pass exactly one). In plaintext and HTML, AppleScript cannot create true Apple Notes checklists \u2014 `<input type="checkbox">`, checklist CSS classes, and markdown `- [ ]` lines do not render as checkable items; create a plain `<ul>` or `- ` list and convert it in Notes.app with \u21E7\u2318L. With format "markdown" (markdownRoute "shortcut"), `- [ ]`/`- [x]` lines, `>` block quotes, ``` fenced code, `---` dividers and `inline code` (which Notes renders as a highlight, not monospace) become native styles (see create-note-markdown-blocks in get-capabilities).'
47708
47866
  ),
47709
47867
  contentPath: external_exports.string().min(1).max(MAX.SAVE_PATH).optional().describe(
47710
47868
  `Absolute path of a local UTF-8 file to use as the body instead of content (pass exactly one). The same locations save-attachment may write to are allowed (home, temp, /Volumes); symbolic links and non-regular files are refused. Limit ${MAX_CONTENT_FILE_BYTES} bytes.`
@@ -47713,7 +47871,7 @@ registerTool(
47713
47871
  "Content format: 'plaintext' (default), 'html' for rich formatting, or 'markdown'. Markdown whose first line is exactly `# <title>` (same case and spacing) has that line and one blank line after it removed, since the title is supplied separately."
47714
47872
  ),
47715
47873
  markdownRoute: external_exports.enum(["shortcut", "html"]).optional().default("shortcut").describe(
47716
- "How format 'markdown' is imported. 'shortcut' (default) uses the Create Markdown Note Shortcut for real Title/Heading/Subheading styles (iCloud only, no tags; see get-capabilities). 'html' is the fallback when that Shortcut is not installed or the note is outside iCloud: it converts the same bounded Markdown subset to HTML and creates the note through AppleScript in any account, rendering `- [ ]` / `- [x]` task items as ordinary list rows that start with a visible \u2610 / \u2611 character \u2014 not native, checkable checklist items."
47874
+ "How format 'markdown' is imported. 'shortcut' (default) uses the Create Markdown Note Shortcut for real Title/Heading/Subheading styles (iCloud only, no tags; see get-capabilities). 'html' is the fallback when that Shortcut is not installed or the note is outside iCloud: it converts the same bounded Markdown subset to HTML and creates the note through AppleScript in any account, rendering `- [ ]` / `- [x]` task items as ordinary list rows that start with a visible \u2610 / \u2611 character \u2014 not native, checkable checklist items \u2014 refusing block quotes, fenced code and inline code, and keeping a `---` line as literal text."
47717
47875
  ),
47718
47876
  tags: external_exports.array(external_exports.string().max(MAX.TAG)).max(MAX.TAGS).optional().describe(
47719
47877
  "Returned-only metadata \u2014 NOT written to Notes.app. Apple Notes tags can't be set via AppleScript, so any values passed here are echoed back in the response but do not appear on the created note. Use #hashtags in the body for searchable text; this does not create native tag objects. Native tags need the Notes Shortcuts action. Refused with format 'markdown': add tags afterwards with add-native-tags."
@@ -47803,6 +47961,7 @@ registerTool(
47803
47961
  'tags are not supported with format "markdown"; create the note without tags, then add them with add-native-tags using the returned id'
47804
47962
  );
47805
47963
  requireValidated("create-note-markdown");
47964
+ if (usesMarkdownBlocks(content)) requireValidated("create-note-markdown-blocks");
47806
47965
  const result = createMarkdownNote(notesManager, { title, content, folder });
47807
47966
  return successResponse(`Note created from Markdown: "${title}" [id: ${result.id}]`, {
47808
47967
  ...result,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-notes-mcp",
3
- "version": "2.8.32",
3
+ "version": "2.8.33",
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",