apple-notes-mcp 2.9.30 → 2.9.32

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 -1
  2. package/build/index.js +110 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -198,6 +198,24 @@ The server is a standard MCP server over stdio, so any client that can launch a
198
198
 
199
199
  Read/list/get tools also return **structured JSON** (`structuredContent`) alongside the text, so agents can consume results without parsing prose.
200
200
 
201
+ Some clients (Claude Desktop among them) pass the model only a result's text
202
+ and drop `structuredContent`. So every result that has `structuredContent` also
203
+ ends with one more text block holding the same data as a single JSON line:
204
+
205
+ ```
206
+ structuredContent: {"id":"x-coredata://…/ICNote/p123","contentHash":"sha256:…","writable":true,…}
207
+ ```
208
+
209
+ That line carries every token a follow-up call needs: the `contentHash`
210
+ revision token for `expectedContentHash`, new ids, `nativeTags`, `writable`,
211
+ `page.nextOffset`, and an error's `code`, `committed` and `indeterminate`. A
212
+ long value the text above already shows in full, such as the body from
213
+ `get-note-content`, appears as `"[shown in full above]"` instead of twice. If
214
+ the line would still exceed 16 KB, fields larger than 1 KB (usually a list
215
+ already printed above) are left out and named in `_omitted`. Tools whose text
216
+ already is that JSON get no extra block. `structuredContent` itself is
217
+ unchanged.
218
+
201
219
  ### MCP resources & prompts
202
220
 
203
221
  Resources expose read-only context the client can attach without a tool call:
@@ -238,7 +256,9 @@ When Full Disk Access is granted, list and read tools also return stable identif
238
256
  ### Error results
239
257
 
240
258
  A failed call returns `isError: true` with the same human-readable text as
241
- before, plus `structuredContent` carrying a stable machine-readable `code`.
259
+ before, plus `structuredContent` carrying a stable machine-readable `code`
260
+ (also repeated in the trailing `structuredContent:` text line, for clients
261
+ that drop `structuredContent`).
242
262
  Branch on `code`, not on the prose, which may be reworded.
243
263
 
244
264
  | `code` | Meaning |
package/build/index.js CHANGED
@@ -3110,12 +3110,13 @@ var require_data = __commonJS({
3110
3110
  }
3111
3111
  });
3112
3112
 
