apple-notes-mcp 2.8.24 → 2.8.26
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 +54 -8
- package/build/index.js +533 -186
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -1105,11 +1105,11 @@ var require_util = __commonJS({
|
|
|
1105
1105
|
return false;
|
|
1106
1106
|
}
|
|
1107
1107
|
exports.schemaHasRules = schemaHasRules;
|
|
1108
|
-
function schemaHasRulesButRef(schema,
|
|
1108
|
+
function schemaHasRulesButRef(schema, RULES2) {
|
|
1109
1109
|
if (typeof schema == "boolean")
|
|
1110
1110
|
return !schema;
|
|
1111
1111
|
for (const key in schema)
|
|
1112
|
-
if (key !== "$ref" &&
|
|
1112
|
+
if (key !== "$ref" && RULES2.all[key])
|
|
1113
1113
|
return true;
|
|
1114
1114
|
return false;
|
|
1115
1115
|
}
|
|
@@ -2503,17 +2503,17 @@ var require_validate = __commonJS({
|
|
|
2503
2503
|
}
|
|
2504
2504
|
function schemaKeywords(it, types, typeErrors, errsCount) {
|
|
2505
2505
|
const { gen, schema, data, allErrors, opts, self } = it;
|
|
2506
|
-
const { RULES } = self;
|
|
2507
|
-
if (schema.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema,
|
|
2508
|
-
gen.block(() => keywordCode(it, "$ref",
|
|
2506
|
+
const { RULES: RULES2 } = self;
|
|
2507
|
+
if (schema.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema, RULES2))) {
|
|
2508
|
+
gen.block(() => keywordCode(it, "$ref", RULES2.all.$ref.definition));
|
|
2509
2509
|
return;
|
|
2510
2510
|
}
|
|
2511
2511
|
if (!opts.jtd)
|
|
2512
2512
|
checkStrictTypes(it, types);
|
|
2513
2513
|
gen.block(() => {
|
|
2514
|
-
for (const group of
|
|
2514
|
+
for (const group of RULES2.rules)
|
|
2515
2515
|
groupKeywords(group);
|
|
2516
|
-
groupKeywords(
|
|
2516
|
+
groupKeywords(RULES2.post);
|
|
2517
2517
|
});
|
|
2518
2518
|
function groupKeywords(group) {
|
|
2519
2519
|
if (!(0, applicability_1.shouldUseGroup)(schema, group))
|
|
@@ -3268,8 +3268,8 @@ var require_utils = __commonJS({
|
|
|
3268
3268
|
}
|
|
3269
3269
|
return ind;
|
|
3270
3270
|
}
|
|
3271
|
-
function removeDotSegments(
|
|
3272
|
-
let input =
|
|
3271
|
+
function removeDotSegments(path6) {
|
|
3272
|
+
let input = path6;
|
|
3273
3273
|
const output = [];
|
|
3274
3274
|
let nextSlash = -1;
|
|
3275
3275
|
let len = 0;
|
|
@@ -3674,8 +3674,8 @@ var require_schemes = __commonJS({
|
|
|
3674
3674
|
}
|
|
3675
3675
|
if (wsComponent.resourceName) {
|
|
3676
3676
|
const queryIndex = wsComponent.resourceName.indexOf("?");
|
|
3677
|
-
const
|
|
3678
|
-
wsComponent.path =
|
|
3677
|
+
const path6 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
3678
|
+
wsComponent.path = path6 && path6 !== "/" ? path6 : void 0;
|
|
3679
3679
|
wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
|
|
3680
3680
|
wsComponent.resourceName = void 0;
|
|
3681
3681
|
}
|
|
@@ -4600,10 +4600,10 @@ var require_core = __commonJS({
|
|
|
4600
4600
|
}
|
|
4601
4601
|
// Remove keyword
|
|
4602
4602
|
removeKeyword(keyword) {
|
|
4603
|
-
const { RULES } = this;
|
|
4604
|
-
delete
|
|
4605
|
-
delete
|
|
4606
|
-
for (const group of
|
|
4603
|
+
const { RULES: RULES2 } = this;
|
|
4604
|
+
delete RULES2.keywords[keyword];
|
|
4605
|
+
delete RULES2.all[keyword];
|
|
4606
|
+
for (const group of RULES2.rules) {
|
|
4607
4607
|
const i = group.rules.findIndex((rule) => rule.keyword === keyword);
|
|
4608
4608
|
if (i >= 0)
|
|
4609
4609
|
group.rules.splice(i, 1);
|
|
@@ -4771,9 +4771,9 @@ var require_core = __commonJS({
|
|
|
4771
4771
|
}
|
|
4772
4772
|
var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i;
|
|
4773
4773
|
function checkKeyword(keyword, def) {
|
|
4774
|
-
const { RULES } = this;
|
|
4774
|
+
const { RULES: RULES2 } = this;
|
|
4775
4775
|
(0, util_1.eachItem)(keyword, (kwd) => {
|
|
4776
|
-
if (
|
|
4776
|
+
if (RULES2.keywords[kwd])
|
|
4777
4777
|
throw new Error(`Keyword ${kwd} is already defined`);
|
|
4778
4778
|
if (!KEYWORD_NAME.test(kwd))
|
|
4779
4779
|
throw new Error(`Keyword ${kwd} has invalid name`);
|
|
@@ -4789,13 +4789,13 @@ var require_core = __commonJS({
|
|
|
4789
4789
|
const post = definition === null || definition === void 0 ? void 0 : definition.post;
|
|
4790
4790
|
if (dataType && post)
|
|
4791
4791
|
throw new Error('keyword with "post" flag cannot have "type"');
|
|
4792
|
-
const { RULES } = this;
|
|
4793
|
-
let ruleGroup = post ?
|
|
4792
|
+
const { RULES: RULES2 } = this;
|
|
4793
|
+
let ruleGroup = post ? RULES2.post : RULES2.rules.find(({ type: t }) => t === dataType);
|
|
4794
4794
|
if (!ruleGroup) {
|
|
4795
4795
|
ruleGroup = { type: dataType, rules: [] };
|
|
4796
|
-
|
|
4796
|
+
RULES2.rules.push(ruleGroup);
|
|
4797
4797
|
}
|
|
4798
|
-
|
|
4798
|
+
RULES2.keywords[keyword] = true;
|
|
4799
4799
|
if (!definition)
|
|
4800
4800
|
return;
|
|
4801
4801
|
const rule = {
|
|
@@ -4810,7 +4810,7 @@ var require_core = __commonJS({
|
|
|
4810
4810
|
addBeforeRule.call(this, ruleGroup, rule, definition.before);
|
|
4811
4811
|
else
|
|
4812
4812
|
ruleGroup.rules.push(rule);
|
|
4813
|
-
|
|
4813
|
+
RULES2.all[keyword] = rule;
|
|
4814
4814
|
(_a = definition.implements) === null || _a === void 0 ? void 0 : _a.forEach((kwd) => this.addKeyword(kwd));
|
|
4815
4815
|
}
|
|
4816
4816
|
function addBeforeRule(ruleGroup, rule, before) {
|
|
@@ -7181,12 +7181,12 @@ var require_dist = __commonJS({
|
|
|
7181
7181
|
throw new Error(`Unknown format "${name}"`);
|
|
7182
7182
|
return f;
|
|
7183
7183
|
};
|
|
7184
|
-
function addFormats(ajv, list,
|
|
7184
|
+
function addFormats(ajv, list, fs6, exportName) {
|
|
7185
7185
|
var _a;
|
|
7186
7186
|
var _b;
|
|
7187
7187
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
7188
7188
|
for (const f of list)
|
|
7189
|
-
ajv.addFormat(f,
|
|
7189
|
+
ajv.addFormat(f, fs6[f]);
|
|
7190
7190
|
}
|
|
7191
7191
|
module.exports = exports = formatsPlugin;
|
|
7192
7192
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -12238,32 +12238,32 @@ var require_URL = __commonJS({
|
|
|
12238
12238
|
else
|
|
12239
12239
|
return basepath.substring(0, lastslash + 1) + refpath;
|
|
12240
12240
|
}
|
|
12241
|
-
function remove_dot_segments(
|
|
12242
|
-
if (!
|
|
12241
|
+
function remove_dot_segments(path6) {
|
|
12242
|
+
if (!path6) return path6;
|
|
12243
12243
|
var output = "";
|
|
12244
|
-
while (
|
|
12245
|
-
if (
|
|
12246
|
-
|
|
12244
|
+
while (path6.length > 0) {
|
|
12245
|
+
if (path6 === "." || path6 === "..") {
|
|
12246
|
+
path6 = "";
|
|
12247
12247
|
break;
|
|
12248
12248
|
}
|
|
12249
|
-
var twochars =
|
|
12250
|
-
var threechars =
|
|
12251
|
-
var fourchars =
|
|
12249
|
+
var twochars = path6.substring(0, 2);
|
|
12250
|
+
var threechars = path6.substring(0, 3);
|
|
12251
|
+
var fourchars = path6.substring(0, 4);
|
|
12252
12252
|
if (threechars === "../") {
|
|
12253
|
-
|
|
12253
|
+
path6 = path6.substring(3);
|
|
12254
12254
|
} else if (twochars === "./") {
|
|
12255
|
-
|
|
12255
|
+
path6 = path6.substring(2);
|
|
12256
12256
|
} else if (threechars === "/./") {
|
|
12257
|
-
|
|
12258
|
-
} else if (twochars === "/." &&
|
|
12259
|
-
|
|
12260
|
-
} else if (fourchars === "/../" || threechars === "/.." &&
|
|
12261
|
-
|
|
12257
|
+
path6 = "/" + path6.substring(3);
|
|
12258
|
+
} else if (twochars === "/." && path6.length === 2) {
|
|
12259
|
+
path6 = "/";
|
|
12260
|
+
} else if (fourchars === "/../" || threechars === "/.." && path6.length === 3) {
|
|
12261
|
+
path6 = "/" + path6.substring(4);
|
|
12262
12262
|
output = output.replace(/\/?[^\/]*$/, "");
|
|
12263
12263
|
} else {
|
|
12264
|
-
var segment =
|
|
12264
|
+
var segment = path6.match(/(\/?([^\/]*))/)[0];
|
|
12265
12265
|
output += segment;
|
|
12266
|
-
|
|
12266
|
+
path6 = path6.substring(segment.length);
|
|
12267
12267
|
}
|
|
12268
12268
|
}
|
|
12269
12269
|
return output;
|
|
@@ -24465,14 +24465,14 @@ var require_turndown_cjs = __commonJS({
|
|
|
24465
24465
|
} else if (node.nodeType === 1) {
|
|
24466
24466
|
replacement = replacementForNode.call(self, node);
|
|
24467
24467
|
}
|
|
24468
|
-
return
|
|
24468
|
+
return join13(output, replacement);
|
|
24469
24469
|
}, "");
|
|
24470
24470
|
}
|
|
24471
24471
|
function postProcess(output) {
|
|
24472
24472
|
var self = this;
|
|
24473
24473
|
this.rules.forEach(function(rule) {
|
|
24474
24474
|
if (typeof rule.append === "function") {
|
|
24475
|
-
output =
|
|
24475
|
+
output = join13(output, rule.append(self.options));
|
|
24476
24476
|
}
|
|
24477
24477
|
});
|
|
24478
24478
|
return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
|
|
@@ -24484,7 +24484,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
24484
24484
|
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
|
24485
24485
|
return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
|
|
24486
24486
|
}
|
|
24487
|
-
function
|
|
24487
|
+
function join13(output, replacement) {
|
|
24488
24488
|
var s1 = trimTrailingNewlines(output);
|
|
24489
24489
|
var s2 = trimLeadingNewlines(replacement);
|
|
24490
24490
|
var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
|
|
@@ -24979,8 +24979,8 @@ function getErrorMap() {
|
|
|
24979
24979
|
|
|
24980
24980
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
24981
24981
|
var makeIssue = (params) => {
|
|
24982
|
-
const { data, path:
|
|
24983
|
-
const fullPath = [...
|
|
24982
|
+
const { data, path: path6, errorMaps, issueData } = params;
|
|
24983
|
+
const fullPath = [...path6, ...issueData.path || []];
|
|
24984
24984
|
const fullIssue = {
|
|
24985
24985
|
...issueData,
|
|
24986
24986
|
path: fullPath
|
|
@@ -25096,11 +25096,11 @@ var errorUtil;
|
|
|
25096
25096
|
|
|
25097
25097
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
25098
25098
|
var ParseInputLazyPath = class {
|
|
25099
|
-
constructor(parent, value,
|
|
25099
|
+
constructor(parent, value, path6, key) {
|
|
25100
25100
|
this._cachedPath = [];
|
|
25101
25101
|
this.parent = parent;
|
|
25102
25102
|
this.data = value;
|
|
25103
|
-
this._path =
|
|
25103
|
+
this._path = path6;
|
|
25104
25104
|
this._key = key;
|
|
25105
25105
|
}
|
|
25106
25106
|
get path() {
|
|
@@ -28737,10 +28737,10 @@ function assignProp(target, prop, value) {
|
|
|
28737
28737
|
configurable: true
|
|
28738
28738
|
});
|
|
28739
28739
|
}
|
|
28740
|
-
function getElementAtPath(obj,
|
|
28741
|
-
if (!
|
|
28740
|
+
function getElementAtPath(obj, path6) {
|
|
28741
|
+
if (!path6)
|
|
28742
28742
|
return obj;
|
|
28743
|
-
return
|
|
28743
|
+
return path6.reduce((acc, key) => acc?.[key], obj);
|
|
28744
28744
|
}
|
|
28745
28745
|
function promiseAllObject(promisesObj) {
|
|
28746
28746
|
const keys = Object.keys(promisesObj);
|
|
@@ -29060,11 +29060,11 @@ function aborted(x, startIndex = 0) {
|
|
|
29060
29060
|
}
|
|
29061
29061
|
return false;
|
|
29062
29062
|
}
|
|
29063
|
-
function prefixIssues(
|
|
29063
|
+
function prefixIssues(path6, issues) {
|
|
29064
29064
|
return issues.map((iss) => {
|
|
29065
29065
|
var _a;
|
|
29066
29066
|
(_a = iss).path ?? (_a.path = []);
|
|
29067
|
-
iss.path.unshift(
|
|
29067
|
+
iss.path.unshift(path6);
|
|
29068
29068
|
return iss;
|
|
29069
29069
|
});
|
|
29070
29070
|
}
|
|
@@ -32475,11 +32475,11 @@ function normalizeObjectSchema(schema) {
|
|
|
32475
32475
|
}
|
|
32476
32476
|
return void 0;
|
|
32477
32477
|
}
|
|
32478
|
-
function getDotPath(
|
|
32479
|
-
if (
|
|
32478
|
+
function getDotPath(path6) {
|
|
32479
|
+
if (path6.length === 0) {
|
|
32480
32480
|
return "object root";
|
|
32481
32481
|
}
|
|
32482
|
-
return
|
|
32482
|
+
return path6.reduce((acc, seg, index) => {
|
|
32483
32483
|
if (index === 0) {
|
|
32484
32484
|
return String(seg);
|
|
32485
32485
|
}
|
|
@@ -39862,8 +39862,8 @@ function allowedSaveRoots() {
|
|
|
39862
39862
|
"/private/tmp"
|
|
39863
39863
|
];
|
|
39864
39864
|
}
|
|
39865
|
-
function canonicalize(
|
|
39866
|
-
return realpathSync.native(
|
|
39865
|
+
function canonicalize(path6) {
|
|
39866
|
+
return realpathSync.native(path6);
|
|
39867
39867
|
}
|
|
39868
39868
|
function isWithinRoots(candidate, roots) {
|
|
39869
39869
|
return roots.some((root) => {
|
|
@@ -39885,9 +39885,9 @@ function canonicalRoots(roots) {
|
|
|
39885
39885
|
}
|
|
39886
39886
|
return canonical;
|
|
39887
39887
|
}
|
|
39888
|
-
function entryExists(
|
|
39888
|
+
function entryExists(path6) {
|
|
39889
39889
|
try {
|
|
39890
|
-
lstatSync(
|
|
39890
|
+
lstatSync(path6);
|
|
39891
39891
|
return true;
|
|
39892
39892
|
} catch (e) {
|
|
39893
39893
|
const code = e.code;
|
|
@@ -40203,8 +40203,8 @@ function buildAppLevelScript(command) {
|
|
|
40203
40203
|
end tell
|
|
40204
40204
|
`;
|
|
40205
40205
|
}
|
|
40206
|
-
function getNoteLinkFromDB(
|
|
40207
|
-
const match =
|
|
40206
|
+
function getNoteLinkFromDB(coreDataId2) {
|
|
40207
|
+
const match = coreDataId2.match(/\/p(\d+)$/);
|
|
40208
40208
|
if (!match) return null;
|
|
40209
40209
|
const pk = parseInt(match[1], 10);
|
|
40210
40210
|
const dbPath2 = join4(homedir4(), "Library/Group Containers/group.com.apple.notes/NoteStore.sqlite");
|
|
@@ -41206,9 +41206,9 @@ var AppleNotesManager = class {
|
|
|
41206
41206
|
);
|
|
41207
41207
|
const idResult = executeAppleScript(idScript);
|
|
41208
41208
|
const [rawId = "", rawAccount = ""] = idResult.success ? idResult.output.split(FIELD_SEP) : [];
|
|
41209
|
-
const
|
|
41209
|
+
const folderId2 = idResult.success ? extractCoreDataId(rawId, "folder") : "";
|
|
41210
41210
|
return {
|
|
41211
|
-
id:
|
|
41211
|
+
id: folderId2,
|
|
41212
41212
|
name,
|
|
41213
41213
|
account: rawAccount.trim() || targetAccount || ""
|
|
41214
41214
|
};
|
|
@@ -42644,6 +42644,244 @@ function getNoteMetadata(noteId3) {
|
|
|
42644
42644
|
}
|
|
42645
42645
|
}
|
|
42646
42646
|
|
|
42647
|
+
// src/utils/noteIdentifiers.ts
|
|
42648
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
42649
|
+
import * as fs4 from "fs";
|
|
42650
|
+
import * as os4 from "os";
|
|
42651
|
+
import * as path4 from "path";
|
|
42652
|
+
var NOTES_DB_PATH4 = path4.join(
|
|
42653
|
+
os4.homedir(),
|
|
42654
|
+
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
42655
|
+
);
|
|
42656
|
+
var UUID_PATTERN = /^[0-9A-Fa-f]{8}(?:-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$/;
|
|
42657
|
+
var NUMERIC_KEY_PATTERN = /^\d{1,18}$/;
|
|
42658
|
+
var NOTE_ID_MESSAGE = "A canonical Apple Note ID is required (x-coredata://.../ICNote/p...), or the note's Notes UUID or numeric key";
|
|
42659
|
+
function identifierForm(value) {
|
|
42660
|
+
if (UUID_PATTERN.test(value)) return "uuid";
|
|
42661
|
+
if (NUMERIC_KEY_PATTERN.test(value)) return "key";
|
|
42662
|
+
return "other";
|
|
42663
|
+
}
|
|
42664
|
+
function isAlternateIdentifier(value) {
|
|
42665
|
+
return identifierForm(value) !== "other";
|
|
42666
|
+
}
|
|
42667
|
+
var IdentifierResolutionError = class extends Error {
|
|
42668
|
+
constructor(code, message) {
|
|
42669
|
+
super(message);
|
|
42670
|
+
this.code = code;
|
|
42671
|
+
this.name = "IdentifierResolutionError";
|
|
42672
|
+
}
|
|
42673
|
+
code;
|
|
42674
|
+
};
|
|
42675
|
+
var ENTITY_LABEL = {
|
|
42676
|
+
ICNote: "note",
|
|
42677
|
+
ICFolder: "folder",
|
|
42678
|
+
ICAccount: "account"
|
|
42679
|
+
};
|
|
42680
|
+
var NO_FDA_MESSAGE = `Resolving a Notes UUID or numeric key reads the Notes database, which needs Full Disk Access for the app that launches this server (System Settings > Privacy & Security > Full Disk Access, then fully quit and relaunch it). x-coredata ids from search-notes, list-notes, or list-folders work without it. Setup guide: ${FULL_DISK_ACCESS_GUIDE_URL}`;
|
|
42681
|
+
function canonicalKey(value) {
|
|
42682
|
+
return BigInt(value).toString();
|
|
42683
|
+
}
|
|
42684
|
+
function assertAll(values, pattern, label) {
|
|
42685
|
+
for (const value of values) {
|
|
42686
|
+
if (!pattern.test(value)) throw new Error(`Refusing to query with an invalid ${label}`);
|
|
42687
|
+
}
|
|
42688
|
+
}
|
|
42689
|
+
function entityClause(entity2) {
|
|
42690
|
+
return `(SELECT Z_ENT FROM Z_PRIMARYKEY WHERE Z_NAME = '${entity2}')`;
|
|
42691
|
+
}
|
|
42692
|
+
function buildResolveSql(entity2, keys, uuids) {
|
|
42693
|
+
assertAll(keys, NUMERIC_KEY_PATTERN, "numeric key");
|
|
42694
|
+
assertAll(uuids, UUID_PATTERN, "UUID");
|
|
42695
|
+
const matches = [];
|
|
42696
|
+
if (keys.length > 0) {
|
|
42697
|
+
matches.push(`o.Z_PK IN (${[...new Set(keys.map(canonicalKey))].join(", ")})`);
|
|
42698
|
+
}
|
|
42699
|
+
if (uuids.length > 0) {
|
|
42700
|
+
const variants = /* @__PURE__ */ new Set();
|
|
42701
|
+
for (const uuid2 of uuids) {
|
|
42702
|
+
variants.add(uuid2);
|
|
42703
|
+
variants.add(uuid2.toUpperCase());
|
|
42704
|
+
variants.add(uuid2.toLowerCase());
|
|
42705
|
+
}
|
|
42706
|
+
matches.push(`o.ZIDENTIFIER IN (${[...variants].map((v) => `'${v}'`).join(", ")})`);
|
|
42707
|
+
}
|
|
42708
|
+
const where = matches.length > 0 ? matches.join(" OR ") : "0";
|
|
42709
|
+
return `SELECT json_object('store', (SELECT Z_UUID FROM Z_METADATA LIMIT 1), 'rows', (SELECT json_group_array(json_object('pk', o.Z_PK, 'identifier', o.ZIDENTIFIER)) FROM ZICCLOUDSYNCINGOBJECT o WHERE o.Z_ENT = ${entityClause(entity2)} AND (${where})));`;
|
|
42710
|
+
}
|
|
42711
|
+
function buildLookupSql(entity2, keys) {
|
|
42712
|
+
assertAll(keys, NUMERIC_KEY_PATTERN, "numeric key");
|
|
42713
|
+
const pks = [...new Set(keys.map(canonicalKey))];
|
|
42714
|
+
const inList = pks.length > 0 ? pks.join(", ") : "NULL";
|
|
42715
|
+
let fields = "'pk', o.Z_PK, 'identifier', o.ZIDENTIFIER";
|
|
42716
|
+
let joins = "";
|
|
42717
|
+
if (entity2 === "ICNote") {
|
|
42718
|
+
fields += ", 'folderIdentifier', f.ZIDENTIFIER, 'accountIdentifier', a.ZIDENTIFIER";
|
|
42719
|
+
joins = "LEFT JOIN ZICCLOUDSYNCINGOBJECT f ON f.Z_PK = o.ZFOLDER LEFT JOIN ZICCLOUDSYNCINGOBJECT a ON a.Z_PK = f.ZOWNER ";
|
|
42720
|
+
} else if (entity2 === "ICFolder") {
|
|
42721
|
+
fields += ", 'parentIdentifier', p.ZIDENTIFIER, 'accountIdentifier', a.ZIDENTIFIER";
|
|
42722
|
+
joins = "LEFT JOIN ZICCLOUDSYNCINGOBJECT p ON p.Z_PK = o.ZPARENT LEFT JOIN ZICCLOUDSYNCINGOBJECT a ON a.Z_PK = o.ZOWNER ";
|
|
42723
|
+
}
|
|
42724
|
+
return `SELECT json_object('store', (SELECT Z_UUID FROM Z_METADATA LIMIT 1), 'rows', (SELECT json_group_array(json_object(${fields})) FROM ZICCLOUDSYNCINGOBJECT o ${joins}WHERE o.Z_ENT = ${entityClause(entity2)} AND o.Z_PK IN (${inList})));`;
|
|
42725
|
+
}
|
|
42726
|
+
function runJsonQuery(sql, dbPath2) {
|
|
42727
|
+
if (!fs4.existsSync(dbPath2)) throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
|
|
42728
|
+
let out;
|
|
42729
|
+
try {
|
|
42730
|
+
out = execFileSync6("sqlite3", ["-readonly", dbPath2, sql], {
|
|
42731
|
+
encoding: "utf8",
|
|
42732
|
+
timeout: 5e3,
|
|
42733
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
42734
|
+
}).trim();
|
|
42735
|
+
} catch (error2) {
|
|
42736
|
+
const detail = error2 instanceof Error ? `${error2.message} ${String(error2.stderr ?? "")}` : String(error2);
|
|
42737
|
+
if (detail.includes("authorization denied") || detail.includes("unable to open database")) {
|
|
42738
|
+
throw new IdentifierResolutionError("no_fda", NO_FDA_MESSAGE);
|
|
42739
|
+
}
|
|
42740
|
+
console.error(`Identifier query failed: ${detail}`);
|
|
42741
|
+
throw new IdentifierResolutionError("query_error", "Failed to read the Notes database.");
|
|
42742
|
+
}
|
|
42743
|
+
const parsed = JSON.parse(out || "{}");
|
|
42744
|
+
const rows = typeof parsed.rows === "string" ? JSON.parse(parsed.rows) : parsed.rows;
|
|
42745
|
+
return { store: parsed.store ?? null, rows: rows ?? [] };
|
|
42746
|
+
}
|
|
42747
|
+
function coreDataId(store, entity2, pk) {
|
|
42748
|
+
return `x-coredata://${store}/${entity2}/p${pk}`;
|
|
42749
|
+
}
|
|
42750
|
+
function resolveIdentifiers(values, entity2, dbPath2 = NOTES_DB_PATH4) {
|
|
42751
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
42752
|
+
const keys = [];
|
|
42753
|
+
const uuids = [];
|
|
42754
|
+
for (const value of values) {
|
|
42755
|
+
const form = identifierForm(value);
|
|
42756
|
+
if (form === "key") keys.push(value);
|
|
42757
|
+
else if (form === "uuid") uuids.push(value);
|
|
42758
|
+
else resolved.set(value, value);
|
|
42759
|
+
}
|
|
42760
|
+
if (keys.length === 0 && uuids.length === 0) return resolved;
|
|
42761
|
+
const { store, rows } = runJsonQuery(
|
|
42762
|
+
buildResolveSql(entity2, keys, uuids),
|
|
42763
|
+
dbPath2
|
|
42764
|
+
);
|
|
42765
|
+
if (!store) {
|
|
42766
|
+
throw new IdentifierResolutionError(
|
|
42767
|
+
"query_error",
|
|
42768
|
+
"The Notes database has no store UUID, so an x-coredata id cannot be built."
|
|
42769
|
+
);
|
|
42770
|
+
}
|
|
42771
|
+
const byKey = new Map(rows.map((row) => [String(row.pk), row]));
|
|
42772
|
+
const byUuid = new Map(
|
|
42773
|
+
rows.filter((row) => row.identifier).map((row) => [String(row.identifier).toUpperCase(), row])
|
|
42774
|
+
);
|
|
42775
|
+
const label = ENTITY_LABEL[entity2];
|
|
42776
|
+
const missing = [];
|
|
42777
|
+
for (const key of keys) {
|
|
42778
|
+
const row = byKey.get(canonicalKey(key));
|
|
42779
|
+
if (row) resolved.set(key, coreDataId(store, entity2, row.pk));
|
|
42780
|
+
else missing.push(`numeric key ${key}`);
|
|
42781
|
+
}
|
|
42782
|
+
for (const uuid2 of uuids) {
|
|
42783
|
+
const row = byUuid.get(uuid2.toUpperCase());
|
|
42784
|
+
if (row) resolved.set(uuid2, coreDataId(store, entity2, row.pk));
|
|
42785
|
+
else missing.push(`identifier ${uuid2}`);
|
|
42786
|
+
}
|
|
42787
|
+
if (missing.length > 0) {
|
|
42788
|
+
throw new IdentifierResolutionError(
|
|
42789
|
+
"not_found",
|
|
42790
|
+
`No ${label} found for ${missing.join(", ")}. A numeric key must belong to a ${label}, not another object type.`
|
|
42791
|
+
);
|
|
42792
|
+
}
|
|
42793
|
+
return resolved;
|
|
42794
|
+
}
|
|
42795
|
+
var COREDATA_PARTS = /^x-coredata:\/\/([0-9A-Fa-f-]+)\/(ICNote|ICFolder|ICAccount)\/p(\d{1,18})$/;
|
|
42796
|
+
function lookupStableIdentifiers(ids, entity2, dbPath2 = NOTES_DB_PATH4) {
|
|
42797
|
+
const result = /* @__PURE__ */ new Map();
|
|
42798
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
42799
|
+
for (const id2 of ids) {
|
|
42800
|
+
const match = COREDATA_PARTS.exec(id2);
|
|
42801
|
+
if (!match || match[2] !== entity2) continue;
|
|
42802
|
+
const pk = canonicalKey(match[3]);
|
|
42803
|
+
const list = wanted.get(pk) ?? [];
|
|
42804
|
+
list.push({ id: id2, store: match[1] });
|
|
42805
|
+
wanted.set(pk, list);
|
|
42806
|
+
}
|
|
42807
|
+
if (wanted.size === 0) return result;
|
|
42808
|
+
let query;
|
|
42809
|
+
try {
|
|
42810
|
+
query = runJsonQuery(buildLookupSql(entity2, [...wanted.keys()]), dbPath2);
|
|
42811
|
+
} catch {
|
|
42812
|
+
return result;
|
|
42813
|
+
}
|
|
42814
|
+
const store = query.store?.toUpperCase();
|
|
42815
|
+
for (const row of query.rows) {
|
|
42816
|
+
const fields = {};
|
|
42817
|
+
for (const key of [
|
|
42818
|
+
"identifier",
|
|
42819
|
+
"folderIdentifier",
|
|
42820
|
+
"parentIdentifier",
|
|
42821
|
+
"accountIdentifier"
|
|
42822
|
+
]) {
|
|
42823
|
+
const value = row[key];
|
|
42824
|
+
if (typeof value === "string" && value) fields[key] = value;
|
|
42825
|
+
}
|
|
42826
|
+
for (const target of wanted.get(String(row.pk)) ?? []) {
|
|
42827
|
+
if (target.store.toUpperCase() === store) result.set(target.id, fields);
|
|
42828
|
+
}
|
|
42829
|
+
}
|
|
42830
|
+
return result;
|
|
42831
|
+
}
|
|
42832
|
+
function withStableIdentifiers(items, entity2, dbPath2 = NOTES_DB_PATH4) {
|
|
42833
|
+
const ids = items.map((item) => item.id).filter((id2) => typeof id2 === "string");
|
|
42834
|
+
if (ids.length === 0) return items;
|
|
42835
|
+
const found = lookupStableIdentifiers(ids, entity2, dbPath2);
|
|
42836
|
+
if (found.size === 0) return items;
|
|
42837
|
+
return items.map(
|
|
42838
|
+
(item) => item.id && found.has(item.id) ? { ...item, ...found.get(item.id) } : item
|
|
42839
|
+
);
|
|
42840
|
+
}
|
|
42841
|
+
var defaultResolver = (values, entity2) => resolveIdentifiers(values, entity2);
|
|
42842
|
+
var DEFAULT_MAX_ID_LENGTH = 2e3;
|
|
42843
|
+
function acceptAlternateForms(pattern) {
|
|
42844
|
+
return new RegExp(
|
|
42845
|
+
`${pattern.source}|${UUID_PATTERN.source}|${NUMERIC_KEY_PATTERN.source}`,
|
|
42846
|
+
pattern.flags
|
|
42847
|
+
);
|
|
42848
|
+
}
|
|
42849
|
+
function resolveInSchema(values, entity2, ctx, resolver) {
|
|
42850
|
+
if (!values.some(isAlternateIdentifier)) return new Map(values.map((v) => [v, v]));
|
|
42851
|
+
try {
|
|
42852
|
+
return resolver(values, entity2);
|
|
42853
|
+
} catch (error2) {
|
|
42854
|
+
ctx.addIssue({
|
|
42855
|
+
code: external_exports.ZodIssueCode.custom,
|
|
42856
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
42857
|
+
});
|
|
42858
|
+
return null;
|
|
42859
|
+
}
|
|
42860
|
+
}
|
|
42861
|
+
function exactIdInput(entity2, pattern, message, options = {}) {
|
|
42862
|
+
const resolver = options.resolver ?? defaultResolver;
|
|
42863
|
+
return external_exports.string().max(options.maxLength ?? DEFAULT_MAX_ID_LENGTH).regex(acceptAlternateForms(pattern), message).transform((value, ctx) => {
|
|
42864
|
+
const map = resolveInSchema([value], entity2, ctx, resolver);
|
|
42865
|
+
return map ? map.get(value) ?? value : external_exports.NEVER;
|
|
42866
|
+
});
|
|
42867
|
+
}
|
|
42868
|
+
function exactIdArrayInput(entity2, pattern, message, options = {}) {
|
|
42869
|
+
const resolver = options.resolver ?? defaultResolver;
|
|
42870
|
+
const item = external_exports.string().max(options.maxLength ?? DEFAULT_MAX_ID_LENGTH).regex(acceptAlternateForms(pattern), message);
|
|
42871
|
+
const array2 = options.maxItems === void 0 ? external_exports.array(item) : external_exports.array(item).max(options.maxItems);
|
|
42872
|
+
return array2.transform((values, ctx) => {
|
|
42873
|
+
const map = resolveInSchema(values, entity2, ctx, resolver);
|
|
42874
|
+
return map ? values.map((value) => map.get(value) ?? value) : external_exports.NEVER;
|
|
42875
|
+
});
|
|
42876
|
+
}
|
|
42877
|
+
function looseIdTransform(entity2, resolver = defaultResolver) {
|
|
42878
|
+
return (value, ctx) => {
|
|
42879
|
+
if (!isAlternateIdentifier(value)) return value;
|
|
42880
|
+
const map = resolveInSchema([value], entity2, ctx, resolver);
|
|
42881
|
+
return map ? map.get(value) ?? value : external_exports.NEVER;
|
|
42882
|
+
};
|
|
42883
|
+
}
|
|
42884
|
+
|
|
42647
42885
|
// src/utils/contentWarnings.ts
|
|
42648
42886
|
function detectChecklistAttempt(content) {
|
|
42649
42887
|
if (!content) return null;
|
|
@@ -43179,13 +43417,13 @@ function positiveTextTerms(node, negated = false) {
|
|
|
43179
43417
|
}
|
|
43180
43418
|
|
|
43181
43419
|
// src/utils/noteQueryStore.ts
|
|
43182
|
-
import { execFileSync as
|
|
43183
|
-
import * as
|
|
43184
|
-
import * as
|
|
43185
|
-
import * as
|
|
43420
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
43421
|
+
import * as fs5 from "fs";
|
|
43422
|
+
import * as os5 from "os";
|
|
43423
|
+
import * as path5 from "path";
|
|
43186
43424
|
import { gunzipSync as gunzipSync3 } from "zlib";
|
|
43187
|
-
var
|
|
43188
|
-
|
|
43425
|
+
var NOTES_DB_PATH5 = path5.join(
|
|
43426
|
+
os5.homedir(),
|
|
43189
43427
|
"Library/Group Containers/group.com.apple.notes/NoteStore.sqlite"
|
|
43190
43428
|
);
|
|
43191
43429
|
var QUERY_SCAN = { DEFAULT: 500, MAX: 5e3 };
|
|
@@ -43320,7 +43558,7 @@ function buildScanSql(available, options) {
|
|
|
43320
43558
|
].join(" ");
|
|
43321
43559
|
}
|
|
43322
43560
|
function runSqlite2(dbPath2, query) {
|
|
43323
|
-
return
|
|
43561
|
+
return execFileSync7("sqlite3", ["-readonly", dbPath2, query], {
|
|
43324
43562
|
encoding: "utf8",
|
|
43325
43563
|
timeout: 3e4,
|
|
43326
43564
|
maxBuffer: 512 * 1024 * 1024,
|
|
@@ -43347,12 +43585,12 @@ function resolveFolders(rows) {
|
|
|
43347
43585
|
seen.add(pk);
|
|
43348
43586
|
const parent = row.parent !== null ? resolve3(row.parent, seen) : void 0;
|
|
43349
43587
|
const name = row.name ?? "";
|
|
43350
|
-
const
|
|
43588
|
+
const path6 = parent ? `${parent.path}/${escapeSegment(name)}` : escapeSegment(name);
|
|
43351
43589
|
const plainPath = parent ? `${parent.plainPath}/${name}` : name;
|
|
43352
43590
|
const info = {
|
|
43353
|
-
path:
|
|
43591
|
+
path: path6,
|
|
43354
43592
|
plainPath,
|
|
43355
|
-
keys: [...new Set([name, plainPath,
|
|
43593
|
+
keys: [...new Set([name, plainPath, path6].map(normalizeForMatch))],
|
|
43356
43594
|
accountPk: row.owner ?? parent?.accountPk ?? null,
|
|
43357
43595
|
shared: Boolean(row.shared) || Boolean(parent?.shared)
|
|
43358
43596
|
};
|
|
@@ -43384,8 +43622,8 @@ function queryNotes(expression, options = {}) {
|
|
|
43384
43622
|
const includeDeleted = options.includeDeleted ?? false;
|
|
43385
43623
|
const withBodies = needsContent(ast);
|
|
43386
43624
|
const withTags = needsTags(ast);
|
|
43387
|
-
const dbPath2 = options.dbPath ??
|
|
43388
|
-
if (!
|
|
43625
|
+
const dbPath2 = options.dbPath ?? NOTES_DB_PATH5;
|
|
43626
|
+
if (!fs5.existsSync(dbPath2)) throw new NoteQueryStoreError(QUERY_FDA_MESSAGE, "no_fda");
|
|
43389
43627
|
let output;
|
|
43390
43628
|
try {
|
|
43391
43629
|
const available = presentColumns2(dbPath2);
|
|
@@ -43520,10 +43758,10 @@ function queryNotes(expression, options = {}) {
|
|
|
43520
43758
|
import { spawnSync } from "child_process";
|
|
43521
43759
|
|
|
43522
43760
|
// src/services/nativeTags.ts
|
|
43523
|
-
import { execFileSync as
|
|
43761
|
+
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
43524
43762
|
import { mkdtempSync as mkdtempSync2, writeFileSync, rmSync as rmSync2 } from "node:fs";
|
|
43525
43763
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
43526
|
-
import { join as
|
|
43764
|
+
import { join as join9 } from "node:path";
|
|
43527
43765
|
|
|
43528
43766
|
// src/services/shortcutConsent.ts
|
|
43529
43767
|
function shortcutConsentHint(shortcut) {
|
|
@@ -43599,7 +43837,7 @@ function addNativeTags(request, deps) {
|
|
|
43599
43837
|
};
|
|
43600
43838
|
}
|
|
43601
43839
|
function listInstalledShortcuts() {
|
|
43602
|
-
return
|
|
43840
|
+
return execFileSync8("/usr/bin/shortcuts", ["list", "--show-identifiers"], {
|
|
43603
43841
|
encoding: "utf8",
|
|
43604
43842
|
timeout: 15e3,
|
|
43605
43843
|
maxBuffer: 1024 * 1024,
|
|
@@ -43625,11 +43863,11 @@ function runNativeTagsShortcut(input) {
|
|
|
43625
43863
|
const status = nativeTagsStatus();
|
|
43626
43864
|
if (!status.installed)
|
|
43627
43865
|
throw new Error(`Import the supplied ${status.shortcut}.shortcut in Shortcuts first`);
|
|
43628
|
-
const directory = mkdtempSync2(
|
|
43866
|
+
const directory = mkdtempSync2(join9(tmpdir2(), "apple-notes-native-tags-"));
|
|
43629
43867
|
try {
|
|
43630
|
-
const
|
|
43631
|
-
writeFileSync(
|
|
43632
|
-
|
|
43868
|
+
const path6 = join9(directory, "request.json");
|
|
43869
|
+
writeFileSync(path6, JSON.stringify(input), { mode: 384 });
|
|
43870
|
+
execFileSync8("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", path6], {
|
|
43633
43871
|
encoding: "utf8",
|
|
43634
43872
|
timeout: 6e4,
|
|
43635
43873
|
maxBuffer: 1024 * 1024,
|
|
@@ -43648,10 +43886,10 @@ function runNativeTagsShortcut(input) {
|
|
|
43648
43886
|
}
|
|
43649
43887
|
|
|
43650
43888
|
// src/services/backgroundNotes.ts
|
|
43651
|
-
import { execFileSync as
|
|
43889
|
+
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
43652
43890
|
import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync2, rmSync as rmSync3 } from "node:fs";
|
|
43653
43891
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
43654
|
-
import { join as
|
|
43892
|
+
import { join as join10 } from "node:path";
|
|
43655
43893
|
|
|
43656
43894
|
// src/utils/appendMarkdown.ts
|
|
43657
43895
|
var escape2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
@@ -43840,9 +44078,9 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
43840
44078
|
throw new Error(
|
|
43841
44079
|
`Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
|
|
43842
44080
|
);
|
|
43843
|
-
const directory = mkdtempSync3(
|
|
44081
|
+
const directory = mkdtempSync3(join10(tmpdir3(), "apple-notes-background-"));
|
|
43844
44082
|
try {
|
|
43845
|
-
const file =
|
|
44083
|
+
const file = join10(directory, "request.json");
|
|
43846
44084
|
writeFileSync2(
|
|
43847
44085
|
file,
|
|
43848
44086
|
JSON.stringify({
|
|
@@ -43862,7 +44100,7 @@ function runBackgroundShortcut(input, status = backgroundStatus()) {
|
|
|
43862
44100
|
{ mode: 384 }
|
|
43863
44101
|
);
|
|
43864
44102
|
try {
|
|
43865
|
-
|
|
44103
|
+
execFileSync9("/usr/bin/shortcuts", ["run", status.identifier, "--input-path", file], {
|
|
43866
44104
|
encoding: "utf8",
|
|
43867
44105
|
timeout: 6e4,
|
|
43868
44106
|
maxBuffer: 1024 * 1024,
|
|
@@ -44065,7 +44303,7 @@ function createMarkdownNote(manager, request, run = runBackgroundShortcut) {
|
|
|
44065
44303
|
throw new Error(
|
|
44066
44304
|
`Install the supplied "${status.shortcut}" Shortcut once; Shortcuts must list it exactly once`
|
|
44067
44305
|
);
|
|
44068
|
-
const segments = (
|
|
44306
|
+
const segments = (path6) => JSON.stringify(splitFolderPath(path6).map((part) => part.toLocaleLowerCase()));
|
|
44069
44307
|
if (request.folder) {
|
|
44070
44308
|
const wanted = segments(request.folder);
|
|
44071
44309
|
if (!manager.listAccounts().some(
|
|
@@ -44154,7 +44392,7 @@ ${request.content}`
|
|
|
44154
44392
|
}
|
|
44155
44393
|
|
|
44156
44394
|
// src/services/capabilityMatrix.ts
|
|
44157
|
-
import { execFileSync as
|
|
44395
|
+
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
44158
44396
|
import { release } from "node:os";
|
|
44159
44397
|
var MACOS_SHORTCUTS_CLI = "12.0";
|
|
44160
44398
|
var MACOS_MARKDOWN_IMPORT = "26.0";
|
|
@@ -44361,7 +44599,7 @@ function evaluateFeatures(env, features = FEATURES) {
|
|
|
44361
44599
|
function readMacOSVersion() {
|
|
44362
44600
|
if (process.platform !== "darwin") return null;
|
|
44363
44601
|
try {
|
|
44364
|
-
const out =
|
|
44602
|
+
const out = execFileSync10("/usr/bin/sw_vers", ["-productVersion"], {
|
|
44365
44603
|
encoding: "utf8",
|
|
44366
44604
|
timeout: 3e3,
|
|
44367
44605
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -44394,9 +44632,9 @@ function getCapabilityMatrix(probe = probeCapabilityEnvironment) {
|
|
|
44394
44632
|
return evaluateFeatures(probe());
|
|
44395
44633
|
}
|
|
44396
44634
|
function formatCapabilityMatrix(matrix) {
|
|
44397
|
-
const
|
|
44635
|
+
const os6 = matrix.runtimeOS;
|
|
44398
44636
|
const lines = [
|
|
44399
|
-
`Feature matrix (macOS ${
|
|
44637
|
+
`Feature matrix (macOS ${os6.macOSVersion ?? "unknown"}, Darwin ${os6.darwinRelease}):`
|
|
44400
44638
|
];
|
|
44401
44639
|
for (const [name, status] of Object.entries(matrix.features)) {
|
|
44402
44640
|
lines.push(
|
|
@@ -44514,19 +44752,19 @@ function formatDoctorReport(r) {
|
|
|
44514
44752
|
}
|
|
44515
44753
|
|
|
44516
44754
|
// src/services/fileConfig.ts
|
|
44517
|
-
import { existsSync as
|
|
44518
|
-
import { join as
|
|
44519
|
-
import { homedir as
|
|
44755
|
+
import { existsSync as existsSync8, readFileSync as readFileSync2 } from "fs";
|
|
44756
|
+
import { join as join11 } from "path";
|
|
44757
|
+
import { homedir as homedir9 } from "os";
|
|
44520
44758
|
function fileConfigPath(env = process.env) {
|
|
44521
44759
|
const override = env.APPLE_NOTES_MCP_CONFIG_FILE;
|
|
44522
44760
|
if (override && override.trim()) return override.trim();
|
|
44523
|
-
return
|
|
44761
|
+
return join11(homedir9(), "Library", "Application Support", "apple-notes-mcp", "config.json");
|
|
44524
44762
|
}
|
|
44525
|
-
function loadFileConfig(env = process.env,
|
|
44763
|
+
function loadFileConfig(env = process.env, path6 = fileConfigPath(env)) {
|
|
44526
44764
|
const applied = [];
|
|
44527
44765
|
try {
|
|
44528
|
-
if (!
|
|
44529
|
-
const parsed = JSON.parse(readFileSync2(
|
|
44766
|
+
if (!existsSync8(path6)) return applied;
|
|
44767
|
+
const parsed = JSON.parse(readFileSync2(path6, "utf8"));
|
|
44530
44768
|
if (!parsed || typeof parsed !== "object") return applied;
|
|
44531
44769
|
for (const [k, v] of Object.entries(parsed)) {
|
|
44532
44770
|
if (typeof v !== "string") continue;
|
|
@@ -44536,7 +44774,7 @@ function loadFileConfig(env = process.env, path5 = fileConfigPath(env)) {
|
|
|
44536
44774
|
}
|
|
44537
44775
|
}
|
|
44538
44776
|
} catch (e) {
|
|
44539
|
-
console.error(`Failed to load apple-notes-mcp config file ${
|
|
44777
|
+
console.error(`Failed to load apple-notes-mcp config file ${path6}: ${String(e)}`);
|
|
44540
44778
|
}
|
|
44541
44779
|
return applied;
|
|
44542
44780
|
}
|
|
@@ -44727,6 +44965,77 @@ function withJsonSchema2020_12(transport2) {
|
|
|
44727
44965
|
return transport2;
|
|
44728
44966
|
}
|
|
44729
44967
|
|
|
44968
|
+
// src/utils/errorCodes.ts
|
|
44969
|
+
var CodedError = class extends Error {
|
|
44970
|
+
envelope;
|
|
44971
|
+
constructor(message, envelope) {
|
|
44972
|
+
super(message);
|
|
44973
|
+
this.name = "CodedError";
|
|
44974
|
+
this.envelope = envelope;
|
|
44975
|
+
}
|
|
44976
|
+
};
|
|
44977
|
+
var RULES = [
|
|
44978
|
+
{ code: "timeout_indeterminate", pattern: /timed out|\bETIMEDOUT\b/i },
|
|
44979
|
+
{
|
|
44980
|
+
code: "verification_failed",
|
|
44981
|
+
pattern: /\buncertain\b|may have (?:been|succeeded)|\baccepted (?:the|an) \w+, but|\bnot verified\b|Do not retry automatically|before any retry|read (?:the )?(?:exact )?(?:note|ID)\b[^.]*before retr/i
|
|
44982
|
+
},
|
|
44983
|
+
{
|
|
44984
|
+
code: "revision_conflict",
|
|
44985
|
+
pattern: /changed after it was read|revision changed|(?:pinned|note) state changed|changed during (?:read|preflight)|changed; read (?:it|the note) again|rich text do not match/i
|
|
44986
|
+
},
|
|
44987
|
+
{ code: "full_disk_access_missing", pattern: /Full Disk Access/i },
|
|
44988
|
+
{
|
|
44989
|
+
code: "shortcut_not_installed",
|
|
44990
|
+
pattern: /(?:Install|Import) the supplied|no such shortcut|Run apple-notes-mcp setup/i
|
|
44991
|
+
},
|
|
44992
|
+
{
|
|
44993
|
+
code: "not_found",
|
|
44994
|
+
pattern: /not found|does not exist|\bNo \w+ found\b|does not contain any checklist items|Can[’']t get (?:note|folder|account|attachment)|Cannot find note|Scope is absent/i
|
|
44995
|
+
},
|
|
44996
|
+
{ code: "ambiguous", pattern: /ambiguous|more than one|exactly once|Duplicate note IDs/i },
|
|
44997
|
+
{
|
|
44998
|
+
code: "notes_unavailable",
|
|
44999
|
+
pattern: /Notes\.app is (?:not responding|busy)|Lost connection to Notes|isn't running/i
|
|
45000
|
+
},
|
|
45001
|
+
{
|
|
45002
|
+
code: "unsupported",
|
|
45003
|
+
pattern: /password-protected|Locked notes|unsupported|not supported|is blocked|blocked because|has not passed live|refuses to sign|No (?:supported )?background|only one where|Real Notes link unavailable/i
|
|
45004
|
+
},
|
|
45005
|
+
{
|
|
45006
|
+
code: "validation_error",
|
|
45007
|
+
pattern: /\bis required\b|required\b|\bInvalid\b|\bmust\b|\bProvide\b|No (?:note IDs|reviewed notes) provided|response limit|Refusing to write|cannot be resolved|at most \d|between \d+ and \d+|supports the (?:end|default)|Use a distinctive|Use create-table|too large|equal cell counts|Folder path is empty/i
|
|
45008
|
+
}
|
|
45009
|
+
];
|
|
45010
|
+
var NOTHING_WRITTEN = /nothing (?:was )?(?:changed|created|written)|no replacement started|No content was (?:replaced|appended)|Nothing was created/i;
|
|
45011
|
+
var WRITE_ACCEPTED = /\baccepted (?:the|an) \w+, but/i;
|
|
45012
|
+
function classifyError(message, cause) {
|
|
45013
|
+
if (cause instanceof CodedError) return { ...cause.envelope };
|
|
45014
|
+
const text = cause instanceof Error && cause.message && !message.includes(cause.message) ? `${message}
|
|
45015
|
+
${cause.message}` : message;
|
|
45016
|
+
const timeoutCause = typeof cause === "object" && cause !== null && cause.code === "ETIMEDOUT";
|
|
45017
|
+
let code = "operation_failed";
|
|
45018
|
+
if (timeoutCause) code = "timeout_indeterminate";
|
|
45019
|
+
else if (isPermissionDenied(text)) code = "permission_denied";
|
|
45020
|
+
else code = RULES.find((rule) => rule.pattern.test(text))?.code ?? "operation_failed";
|
|
45021
|
+
const envelope = { code };
|
|
45022
|
+
if (code === "timeout_indeterminate" || code === "verification_failed") {
|
|
45023
|
+
envelope.indeterminate = true;
|
|
45024
|
+
if (WRITE_ACCEPTED.test(text)) envelope.committed = true;
|
|
45025
|
+
} else if (code === "revision_conflict" || NOTHING_WRITTEN.test(text)) {
|
|
45026
|
+
envelope.committed = false;
|
|
45027
|
+
envelope.indeterminate = false;
|
|
45028
|
+
}
|
|
45029
|
+
return envelope;
|
|
45030
|
+
}
|
|
45031
|
+
function errorResult(message, cause) {
|
|
45032
|
+
return {
|
|
45033
|
+
content: [{ type: "text", text: message }],
|
|
45034
|
+
structuredContent: classifyError(message, cause),
|
|
45035
|
+
isError: true
|
|
45036
|
+
};
|
|
45037
|
+
}
|
|
45038
|
+
|
|
44730
45039
|
// src/utils/shutdown.ts
|
|
44731
45040
|
var SHUTDOWN_DRAIN_TIMEOUT_MS = 2e3;
|
|
44732
45041
|
function createShutdown(stream, exit, timeoutMs = SHUTDOWN_DRAIN_TIMEOUT_MS) {
|
|
@@ -44976,9 +45285,14 @@ import {
|
|
|
44976
45285
|
writeFileSync as writeFileSync3
|
|
44977
45286
|
} from "node:fs";
|
|
44978
45287
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
44979
|
-
import { basename, isAbsolute as isAbsolute2, join as
|
|
44980
|
-
var noteId =
|
|
45288
|
+
import { basename, isAbsolute as isAbsolute2, join as join12 } from "node:path";
|
|
45289
|
+
var noteId = exactIdInput(
|
|
45290
|
+
"ICNote",
|
|
45291
|
+
/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
|
|
45292
|
+
NOTE_ID_MESSAGE
|
|
45293
|
+
);
|
|
44981
45294
|
var revision = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
45295
|
+
var folderId = external_exports.string().max(2e3).transform(looseIdTransform("ICFolder"));
|
|
44982
45296
|
function readSnapshot(manager, id2) {
|
|
44983
45297
|
const note = manager.getNoteById(id2);
|
|
44984
45298
|
if (!note) throw new Error("Note not found");
|
|
@@ -45014,9 +45328,9 @@ function assertExistingContentPreserved(before, after) {
|
|
|
45014
45328
|
throw new Error("Existing native object content or presentation changed");
|
|
45015
45329
|
}
|
|
45016
45330
|
}
|
|
45017
|
-
function localAttachment(
|
|
45018
|
-
if (!isAbsolute2(
|
|
45019
|
-
const descriptor = openSync(
|
|
45331
|
+
function localAttachment(path6) {
|
|
45332
|
+
if (!isAbsolute2(path6)) throw new Error("An absolute local file path is required");
|
|
45333
|
+
const descriptor = openSync(path6, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
45020
45334
|
try {
|
|
45021
45335
|
const stat = fstatSync(descriptor);
|
|
45022
45336
|
if (!stat.isFile() || stat.size === 0 || stat.size > 64 * 1024 * 1024)
|
|
@@ -45047,15 +45361,7 @@ function registerDirectOperations(server2, manager) {
|
|
|
45047
45361
|
structuredContent: result
|
|
45048
45362
|
};
|
|
45049
45363
|
} catch (error2) {
|
|
45050
|
-
return
|
|
45051
|
-
content: [
|
|
45052
|
-
{
|
|
45053
|
-
type: "text",
|
|
45054
|
-
text: error2 instanceof Error ? error2.message : String(error2)
|
|
45055
|
-
}
|
|
45056
|
-
],
|
|
45057
|
-
isError: true
|
|
45058
|
-
};
|
|
45364
|
+
return errorResult(error2 instanceof Error ? error2.message : String(error2), error2);
|
|
45059
45365
|
}
|
|
45060
45366
|
})
|
|
45061
45367
|
);
|
|
@@ -45063,15 +45369,18 @@ function registerDirectOperations(server2, manager) {
|
|
|
45063
45369
|
tool(
|
|
45064
45370
|
"get-folder-by-id",
|
|
45065
45371
|
"Use when: reading the exact folder name and parent before a guarded rename.\nReturns: folder id, current name, and parent id.\nDo not use when: listing folders by path (list-folders).\nSafety: read-only.",
|
|
45066
|
-
{ id:
|
|
45067
|
-
({ id: id2 }) =>
|
|
45372
|
+
{ id: folderId },
|
|
45373
|
+
({ id: id2 }) => {
|
|
45374
|
+
const folder = manager.getFolderById(id2);
|
|
45375
|
+
return { ...folder, ...lookupStableIdentifiers([folder.id], "ICFolder").get(folder.id) };
|
|
45376
|
+
},
|
|
45068
45377
|
true
|
|
45069
45378
|
);
|
|
45070
45379
|
tool(
|
|
45071
45380
|
"rename-folder",
|
|
45072
45381
|
"Use when: renaming one previously read folder in place.\nReturns: the unchanged folder id, new name, and parent id after readback.\nDo not use when: creating, moving, or deleting a folder.\nSafety: requires the expected current name and parent; refuses stale metadata and sibling conflicts.",
|
|
45073
45382
|
{
|
|
45074
|
-
id:
|
|
45383
|
+
id: folderId,
|
|
45075
45384
|
expectedName: external_exports.string().max(1e3),
|
|
45076
45385
|
expectedParentId: external_exports.string().max(2e3),
|
|
45077
45386
|
newName: external_exports.string().min(1).max(1e3)
|
|
@@ -45085,13 +45394,13 @@ function registerDirectOperations(server2, manager) {
|
|
|
45085
45394
|
"add-attachment",
|
|
45086
45395
|
"Use when: adding one local file to an exact note without replacing its body.\nReturns: the new attachment id, byte count, and post-write content hash after exact byte verification.\nDo not use when: reading or exporting an existing attachment.\nSafety: requires a fresh rich revision, copies at most 64 MiB through a private temporary file, never retries insertion, and verifies existing content plus fetched bytes.",
|
|
45087
45396
|
{ id: noteId, expectedContentHash: revision, path: external_exports.string().min(1).max(4096) },
|
|
45088
|
-
({ id: id2, expectedContentHash, path:
|
|
45397
|
+
({ id: id2, expectedContentHash, path: path6 }) => {
|
|
45089
45398
|
const before = readSnapshot(manager, id2);
|
|
45090
45399
|
if (before.hash !== expectedContentHash) throw new Error("Note revision changed");
|
|
45091
|
-
const bytes = localAttachment(
|
|
45400
|
+
const bytes = localAttachment(path6);
|
|
45092
45401
|
const beforeAttachments = manager.listAttachmentsById(id2);
|
|
45093
|
-
const directory = mkdtempSync4(
|
|
45094
|
-
const temporaryFile =
|
|
45402
|
+
const directory = mkdtempSync4(join12(tmpdir4(), "notes-attachment-add-"));
|
|
45403
|
+
const temporaryFile = join12(directory, basename(path6));
|
|
45095
45404
|
try {
|
|
45096
45405
|
writeFileSync3(temporaryFile, bytes, { mode: 384 });
|
|
45097
45406
|
if (readSnapshot(manager, id2).hash !== before.hash)
|
|
@@ -45143,7 +45452,11 @@ function registerDirectOperations(server2, manager) {
|
|
|
45143
45452
|
}
|
|
45144
45453
|
|
|
45145
45454
|
// src/tools/nativeTagsBridge.ts
|
|
45146
|
-
var noteId2 =
|
|
45455
|
+
var noteId2 = exactIdInput(
|
|
45456
|
+
"ICNote",
|
|
45457
|
+
/^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i,
|
|
45458
|
+
NOTE_ID_MESSAGE
|
|
45459
|
+
);
|
|
45147
45460
|
var revision2 = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
45148
45461
|
function registerNativeTagsBridge(server2, manager) {
|
|
45149
45462
|
function tool(name, description, inputSchema, handler) {
|
|
@@ -45162,15 +45475,7 @@ function registerNativeTagsBridge(server2, manager) {
|
|
|
45162
45475
|
structuredContent: result
|
|
45163
45476
|
};
|
|
45164
45477
|
} catch (error2) {
|
|
45165
|
-
return
|
|
45166
|
-
content: [
|
|
45167
|
-
{
|
|
45168
|
-
type: "text",
|
|
45169
|
-
text: error2 instanceof Error ? error2.message : String(error2)
|
|
45170
|
-
}
|
|
45171
|
-
],
|
|
45172
|
-
isError: true
|
|
45173
|
-
};
|
|
45478
|
+
return errorResult(error2 instanceof Error ? error2.message : String(error2), error2);
|
|
45174
45479
|
}
|
|
45175
45480
|
})
|
|
45176
45481
|
);
|
|
@@ -45263,7 +45568,7 @@ function requireValidated(name) {
|
|
|
45263
45568
|
UNAVAILABLE[name] || LIVE_VALIDATION_BLOCKERS[name] || `${name} has not passed live background validation in this build; see get-capabilities`
|
|
45264
45569
|
);
|
|
45265
45570
|
}
|
|
45266
|
-
var id =
|
|
45571
|
+
var id = exactIdInput("ICNote", /^x-coredata:\/\/[0-9a-f-]+\/ICNote\/p\d+$/i, NOTE_ID_MESSAGE);
|
|
45267
45572
|
var revision3 = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
45268
45573
|
var common = {
|
|
45269
45574
|
id,
|
|
@@ -45357,15 +45662,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
45357
45662
|
structuredContent: result
|
|
45358
45663
|
};
|
|
45359
45664
|
} catch (error2) {
|
|
45360
|
-
return
|
|
45361
|
-
content: [
|
|
45362
|
-
{
|
|
45363
|
-
type: "text",
|
|
45364
|
-
text: error2 instanceof Error ? error2.message : "Operation failed"
|
|
45365
|
-
}
|
|
45366
|
-
],
|
|
45367
|
-
isError: true
|
|
45368
|
-
};
|
|
45665
|
+
return errorResult(error2 instanceof Error ? error2.message : "Operation failed", error2);
|
|
45369
45666
|
}
|
|
45370
45667
|
})
|
|
45371
45668
|
);
|
|
@@ -45649,7 +45946,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
45649
45946
|
|
|
45650
45947
|
// src/setupShortcuts.ts
|
|
45651
45948
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
45652
|
-
import { existsSync as
|
|
45949
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
45653
45950
|
import { release as release2 } from "node:os";
|
|
45654
45951
|
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
45655
45952
|
import { fileURLToPath } from "node:url";
|
|
@@ -45669,16 +45966,16 @@ var shortcutFiles = [
|
|
|
45669
45966
|
];
|
|
45670
45967
|
function setupShortcuts(checkOnly, dependencies = {}) {
|
|
45671
45968
|
const status = dependencies.status || nativeTagsStatus;
|
|
45672
|
-
const exists = dependencies.exists ||
|
|
45673
|
-
const open = dependencies.open || ((
|
|
45674
|
-
const result = spawnSync2("/usr/bin/open", [
|
|
45969
|
+
const exists = dependencies.exists || existsSync9;
|
|
45970
|
+
const open = dependencies.open || ((path6) => {
|
|
45971
|
+
const result = spawnSync2("/usr/bin/open", [path6], { encoding: "utf8" });
|
|
45675
45972
|
return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
|
|
45676
45973
|
});
|
|
45677
45974
|
const baseDirectory = dependencies.baseDirectory || resolve2(dirname2(fileURLToPath(import.meta.url)), "../shortcuts");
|
|
45678
45975
|
const osRelease = (dependencies.osRelease || release2)();
|
|
45679
45976
|
const darwinMajor = Number.parseInt(osRelease.split(".")[0], 10);
|
|
45680
45977
|
const items = shortcutFiles.map(({ name, file, optional: optional2 }) => {
|
|
45681
|
-
const
|
|
45978
|
+
const path6 = resolve2(baseDirectory, file);
|
|
45682
45979
|
let installed = false;
|
|
45683
45980
|
let identifier;
|
|
45684
45981
|
let error2;
|
|
@@ -45694,9 +45991,9 @@ function setupShortcuts(checkOnly, dependencies = {}) {
|
|
|
45694
45991
|
if (!installed && !checkOnly) {
|
|
45695
45992
|
if (optional2 && !(darwinMajor >= MARKDOWN_MIN_DARWIN_MAJOR))
|
|
45696
45993
|
skipped = `requires macOS 26 or later (this Mac reports Darwin ${osRelease})`;
|
|
45697
|
-
else if (!exists(
|
|
45994
|
+
else if (!exists(path6)) error2 = `Packaged Shortcut is missing: ${path6}`;
|
|
45698
45995
|
else {
|
|
45699
|
-
const result = open(
|
|
45996
|
+
const result = open(path6);
|
|
45700
45997
|
opened = result.ok;
|
|
45701
45998
|
if (!result.ok) error2 = result.error || `Could not open ${file}`;
|
|
45702
45999
|
}
|
|
@@ -45705,7 +46002,7 @@ function setupShortcuts(checkOnly, dependencies = {}) {
|
|
|
45705
46002
|
name,
|
|
45706
46003
|
installed,
|
|
45707
46004
|
identifier,
|
|
45708
|
-
file:
|
|
46005
|
+
file: path6,
|
|
45709
46006
|
opened,
|
|
45710
46007
|
...optional2 ? { optional: optional2 } : {},
|
|
45711
46008
|
...skipped ? { skipped } : {},
|
|
@@ -45759,11 +46056,8 @@ function successResponse(message, structured) {
|
|
|
45759
46056
|
if (structured) res.structuredContent = structured;
|
|
45760
46057
|
return res;
|
|
45761
46058
|
}
|
|
45762
|
-
function errorResponse(message) {
|
|
45763
|
-
return
|
|
45764
|
-
content: [{ type: "text", text: message }],
|
|
45765
|
-
isError: true
|
|
45766
|
-
};
|
|
46059
|
+
function errorResponse(message, cause) {
|
|
46060
|
+
return errorResult(message, cause);
|
|
45767
46061
|
}
|
|
45768
46062
|
function withErrorHandling(handler, errorPrefix) {
|
|
45769
46063
|
return async (params) => {
|
|
@@ -45771,7 +46065,7 @@ function withErrorHandling(handler, errorPrefix) {
|
|
|
45771
46065
|
return handler(params);
|
|
45772
46066
|
} catch (error2) {
|
|
45773
46067
|
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
45774
|
-
return errorResponse(`${errorPrefix}: ${message}
|
|
46068
|
+
return errorResponse(`${errorPrefix}: ${message}`, error2);
|
|
45775
46069
|
}
|
|
45776
46070
|
};
|
|
45777
46071
|
}
|
|
@@ -45794,10 +46088,25 @@ var noteTitleSchema = {
|
|
|
45794
46088
|
"Account name (defaults to Notes.app's default account; exact or unique-prefix match)"
|
|
45795
46089
|
)
|
|
45796
46090
|
};
|
|
45797
|
-
var
|
|
45798
|
-
|
|
45799
|
-
|
|
45800
|
-
|
|
46091
|
+
var NOTE_COREDATA_ID = /^x-coredata:\/\/[0-9A-Fa-f-]+\/ICNote\/p\d+$/;
|
|
46092
|
+
var NOTE_ID_FORMS = "x-coredata id, Notes UUID, or numeric key";
|
|
46093
|
+
var noteIdInput = exactIdInput("ICNote", NOTE_COREDATA_ID, NOTE_ID_MESSAGE, {
|
|
46094
|
+
maxLength: MAX.ID
|
|
46095
|
+
}).describe(`Exact note ID returned by search-notes, list-notes, or create-note: ${NOTE_ID_FORMS}`);
|
|
46096
|
+
var noteIdArrayInput = exactIdArrayInput("ICNote", NOTE_COREDATA_ID, NOTE_ID_MESSAGE, {
|
|
46097
|
+
maxLength: MAX.ID,
|
|
46098
|
+
maxItems: MAX.BATCH_IDS
|
|
46099
|
+
});
|
|
46100
|
+
var looseNoteId = (base) => base.max(MAX.ID).transform(looseIdTransform("ICNote"));
|
|
46101
|
+
var looseFolderId = (base) => base.max(MAX.ID).transform(looseIdTransform("ICFolder"));
|
|
46102
|
+
var noteIdentifierOutput = {
|
|
46103
|
+
identifier: external_exports.string().optional(),
|
|
46104
|
+
folderIdentifier: external_exports.string().optional(),
|
|
46105
|
+
accountIdentifier: external_exports.string().optional()
|
|
46106
|
+
};
|
|
46107
|
+
function noteIdentifiers(id2) {
|
|
46108
|
+
return lookupStableIdentifiers([id2], "ICNote").get(id2) ?? {};
|
|
46109
|
+
}
|
|
45801
46110
|
var expectedContentHashInput = external_exports.string().regex(/^sha256:[a-f0-9]{64}$/, "expectedContentHash must come from get-note-content").describe(
|
|
45802
46111
|
"Revision token returned by get-note-content for this exact ID. The mutation stops if the note changed since that read."
|
|
45803
46112
|
);
|
|
@@ -45973,7 +46282,7 @@ ${syncWarnings.join(" ")}` : "";
|
|
|
45973
46282
|
return successResponse(
|
|
45974
46283
|
`Found ${notes.length} notes (searched ${searchType}${folderInfo}${dateInfo}${limitInfo}):
|
|
45975
46284
|
${noteList}${truncationNote}${syncNote}`,
|
|
45976
|
-
{ notes, count: notes.length }
|
|
46285
|
+
{ notes: withStableIdentifiers(notes, "ICNote"), count: notes.length }
|
|
45977
46286
|
);
|
|
45978
46287
|
}, "Error searching notes")
|
|
45979
46288
|
);
|
|
@@ -46057,7 +46366,7 @@ registerTool(
|
|
|
46057
46366
|
{
|
|
46058
46367
|
description: "Use when: reading the full body text of one known note, by id (preferred) or title.\nReturns: the exact note id, content, contentHash revision token, parsed hashtags, nativeTags, restored links, richContentComplete/writable, and strippedImages/truncated when the body was capped. Read the warning when writable is false.\nDo not use when: you only need metadata (get-note-details) or Markdown with checklist state (get-note-markdown).\nNote: password-protected notes must be unlocked in Notes.app first.\nSafety: inline images larger than APPLE_NOTES_MCP_MAX_INLINE_IMAGE_BYTES (default 256 KB) are replaced with '[inline image omitted: ...]' text placeholders, so the returned body is lossy whenever truncated is true. Mutations refuse attachment-bearing notes; edit those in Notes.app.",
|
|
46059
46368
|
inputSchema: {
|
|
46060
|
-
id: external_exports.string()
|
|
46369
|
+
id: looseNoteId(external_exports.string()).optional().describe(`Note ID (preferred - more reliable than title): ${NOTE_ID_FORMS}`),
|
|
46061
46370
|
title: external_exports.string().max(MAX.TITLE).optional().describe("Note title (use id instead when available)"),
|
|
46062
46371
|
account: external_exports.string().max(MAX.ACCOUNT).optional().describe(
|
|
46063
46372
|
"Account name (defaults to Notes.app's default account; exact or unique-prefix match, ignored if id is provided)"
|
|
@@ -46065,6 +46374,7 @@ registerTool(
|
|
|
46065
46374
|
},
|
|
46066
46375
|
outputSchema: {
|
|
46067
46376
|
id: external_exports.string().optional(),
|
|
46377
|
+
...noteIdentifierOutput,
|
|
46068
46378
|
title: external_exports.string().optional(),
|
|
46069
46379
|
content: external_exports.string().optional(),
|
|
46070
46380
|
contentHash: external_exports.string().optional(),
|
|
@@ -46104,6 +46414,7 @@ registerTool(
|
|
|
46104
46414
|
const warning2 = [strippedImagesWarning(stripped2), rich2.warning].filter(Boolean).join("\n\n");
|
|
46105
46415
|
return successResponse(warning2 ? content2 + warning2 : content2, {
|
|
46106
46416
|
id: id2,
|
|
46417
|
+
...noteIdentifiers(id2),
|
|
46107
46418
|
title: note2.title,
|
|
46108
46419
|
content: content2,
|
|
46109
46420
|
contentHash: richContentHash(rawContent2, rich2),
|
|
@@ -46145,6 +46456,7 @@ registerTool(
|
|
|
46145
46456
|
const warning = [strippedImagesWarning(stripped), rich.warning].filter(Boolean).join("\n\n");
|
|
46146
46457
|
return successResponse(warning ? content + warning : content, {
|
|
46147
46458
|
id: note.id,
|
|
46459
|
+
...noteIdentifiers(note.id),
|
|
46148
46460
|
title,
|
|
46149
46461
|
content,
|
|
46150
46462
|
contentHash: richContentHash(rawContent, rich),
|
|
@@ -46169,7 +46481,7 @@ registerTool(
|
|
|
46169
46481
|
{
|
|
46170
46482
|
description: "Use when: reading one note's body as plain text with no HTML, by id (preferred) or title.\nReturns: the note's plaintext exactly as Notes exposes it.\nDo not use when: you need the HTML body (get-note-content) or Markdown with checklist state (get-note-markdown).\nNote: this reads the note's native plaintext property, so it skips the HTML-to-text conversion; password-protected notes must be unlocked in Notes.app first.",
|
|
46171
46483
|
inputSchema: {
|
|
46172
|
-
id: external_exports.string()
|
|
46484
|
+
id: looseNoteId(external_exports.string()).optional().describe(`Note ID (preferred - more reliable than title): ${NOTE_ID_FORMS}`),
|
|
46173
46485
|
title: external_exports.string().max(MAX.TITLE).optional().describe("Note title (use id instead when available)"),
|
|
46174
46486
|
account: external_exports.string().max(MAX.ACCOUNT).optional().describe(
|
|
46175
46487
|
"Account name (defaults to Notes.app's default account; exact or unique-prefix match, ignored if id is provided)"
|
|
@@ -46221,7 +46533,9 @@ registerTool(
|
|
|
46221
46533
|
{
|
|
46222
46534
|
description: "Use when: you have a note id and need its metadata only.\nReturns: id, title, created, modified, shared, passwordProtected.\nDo not use when: you need the body text (get-note-content) or only have a title (get-note-details).",
|
|
46223
46535
|
inputSchema: {
|
|
46224
|
-
id: external_exports.string().min(1, "Note ID is required").
|
|
46536
|
+
id: looseNoteId(external_exports.string().min(1, "Note ID is required")).describe(
|
|
46537
|
+
`Note ID: ${NOTE_ID_FORMS}`
|
|
46538
|
+
)
|
|
46225
46539
|
},
|
|
46226
46540
|
outputSchema: {
|
|
46227
46541
|
id: external_exports.string().optional(),
|
|
@@ -46229,7 +46543,8 @@ registerTool(
|
|
|
46229
46543
|
created: external_exports.string().optional(),
|
|
46230
46544
|
modified: external_exports.string().optional(),
|
|
46231
46545
|
shared: external_exports.boolean().optional(),
|
|
46232
|
-
passwordProtected: external_exports.boolean().optional()
|
|
46546
|
+
passwordProtected: external_exports.boolean().optional(),
|
|
46547
|
+
...noteIdentifierOutput
|
|
46233
46548
|
}
|
|
46234
46549
|
},
|
|
46235
46550
|
withErrorHandling(({ id: id2 }) => {
|
|
@@ -46243,7 +46558,8 @@ registerTool(
|
|
|
46243
46558
|
created: note.created.toISOString(),
|
|
46244
46559
|
modified: note.modified.toISOString(),
|
|
46245
46560
|
shared: note.shared,
|
|
46246
|
-
passwordProtected: note.passwordProtected
|
|
46561
|
+
passwordProtected: note.passwordProtected,
|
|
46562
|
+
...noteIdentifiers(note.id)
|
|
46247
46563
|
};
|
|
46248
46564
|
return successResponse(JSON.stringify(metadata, null, 2), metadata);
|
|
46249
46565
|
}, "Error retrieving note")
|
|
@@ -46260,7 +46576,8 @@ registerTool(
|
|
|
46260
46576
|
modified: external_exports.string().optional(),
|
|
46261
46577
|
shared: external_exports.boolean().optional(),
|
|
46262
46578
|
passwordProtected: external_exports.boolean().optional(),
|
|
46263
|
-
account: external_exports.string().optional()
|
|
46579
|
+
account: external_exports.string().optional(),
|
|
46580
|
+
...noteIdentifierOutput
|
|
46264
46581
|
}
|
|
46265
46582
|
},
|
|
46266
46583
|
withErrorHandling(({ title, account }) => {
|
|
@@ -46275,7 +46592,8 @@ registerTool(
|
|
|
46275
46592
|
modified: note.modified.toISOString(),
|
|
46276
46593
|
shared: note.shared,
|
|
46277
46594
|
passwordProtected: note.passwordProtected,
|
|
46278
|
-
account: note.account
|
|
46595
|
+
account: note.account,
|
|
46596
|
+
...noteIdentifiers(note.id)
|
|
46279
46597
|
};
|
|
46280
46598
|
return successResponse(JSON.stringify(metadata, null, 2), metadata);
|
|
46281
46599
|
}, "Error retrieving note details")
|
|
@@ -46285,7 +46603,9 @@ registerTool(
|
|
|
46285
46603
|
{
|
|
46286
46604
|
description: "Use when: the user wants to reveal a known note in Notes.app by id.\nReturns: confirmation that Notes.app accepted the show command.\nDo not use when: you only need note content (get-note-content) or metadata (get-note-by-id).\nNote: this opens or focuses the Notes UI.",
|
|
46287
46605
|
inputSchema: {
|
|
46288
|
-
id: external_exports.string().min(1, "Note ID is required").
|
|
46606
|
+
id: looseNoteId(external_exports.string().min(1, "Note ID is required")).describe(
|
|
46607
|
+
`Note ID: ${NOTE_ID_FORMS}`
|
|
46608
|
+
),
|
|
46289
46609
|
separately: external_exports.boolean().optional().describe("Open in a separate note window when supported by Notes.app")
|
|
46290
46610
|
},
|
|
46291
46611
|
outputSchema: {
|
|
@@ -46306,7 +46626,7 @@ registerTool(
|
|
|
46306
46626
|
{
|
|
46307
46627
|
description: "Use when: you need the notes:// deep-link URL for a note so it can be stored in a Reminders task, shared, or opened directly.\nReturns: a notes://showNote?identifier=<uuid> URL that opens the note in Notes.app on iOS and macOS.\nDo not use when: you only need the note's CoreData id (get-note-by-id) or want to reveal the note on screen (show-note).\nNote: the primary path reads the note's identifier from the Notes database, so it needs Full Disk Access for the app that launches this server; macOS 12-15 can fall back to the AppleScript 'note link' property, which macOS 26+ no longer exposes. Password-protected notes cannot be linked.",
|
|
46308
46628
|
inputSchema: {
|
|
46309
|
-
id: external_exports.string()
|
|
46629
|
+
id: looseNoteId(external_exports.string()).optional().describe(`Note ID (preferred - more reliable than title): ${NOTE_ID_FORMS}`),
|
|
46310
46630
|
title: external_exports.string().max(MAX.TITLE).optional().describe("Note title (use id instead when available)"),
|
|
46311
46631
|
account: external_exports.string().max(MAX.ACCOUNT).optional().describe("Account containing the note (ignored if id is provided)")
|
|
46312
46632
|
},
|
|
@@ -46361,7 +46681,9 @@ registerTool(
|
|
|
46361
46681
|
{
|
|
46362
46682
|
description: "Use when: the user wants to reveal a known folder in Notes.app by id.\nReturns: confirmation that Notes.app accepted the show command.\nDo not use when: you only need the folder list (list-folders).\nNote: this opens or focuses the Notes UI. Get the id from list-folders.",
|
|
46363
46683
|
inputSchema: {
|
|
46364
|
-
id: external_exports.string().min(1, "Folder ID is required").
|
|
46684
|
+
id: looseFolderId(external_exports.string().min(1, "Folder ID is required")).describe(
|
|
46685
|
+
"Folder ID from list-folders: x-coredata id, Notes UUID, or numeric key"
|
|
46686
|
+
),
|
|
46365
46687
|
separately: external_exports.boolean().optional().describe("Open in a separate window when supported by Notes.app")
|
|
46366
46688
|
},
|
|
46367
46689
|
outputSchema: {
|
|
@@ -46905,7 +47227,15 @@ registerTool(
|
|
|
46905
47227
|
limit: external_exports.number().int().positive().optional().describe("Maximum number of notes to return")
|
|
46906
47228
|
},
|
|
46907
47229
|
outputSchema: {
|
|
46908
|
-
notes: external_exports.array(
|
|
47230
|
+
notes: external_exports.array(
|
|
47231
|
+
external_exports.object({
|
|
47232
|
+
title: external_exports.string(),
|
|
47233
|
+
id: external_exports.string(),
|
|
47234
|
+
identifier: external_exports.string().optional(),
|
|
47235
|
+
folderIdentifier: external_exports.string().optional(),
|
|
47236
|
+
accountIdentifier: external_exports.string().optional()
|
|
47237
|
+
})
|
|
47238
|
+
).optional(),
|
|
46909
47239
|
count: external_exports.number().optional()
|
|
46910
47240
|
}
|
|
46911
47241
|
},
|
|
@@ -46942,7 +47272,7 @@ ${syncWarnings.join(" ")}` : "";
|
|
|
46942
47272
|
return successResponse(
|
|
46943
47273
|
`Found ${notes.length} notes${location}${acct}${dateInfo}${limitInfo}:
|
|
46944
47274
|
${noteList}${syncNote}`,
|
|
46945
|
-
{ notes, count: notes.length }
|
|
47275
|
+
{ notes: withStableIdentifiers(notes, "ICNote"), count: notes.length }
|
|
46946
47276
|
);
|
|
46947
47277
|
}, "Error listing notes")
|
|
46948
47278
|
);
|
|
@@ -46966,7 +47296,10 @@ registerTool(
|
|
|
46966
47296
|
}
|
|
46967
47297
|
const noteList = notes.map((n) => ` - ${n.title} [id: ${n.id}]`).join("\n");
|
|
46968
47298
|
return successResponse(`Selected note(s):
|
|
46969
|
-
${noteList}`, {
|
|
47299
|
+
${noteList}`, {
|
|
47300
|
+
notes: withStableIdentifiers(notes, "ICNote"),
|
|
47301
|
+
count: notes.length
|
|
47302
|
+
});
|
|
46970
47303
|
}, "Error getting selected notes")
|
|
46971
47304
|
);
|
|
46972
47305
|
registerTool(
|
|
@@ -47007,7 +47340,7 @@ ${syncWarnings.join(" ")}` : "";
|
|
|
47007
47340
|
`Found ${folders.length} folders${resolvedAcct}:
|
|
47008
47341
|
${folderList}${syncNote}`,
|
|
47009
47342
|
{
|
|
47010
|
-
folders,
|
|
47343
|
+
folders: withStableIdentifiers(folders, "ICFolder"),
|
|
47011
47344
|
count: folders.length
|
|
47012
47345
|
}
|
|
47013
47346
|
);
|
|
@@ -47083,7 +47416,7 @@ registerTool(
|
|
|
47083
47416
|
}).join("\n");
|
|
47084
47417
|
return successResponse(`Found ${accounts.length} accounts:
|
|
47085
47418
|
${accountList}`, {
|
|
47086
|
-
accounts,
|
|
47419
|
+
accounts: withStableIdentifiers(accounts, "ICAccount"),
|
|
47087
47420
|
count: accounts.length
|
|
47088
47421
|
});
|
|
47089
47422
|
}, "Error listing accounts")
|
|
@@ -47102,7 +47435,9 @@ registerTool(
|
|
|
47102
47435
|
const location = notesManager.getDefaultLocation();
|
|
47103
47436
|
const message = `Default account: ${location.account.name} [id: ${location.account.id}]
|
|
47104
47437
|
Default folder: ${location.folder.name} [id: ${location.folder.id}]`;
|
|
47105
|
-
|
|
47438
|
+
const [account] = withStableIdentifiers([location.account], "ICAccount");
|
|
47439
|
+
const [folder] = withStableIdentifiers([location.folder], "ICFolder");
|
|
47440
|
+
return successResponse(message, { account, folder });
|
|
47106
47441
|
}, "Error getting default Notes location")
|
|
47107
47442
|
);
|
|
47108
47443
|
registerTool(
|
|
@@ -47132,7 +47467,7 @@ registerTool(
|
|
|
47132
47467
|
${noteList}
|
|
47133
47468
|
|
|
47134
47469
|
\u26A0\uFE0F Changes to shared notes are visible to all collaborators.`,
|
|
47135
|
-
{ notes: sharedNotes, count: sharedNotes.length }
|
|
47470
|
+
{ notes: withStableIdentifiers(sharedNotes, "ICNote"), count: sharedNotes.length }
|
|
47136
47471
|
);
|
|
47137
47472
|
}, "Error listing shared notes")
|
|
47138
47473
|
);
|
|
@@ -47273,7 +47608,7 @@ registerTool(
|
|
|
47273
47608
|
{
|
|
47274
47609
|
description: "Use when: listing the attachments of one note, by id (preferred) or title.\nReturns: each attachment's name, content type, and id (use with save-attachment/fetch-attachment).\nDo not use when: you want the attachment bytes (fetch-attachment) or a file on disk (save-attachment).",
|
|
47275
47610
|
inputSchema: {
|
|
47276
|
-
id: external_exports.string()
|
|
47611
|
+
id: looseNoteId(external_exports.string()).optional().describe(`Note ID (preferred - more reliable than title): ${NOTE_ID_FORMS}`),
|
|
47277
47612
|
title: external_exports.string().max(MAX.TITLE).optional().describe("Note title (use id instead when available)"),
|
|
47278
47613
|
account: external_exports.string().max(MAX.ACCOUNT).optional().describe("Account containing the note (ignored if id is provided)")
|
|
47279
47614
|
},
|
|
@@ -47381,7 +47716,9 @@ registerTool(
|
|
|
47381
47716
|
{
|
|
47382
47717
|
description: "Use when: moving multiple notes by id into one destination folder.\nReturns: per-id success/failure counts after destination-folder verification.\nDo not use when: moving a single note (move-note).\nSafety: each moved note's actual container ID is compared with the destination folder ID before success is reported. The destination folder must already exist (create-folder).",
|
|
47383
47718
|
inputSchema: {
|
|
47384
|
-
ids:
|
|
47719
|
+
ids: noteIdArrayInput.describe(
|
|
47720
|
+
`Array of note IDs to move (max ${MAX.BATCH_IDS} per request)`
|
|
47721
|
+
),
|
|
47385
47722
|
folder: external_exports.string().max(MAX.FOLDER).describe(
|
|
47386
47723
|
'Destination folder name or nested path (e.g. "Work/Clients"). Must already exist \u2014 create-folder first.'
|
|
47387
47724
|
),
|
|
@@ -47425,7 +47762,9 @@ registerTool(
|
|
|
47425
47762
|
{
|
|
47426
47763
|
description: "Use when: writing one note attachment to a file on disk.\nReturns: the saved path.\nDo not use when: you want the bytes in-memory as base64 (fetch-attachment).\nSafety: writes a file; savePath must be absolute and under the home directory, a temp dir, or /Volumes. Get the ids from list-attachments first.",
|
|
47427
47764
|
inputSchema: {
|
|
47428
|
-
noteId: external_exports.string().min(1, "noteId is required")
|
|
47765
|
+
noteId: looseNoteId(external_exports.string().min(1, "noteId is required")).describe(
|
|
47766
|
+
`Note id (from search/list): ${NOTE_ID_FORMS}`
|
|
47767
|
+
),
|
|
47429
47768
|
attachmentId: external_exports.string().min(1, "attachmentId is required").max(MAX.ATTACHMENT_ID).describe("Attachment id (from list-attachments)"),
|
|
47430
47769
|
savePath: external_exports.string().min(1, "savePath is required").max(MAX.SAVE_PATH).describe("Absolute destination file path (must be under home, temp, or /Volumes)")
|
|
47431
47770
|
},
|
|
@@ -47452,7 +47791,9 @@ registerTool(
|
|
|
47452
47791
|
{
|
|
47453
47792
|
description: "Use when: retrieving one note attachment's bytes inline as base64 (no file written).\nReturns: name, content type, byte count, and base64 data.\nDo not use when: you want it saved to disk (save-attachment).\nNote: get the ids from list-attachments first.",
|
|
47454
47793
|
inputSchema: {
|
|
47455
|
-
noteId: external_exports.string().min(1, "noteId is required")
|
|
47794
|
+
noteId: looseNoteId(external_exports.string().min(1, "noteId is required")).describe(
|
|
47795
|
+
`Note id (from search/list): ${NOTE_ID_FORMS}`
|
|
47796
|
+
),
|
|
47456
47797
|
attachmentId: external_exports.string().min(1, "attachmentId is required").max(MAX.ATTACHMENT_ID).describe("Attachment id (from list-attachments)")
|
|
47457
47798
|
},
|
|
47458
47799
|
outputSchema: {
|
|
@@ -47478,7 +47819,9 @@ registerTool(
|
|
|
47478
47819
|
{
|
|
47479
47820
|
description: "Use when: the user wants to reveal one note attachment in Notes.app.\nReturns: confirmation that Notes.app revealed the attachment.\nDo not use when: you want the bytes (fetch-attachment) or a file on disk (save-attachment).\nNote: this opens or focuses the Notes UI. Get the ids from list-attachments first.",
|
|
47480
47821
|
inputSchema: {
|
|
47481
|
-
noteId: external_exports.string().min(1, "noteId is required")
|
|
47822
|
+
noteId: looseNoteId(external_exports.string().min(1, "noteId is required")).describe(
|
|
47823
|
+
`Note id (from search/list): ${NOTE_ID_FORMS}`
|
|
47824
|
+
),
|
|
47482
47825
|
attachmentId: external_exports.string().min(1, "attachmentId is required").max(MAX.ATTACHMENT_ID).describe("Attachment id (from list-attachments)"),
|
|
47483
47826
|
separately: external_exports.boolean().optional().describe("Open in a separate window when supported by Notes.app")
|
|
47484
47827
|
},
|
|
@@ -47578,7 +47921,7 @@ registerTool(
|
|
|
47578
47921
|
{
|
|
47579
47922
|
description: "Use when: reading a note as Markdown, with checklist items annotated [x]/[ ] when Full Disk Access is granted.\nReturns: the note's Markdown.\nDo not use when: you need the raw HTML/plaintext body (get-note-content) or only metadata (get-note-details).\nNote: falls back to plain lists (no checkmarks) without Full Disk Access.",
|
|
47580
47923
|
inputSchema: {
|
|
47581
|
-
id: external_exports.string()
|
|
47924
|
+
id: looseNoteId(external_exports.string()).optional().describe(`Note ID (preferred - more reliable than title): ${NOTE_ID_FORMS}`),
|
|
47582
47925
|
title: external_exports.string().max(MAX.TITLE).optional().describe("Note title (use id instead when available)"),
|
|
47583
47926
|
account: external_exports.string().max(MAX.ACCOUNT).optional().describe("Account containing the note (ignored if id is provided)")
|
|
47584
47927
|
},
|
|
@@ -47611,7 +47954,9 @@ registerTool(
|
|
|
47611
47954
|
{
|
|
47612
47955
|
description: "Use when: reading the checked/unchecked state of a note's checklist items, by id.\nReturns: each item's text and done state plus checked/total counts.\nDo not use when: you only have a title (get the id via search-notes first) or want the full body text (get-note-content).\nNote: requires Full Disk Access; reads the NoteStore database directly.",
|
|
47613
47956
|
inputSchema: {
|
|
47614
|
-
id:
|
|
47957
|
+
id: looseNoteId(
|
|
47958
|
+
external_exports.string().min(1, "Note ID is required. Use search-notes to find the note ID first.")
|
|
47959
|
+
).describe(`Note ID: ${NOTE_ID_FORMS}`)
|
|
47615
47960
|
},
|
|
47616
47961
|
outputSchema: {
|
|
47617
47962
|
items: external_exports.array(external_exports.object({}).passthrough()).optional(),
|
|
@@ -47647,7 +47992,9 @@ registerTool(
|
|
|
47647
47992
|
{
|
|
47648
47993
|
description: "[BETA] Use when: reading note metadata AppleScript cannot expose \u2014 pinned state, checklist flags, trash/recovery state, preview snippet, password hint \u2014 by id.\nReturns: a metadata object; fields vary by macOS version and are omitted when unavailable.\nDo not use when: you need the body (get-note-content) or per-item checklist state (get-checklist-state).\nNote: reads the NoteStore SQLite database read-only and requires Full Disk Access. BETA \u2014 the database schema changes between macOS releases, so some fields may be absent. Works on trashed notes that AppleScript can no longer resolve.",
|
|
47649
47994
|
inputSchema: {
|
|
47650
|
-
id:
|
|
47995
|
+
id: looseNoteId(
|
|
47996
|
+
external_exports.string().min(1, "Note ID is required. Use search-notes to find the note ID first.")
|
|
47997
|
+
).describe(`Note ID: ${NOTE_ID_FORMS}`)
|
|
47651
47998
|
},
|
|
47652
47999
|
outputSchema: {
|
|
47653
48000
|
pinned: external_exports.boolean().optional(),
|