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.
- package/README.md +21 -1
- package/build/index.js +110 -18
- 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.
|
|
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.
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
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.
|
|
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",
|