3113
- // node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/lib/utils.js
3113
+ // node_modules/.pnpm/fast-uri@3.1.8/node_modules/fast-uri/lib/utils.js
3114
3114
  var require_utils = __commonJS({
3115
- "node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/lib/utils.js"(exports, module) {
3115
+ "node_modules/.pnpm/fast-uri@3.1.8/node_modules/fast-uri/lib/utils.js"(exports, module) {
3116
3116
  "use strict";
3117
3117
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
3118
3118
  var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
3119
+ var isPort = RegExp.prototype.test.bind(/^\d*$/u);
3119
3120
  var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
3120
3121
  var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
3121
3122
  var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
@@ -3581,8 +3582,12 @@ var require_utils = __commonJS({
3581
3582
  uriTokens.push(host);
3582
3583
  }
3583
3584
  if (typeof component2.port === "number" || typeof component2.port === "string") {
3585
+ const port = String(component2.port);
3586
+ if (!isPort(port)) {
3587
+ throw new TypeError("URI port is malformed.");
3588
+ }
3584
3589
  uriTokens.push(":");
3585
- uriTokens.push(String(component2.port));
3590
+ uriTokens.push(port);
3586
3591
  }
3587
3592
  return uriTokens.length ? uriTokens.join("") : void 0;
3588
3593
  }
@@ -3607,9 +3612,9 @@ var require_utils = __commonJS({
3607
3612
  }
3608
3613
  });
3609
3614
 
3610
- // node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/lib/schemes.js
3615
+ // node_modules/.pnpm/fast-uri@3.1.8/node_modules/fast-uri/lib/schemes.js
3611
3616
  var require_schemes = __commonJS({
3612
- "node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/lib/schemes.js"(exports, module) {
3617
+ "node_modules/.pnpm/fast-uri@3.1.8/node_modules/fast-uri/lib/schemes.js"(exports, module) {
3613
3618
  "use strict";
3614
3619
  var { isUUID } = require_utils();
3615
3620
  var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
@@ -3818,9 +3823,9 @@ var require_schemes = __commonJS({
3818
3823
  }
3819
3824
  });
3820
3825
 
3821
- // node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/index.js
3826
+ // node_modules/.pnpm/fast-uri@3.1.8/node_modules/fast-uri/index.js
3822
3827
  var require_fast_uri = __commonJS({
3823
- "node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/index.js"(exports, module) {
3828
+ "node_modules/.pnpm/fast-uri@3.1.8/node_modules/fast-uri/index.js"(exports, module) {
3824
3829
  "use strict";
3825
3830
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3826
3831
  var { SCHEMES, getSchemeHandler } = require_schemes();
@@ -4025,12 +4030,15 @@ var require_fast_uri = __commonJS({
4025
4030
  }
4026
4031
  return false;
4027
4032
  }
4033
+ function isIPLiteral(host) {
4034
+ return host[0] === "[" && host[host.length - 1] === "]";
4035
+ }
4028
4036
  function hasMalformedComponentPercentEncoding(matches) {
4029
4037
  const host = matches[4];
4030
- return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !(host[0] === "[" && host[host.length - 1] === "]") && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
4038
+ return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
4031
4039
  }
4032
4040
  function canonicalizeHost(parsed, options, schemeHandler, isIP) {
4033
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && parsed.host[0] !== "[" && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
4041
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
4034
4042
  try {
4035
4043
  parsed.host = new URL("http://" + parsed.host).hostname;
4036
4044
  } catch (e) {
@@ -4117,10 +4125,11 @@ var require_fast_uri = __commonJS({
4117
4125
  if (parsed.host) {
4118
4126
  const ipv4result = isIPv4(parsed.host);
4119
4127
  if (ipv4result === false) {
4120
- const bracketedIPLiteral = parsed.host[0] === "[" && parsed.host[parsed.host.length - 1] === "]";
4128
+ const bracketedIPLiteral = isIPLiteral(parsed.host);
4129
+ const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
4121
4130
  const ipv6result = normalizeIPv6(parsed.host);
4122
4131
  isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
4123
- malformedIPLiteral = bracketedIPLiteral && ipv6result.error === true;
4132
+ malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
4124
4133
  parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
4125
4134
  if (malformedIPLiteral) {
4126
4135
  parsed.error = parsed.error || "URI host is malformed.";
@@ -4143,14 +4152,17 @@ var require_fast_uri = __commonJS({
4143
4152
  parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
4144
4153
  }
4145
4154
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
4146
- malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4147
- if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4148
- if (uri.indexOf("%") !== -1) {
4149
- if (parsed.host !== void 0 && !malformedIPLiteral) {
4150
- const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4151
- parsed.host = reescapeHostDelimiters(host, isIP);
4152
- }
4155
+ if (!malformedIPLiteral) {
4156
+ malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4157
+ }
4158
+ if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
4159
+ let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4160
+ if (!isIP) {
4161
+ host = normalizePercentEncoding(host.toLowerCase());
4153
4162
  }
4163
+ parsed.host = reescapeHostDelimiters(host, isIP);
4164
+ }
4165
+ if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4154
4166
  if (parsed.path) {
4155
4167
  parsed.path = normalizePathEncoding(parsed.path);
4156
4168
  }
@@ -53486,6 +53498,85 @@ function exportWithTemplate(request, deps, chosen, notes, skipped, { output, ass
53486
53498
  return { ...receipt, markdown };
53487
53499
  }
53488
53500
 
53501
+ // src/utils/structuredText.ts
53502
+ import { isDeepStrictEqual } from "node:util";
53503
+ var STRUCTURED_TEXT_PREFIX = "structuredContent: ";
53504
+ var SHOWN_ABOVE = "[shown in full above]";
53505
+ var MIN_ELIDED_CHARS = 64;
53506
+ var MAX_MIRROR_CHARS = 16384;
53507
+ var MAX_FIELD_CHARS = 1024;
53508
+ function textOf(content) {
53509
+ return content.map(
53510
+ (item) => item && typeof item === "object" && item.type === "text" ? String(item.text ?? "") : ""
53511
+ ).join("\n");
53512
+ }
53513
+ function elide(value, text2) {
53514
+ if (typeof value === "string") {
53515
+ return value.length >= MIN_ELIDED_CHARS && text2.includes(value) ? SHOWN_ABOVE : value;
53516
+ }
53517
+ if (Array.isArray(value)) return value.map((item) => elide(item, text2));
53518
+ if (value && typeof value === "object") {
53519
+ const out = {};
53520
+ for (const [key, item] of Object.entries(value)) {
53521
+ if (item !== void 0) out[key] = elide(item, text2);
53522
+ }
53523
+ return out;
53524
+ }
53525
+ return value;
53526
+ }
53527
+ function textIsTheJson(content, structured) {
53528
+ const plain = JSON.parse(JSON.stringify(structured));
53529
+ return content.some((item) => {
53530
+ const text2 = item?.text;
53531
+ if (item?.type !== "text" || typeof text2 !== "string") return false;
53532
+ const trimmed = text2.trim();
53533
+ if (!trimmed.startsWith("{")) return false;
53534
+ try {
53535
+ return isDeepStrictEqual(JSON.parse(trimmed), plain);
53536
+ } catch {
53537
+ return false;
53538
+ }
53539
+ });
53540
+ }
53541
+ function structuredTextLine(structured, text2) {
53542
+ const elided = elide(structured, text2);
53543
+ let json2 = JSON.stringify(elided);
53544
+ if (json2.length > MAX_MIRROR_CHARS) {
53545
+ const kept = {};
53546
+ const omitted = [];
53547
+ for (const [key, value] of Object.entries(elided)) {
53548
+ if (JSON.stringify(value).length > MAX_FIELD_CHARS) omitted.push(key);
53549
+ else kept[key] = value;
53550
+ }
53551
+ json2 = JSON.stringify({ ...kept, _omitted: omitted });
53552
+ }
53553
+ return STRUCTURED_TEXT_PREFIX + json2;
53554
+ }
53555
+ function withStructuredText(result) {
53556
+ const r = result;
53557
+ if (!r || typeof r !== "object") return result;
53558
+ const structured = r.structuredContent;
53559
+ if (!structured || typeof structured !== "object" || Array.isArray(structured)) return result;
53560
+ const content = Array.isArray(r.content) ? r.content : [];
53561
+ if (textIsTheJson(content, structured)) return result;
53562
+ const line = structuredTextLine(structured, textOf(content));
53563
+ return { ...r, content: [...content, { type: "text", text: line }] };
53564
+ }
53565
+ function installStructuredText(server2) {
53566
+ const target = server2;
53567
+ const register = target.registerTool;
53568
+ if (typeof register === "function") {
53569
+ target.registerTool = function(name, config2, cb) {
53570
+ const wrapped = typeof cb === "function" ? async (...args) => withStructuredText(await cb(...args)) : cb;
53571
+ return register.call(this, name, config2, wrapped);
53572
+ };
53573
+ }
53574
+ const createError = target.createToolError;
53575
+ if (typeof createError === "function") {
53576
+ target.createToolError = (message) => withStructuredText(createError.call(target, message));
53577
+ }
53578
+ }
53579
+
53489
53580
  // src/tools/directOperations.ts
53490
53581
  import { createHash as createHash3 } from "node:crypto";
53491
53582
  import {
@@ -59303,6 +59394,7 @@ var server = new McpServer({
59303
59394
  description: "MCP server for managing Apple Notes - create, search, update, and organize notes"
59304
59395
  });
59305
59396
  installSdkErrorCodes(server);
59397
+ installStructuredText(server);
59306
59398
  var notesManager = new AppleNotesManager();
59307
59399
  registerDirectOperations(server, notesManager);
59308
59400
  registerFolderDelete(server, notesManager);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apple-notes-mcp",
3
- "version": "2.9.30",
3
+ "version": "2.9.32",
4
4
  "description": "MCP server for Apple Notes - read, search, create, edit, organize, and export notes on macOS via Claude and other AI assistants",
5
5
  "type": "module",
6
6
  "main": "build/index.js",