apple-notes-mcp 2.8.11 → 2.8.12

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 +21 -3
  2. package/build/index.js +62 -42
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -565,8 +565,22 @@ visible text after saving, not byte-identical rich formatting. Warns when the
565
565
  note is shared with collaborators.
566
566
 
567
567
  **Safety:** The append is rejected if the note changed since it was read, rich
568
- metadata is unavailable, or the note contains attachments or other native
569
- objects. Existing link destinations are verified after saving.
568
+ metadata is unavailable, or the note contains attachments. Existing link
569
+ destinations are verified after saving.
570
+
571
+ **Notes containing native objects** (a table, a checklist, native tags) cannot be
572
+ spliced, so they are routed to the native end-append bridge — see
573
+ [`append-native`](#append-native). That path additionally requires `scopeText`,
574
+ keeps the default blank-line `separator` and `position: "after"`, and accepts a
575
+ fixed HTML subset rather than anything Notes.app can render:
576
+
577
+ | | Native append |
578
+ |---|---|
579
+ | Elements | `<a>` `<b>` `<br>` `<code>` `<del>` `<div>` `<em>` `<h1>` `<h2>` `<h3>` `<i>` `<li>` `<ol>` `<p>` `<s>` `<span>` `<strong>` `<table>` `<tbody>` `<td>` `<th>` `<thead>` `<tr>` `<tt>` `<u>` `<ul>` |
580
+ | Attributes | `href` on `<a>` (`https:`, `http:`, `notes:`, `applenotes:`, `mailto:` only) and a `font-size` style on `<span>`, e.g. `<span style="font-size: 18px">` — the form Notes itself stores a heading as |
581
+ | Refused | every other element and attribute, by name, naming the accepted subset; `<table>` here (use [`create-table`](#create-table)); comments, doctype and processing instructions |
582
+
583
+ Ordinary notes take the guarded HTML path and are not restricted to that subset.
570
584
 
571
585
  ---
572
586
 
@@ -1050,7 +1064,11 @@ Appends bounded plaintext, semantic HTML, or Markdown while preserving existing
1050
1064
  native objects. The regular `append-to-note` tool routes protected notes here
1051
1065
  when `scopeText` is supplied. Use a distinctive phrase of plain words without
1052
1066
  punctuation, hashtags, or paths because Notes search may not resolve them
1053
- literally.
1067
+ literally. `format: "html"` accepts the fixed subset tabulated under
1068
+ [`append-to-note`](#append-to-note); content outside it is refused by name, with
1069
+ the accepted subset in the error. A transport failure names the Shortcut it was
1070
+ waiting on, so a missing or unapproved bridge is identified rather than guessed
1071
+ at.
1054
1072
 
1055
1073
  #### `create-checklist-item`
1056
1074
 
package/build/index.js CHANGED
@@ -42832,7 +42832,7 @@ function runNativeTagsShortcut(input) {
42832
42832
  });
42833
42833
  } catch {
42834
42834
  throw new Error(
42835
- "Native tag operation did not complete cleanly (possibly waiting for macOS permission). Do not retry automatically; read the exact note and check Shortcuts"
42835
+ `Native tag operation did not complete cleanly; the "${status.shortcut}" Shortcut may be waiting for macOS permission. Do not retry automatically; read the exact note and check that Shortcut in Shortcuts.app`
42836
42836
  );
42837
42837
  } finally {
42838
42838
  rmSync2(directory, { recursive: true, force: true });
@@ -42930,49 +42930,62 @@ function readBackgroundSnapshot(manager, id2) {
42930
42930
  checklist: getChecklistItems(id2).items || []
42931
42931
  };
42932
42932
  }
42933
+ var NATIVE_APPEND_ELEMENTS = [
42934
+ "a",
42935
+ "b",
42936
+ "br",
42937
+ "code",
42938
+ "del",
42939
+ "div",
42940
+ "em",
42941
+ "h1",
42942
+ "h2",
42943
+ "h3",
42944
+ "i",
42945
+ "li",
42946
+ "ol",
42947
+ "p",
42948
+ "s",
42949
+ "span",
42950
+ "strong",
42951
+ "table",
42952
+ "tbody",
42953
+ "td",
42954
+ "th",
42955
+ "thead",
42956
+ "tr",
42957
+ "tt",
42958
+ "u",
42959
+ "ul"
42960
+ ];
42961
+ var NATIVE_APPEND_SPAN_STYLE = /^font-size\s*:\s*\d{1,3}(?:\.\d+)?(?:px|pt)\s*;?$/i;
42962
+ var NATIVE_APPEND_HTML_SUBSET = `Native append accepts ${NATIVE_APPEND_ELEMENTS.map((e) => `<${e}>`).join(" ")}, with href on <a> and a font-size style on <span> as the only attributes; everything else needs update-note.`;
42933
42963
  function validateAppendContent(content, format) {
42934
42964
  if (!content || content.length > 1024 * 1024 || content.includes("\0"))
42935
42965
  throw new Error("Invalid append content (limit 1 MiB)");
42936
42966
  if (format === "html") {
42937
- const allowed = /* @__PURE__ */ new Set([
42938
- "div",
42939
- "p",
42940
- "br",
42941
- "b",
42942
- "strong",
42943
- "i",
42944
- "em",
42945
- "u",
42946
- "s",
42947
- "del",
42948
- "h1",
42949
- "h2",
42950
- "h3",
42951
- "ul",
42952
- "ol",
42953
- "li",
42954
- "a",
42955
- "table",
42956
- "thead",
42957
- "tbody",
42958
- "tr",
42959
- "td",
42960
- "th"
42961
- ]);
42967
+ const allowed = new Set(NATIVE_APPEND_ELEMENTS);
42962
42968
  for (const tag of content.matchAll(/<\/?\s*([a-z][a-z0-9]*)\b([^>]*)>/gi)) {
42963
- if (!allowed.has(tag[1].toLowerCase()))
42964
- throw new Error(`Unsupported HTML element: ${tag[1]}`);
42969
+ const name = tag[1].toLowerCase();
42970
+ if (!allowed.has(name))
42971
+ throw new Error(`Unsupported HTML element: <${name}>. ${NATIVE_APPEND_HTML_SUBSET}`);
42965
42972
  let attrs = tag[2].replace(/\/$/, "").trim();
42966
- if (tag[1].toLowerCase() === "a")
42973
+ if (name === "a")
42967
42974
  attrs = attrs.replace(/\bhref\s*=\s*(["'])(.*?)\1/gi, (_s, _q, url) => {
42968
42975
  if (!/^(https?:\/\/|notes:\/\/|applenotes:|mailto:)/i.test(url) || Array.from(url).some((c) => c.charCodeAt(0) < 33))
42969
42976
  throw new Error("Unsupported link URL");
42970
42977
  return "";
42971
42978
  });
42979
+ if (name === "span")
42980
+ attrs = attrs.replace(/\bstyle\s*=\s*(["'])(.*?)\1/gi, (_s, _q, style) => {
42981
+ if (!NATIVE_APPEND_SPAN_STYLE.test(style.trim()))
42982
+ throw new Error(
42983
+ `Unsupported <span> style: ${style.trim().slice(0, 80)}. Native append accepts font-size only, as in <span style="font-size: 18px">.`
42984
+ );
42985
+ return "";
42986
+ });
42972
42987
  if (attrs.trim())
42973
- throw new Error(
42974
- "Unsupported HTML attributes; use semantic formatting and explicit blank paragraphs"
42975
- );
42988
+ throw new Error(`Unsupported HTML attributes on <${name}>. ${NATIVE_APPEND_HTML_SUBSET}`);
42976
42989
  }
42977
42990
  if (/<!--|<!|<\?|<[^>]*$/u.test(content)) throw new Error("Unsupported HTML markup");
42978
42991
  }
@@ -42982,7 +42995,9 @@ function validateAppendContent(content, format) {
42982
42995
  function runBackgroundShortcut(input) {
42983
42996
  const status = backgroundStatus();
42984
42997
  if (!status.installed)
42985
- throw new Error("Install the supplied Background Operations shortcut once");
42998
+ throw new Error(
42999
+ `Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
43000
+ );
42986
43001
  const directory = mkdtempSync3(join8(tmpdir3(), "apple-notes-background-"));
42987
43002
  try {
42988
43003
  const file = join8(directory, "request.json");
@@ -43004,12 +43019,16 @@ function runBackgroundShortcut(input) {
43004
43019
  }),
43005
43020
  { mode: 384 }
43006
43021
  );
43007
- execFileSync7("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
43008
- encoding: "utf8",
43009
- timeout: 6e4,
43010
- maxBuffer: 1024 * 1024,
43011
- stdio: ["ignore", "pipe", "pipe"]
43012
- });
43022
+ try {
43023
+ execFileSync7("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
43024
+ encoding: "utf8",
43025
+ timeout: 6e4,
43026
+ maxBuffer: 1024 * 1024,
43027
+ stdio: ["ignore", "pipe", "pipe"]
43028
+ });
43029
+ } catch (error2) {
43030
+ throw Object.assign(error2, { shortcut: status.shortcut });
43031
+ }
43013
43032
  } finally {
43014
43033
  rmSync3(directory, { recursive: true, force: true });
43015
43034
  }
@@ -43096,7 +43115,8 @@ function mutateBackground(request, operation, data, verify, deps) {
43096
43115
  } catch (error2) {
43097
43116
  transportUncertain = true;
43098
43117
  const detail = error2;
43099
- transportMessage = detail?.code === "ETIMEDOUT" ? "Shortcuts timed out; check for an interactive parameter or permission request" : String(detail?.stderr || detail?.message || "Shortcuts failed").trim().slice(0, 500);
43118
+ const named = detail?.shortcut ? `the "${detail.shortcut}" Shortcut` : "the background Shortcut";
43119
+ transportMessage = detail?.code === "ETIMEDOUT" ? `Shortcuts timed out waiting for ${named}; check for an interactive parameter or permission request` : `${named} failed: ${String(detail?.stderr || detail?.message || "no output").trim().slice(0, 400)}`;
43100
43120
  }
43101
43121
  const after = deps.read(request.id);
43102
43122
  try {
@@ -43980,7 +44000,7 @@ function registerNativeOperations(server2, manager) {
43980
44000
  );
43981
44001
  tool(
43982
44002
  "append-native",
43983
- "Use when: appending formatted content to a native-object note without replacing its body.\nReturns: exact-ID preservation and appended-content readback.\nDo not use when: the existing note can be safely handled by append-to-note or the scope phrase is ambiguous.\nSafety: requires a fresh revision and unique existing scope phrase of plain words; avoid punctuation, hashtags, and paths because Notes search may not resolve them literally. Supports bounded plaintext, semantic HTML, and Markdown without external fetching or automatic retries.",
44003
+ "Use when: appending formatted content to a native-object note without replacing its body.\nReturns: exact-ID preservation and appended-content readback.\nDo not use when: the existing note can be safely handled by append-to-note or the scope phrase is ambiguous.\nSafety: requires a fresh revision and unique existing scope phrase of plain words; avoid punctuation, hashtags, and paths because Notes search may not resolve them literally. Supports bounded plaintext, semantic HTML, and Markdown without external fetching or automatic retries.\nHTML subset: " + NATIVE_APPEND_HTML_SUBSET,
43984
44004
  {
43985
44005
  ...common,
43986
44006
  content: external_exports.string().min(1).max(1024 * 1024),
@@ -45022,7 +45042,7 @@ registerTool(
45022
45042
  registerTool(
45023
45043
  "append-to-note",
45024
45044
  {
45025
- description: "Use when: adding content to one exact note after reading it by id.\nReturns: exact id, new content hash, and visible-text readback verification.\nDo not use when: you only have a title or the note changed since the read.\nSafety: protected native-object notes use native end-append with scopeText; ordinary notes retain guarded HTML editing. Notes.app normalizes HTML, so rich formatting is not claimed as byte-identical.",
45045
+ description: "Use when: adding content to one exact note after reading it by id.\nReturns: exact id, new content hash, and visible-text readback verification.\nDo not use when: you only have a title or the note changed since the read.\nSafety: protected native-object notes use native end-append with scopeText; ordinary notes retain guarded HTML editing. Notes.app normalizes HTML, so rich formatting is not claimed as byte-identical.\nNative-append HTML subset (protected notes only; ordinary notes accept any HTML Notes.app renders): " + NATIVE_APPEND_HTML_SUBSET + " Native append also requires scopeText, the default blank-line separator and position 'after'.",
45026
45046
  inputSchema: {
45027
45047
  id: noteIdInput,
45028
45048
  expectedContentHash: expectedContentHashInput,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-notes-mcp",
3
- "version": "2.8.11",
3
+ "version": "2.8.12",
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",