apple-notes-mcp 2.9.15 → 2.9.16
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 +84 -1
- package/build/index.js +543 -111
- package/docs/markdown-templates.md +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1710,7 +1710,7 @@ single note.
|
|
|
1710
1710
|
| `outputPath` | string | No | Absolute file to create. Create-only: an existing file (or symlink) is refused with `[output_exists]` |
|
|
1711
1711
|
| `assetsDir` | string | No | Absolute directory for attachment copies. Existing files are never replaced; a taken name gets `-2`, `-3`, ... |
|
|
1712
1712
|
| `wrap` | number | No | Hard-wrap prose at this many columns. Code, tables and headings are never wrapped |
|
|
1713
|
-
| `template` | string | No | Render through a template: `standard-markdown` or
|
|
1713
|
+
| `template` | string | No | Render through a template: `standard-markdown`, `obsidian`, or a saved template's name. Exclusive with `templateFile` |
|
|
1714
1714
|
| `templateFile` | string | No | Render through the JSON template in this `.json` file (same path rules; at most 256 KiB) |
|
|
1715
1715
|
|
|
1716
1716
|
Both paths follow the `save-attachment` rules (absolute, under the home
|
|
@@ -1785,6 +1785,88 @@ already written is deleted if a later step fails.
|
|
|
1785
1785
|
|
|
1786
1786
|
---
|
|
1787
1787
|
|
|
1788
|
+
#### `list-markdown-templates`
|
|
1789
|
+
|
|
1790
|
+
Lists the Markdown export templates: the built-ins (`standard-markdown`,
|
|
1791
|
+
`obsidian`) and every saved template in the library, with its display name,
|
|
1792
|
+
description, size and modification date. Unreadable, invalid or unsafe files
|
|
1793
|
+
in the library are skipped and counted in `skipped`. Takes no parameters.
|
|
1794
|
+
|
|
1795
|
+
**Returns:** `builtins`, `templates`, `skipped`, and `dir` (the library
|
|
1796
|
+
directory).
|
|
1797
|
+
|
|
1798
|
+
---
|
|
1799
|
+
|
|
1800
|
+
#### `show-markdown-template`
|
|
1801
|
+
|
|
1802
|
+
Returns one template in its portable form: a built-in, or a saved template
|
|
1803
|
+
exactly as stored (overrides only). Use it as the starting point for a new
|
|
1804
|
+
template.
|
|
1805
|
+
|
|
1806
|
+
| Parameter | Type | Required | Description |
|
|
1807
|
+
|-----------|------|----------|-------------|
|
|
1808
|
+
| `name` | string | Yes | A built-in or saved template name |
|
|
1809
|
+
| `expanded` | boolean | No | Also return `expanded`, with every rule filled in from its base |
|
|
1810
|
+
|
|
1811
|
+
**Returns:** `name`, `source` (`builtin` or `saved`), `template`, and
|
|
1812
|
+
optionally `expanded`.
|
|
1813
|
+
|
|
1814
|
+
---
|
|
1815
|
+
|
|
1816
|
+
#### `validate-markdown-template`
|
|
1817
|
+
|
|
1818
|
+
Checks a template without saving it. Pass exactly one of `name`, `template`
|
|
1819
|
+
or `templateFile`.
|
|
1820
|
+
|
|
1821
|
+
| Parameter | Type | Required | Description |
|
|
1822
|
+
|-----------|------|----------|-------------|
|
|
1823
|
+
| `name` | string | One of three | A saved or built-in template |
|
|
1824
|
+
| `template` | object or string | One of three | The template as a JSON object or JSON text |
|
|
1825
|
+
| `templateFile` | string | One of three | Absolute path of a JSON template file (home, a temp directory, or `/Volumes`; at most 256 KiB) |
|
|
1826
|
+
|
|
1827
|
+
**Returns:** `valid`, and `errors` as `{path, message}` pairs such as
|
|
1828
|
+
`$.rules["inline.bold"].after: is required when mode is "wrap"`. An invalid
|
|
1829
|
+
template is a normal result, not a tool error.
|
|
1830
|
+
|
|
1831
|
+
---
|
|
1832
|
+
|
|
1833
|
+
#### `save-markdown-template`
|
|
1834
|
+
|
|
1835
|
+
Validates a template and stores it in the library so `export-notes-markdown`
|
|
1836
|
+
can use it by `template` name.
|
|
1837
|
+
|
|
1838
|
+
| Parameter | Type | Required | Description |
|
|
1839
|
+
|-----------|------|----------|-------------|
|
|
1840
|
+
| `name` | string | Yes | Lowercase `a-z`, `0-9`, `-` and `_`, 1-64 characters, starting with a letter or digit. Built-in names are reserved |
|
|
1841
|
+
| `template` | object or string | One of `template`/`templateFile` | The template |
|
|
1842
|
+
| `templateFile` | string | One of `template`/`templateFile` | A JSON template file |
|
|
1843
|
+
| `force` | boolean | No | Replace an existing template of this name (default `false`) |
|
|
1844
|
+
|
|
1845
|
+
Saving is create-only: an existing name is refused with `[template-exists]`
|
|
1846
|
+
unless `force` is true. An invalid template is refused with
|
|
1847
|
+
`[invalid-template]` and one JSON path per problem. The file is written to a
|
|
1848
|
+
temporary name and then moved into place, with mode `0600` in a `0700`
|
|
1849
|
+
directory. The library is `~/Library/Application Support/apple-notes-mcp/templates`,
|
|
1850
|
+
or `APPLE_NOTES_MCP_TEMPLATE_DIR`. A symlinked library or template file is
|
|
1851
|
+
refused.
|
|
1852
|
+
|
|
1853
|
+
**Returns:** `name`, `path`, `bytes`, and `replaced`.
|
|
1854
|
+
|
|
1855
|
+
---
|
|
1856
|
+
|
|
1857
|
+
#### `delete-markdown-template`
|
|
1858
|
+
|
|
1859
|
+
Removes one saved template file from the library. Built-in templates cannot
|
|
1860
|
+
be deleted.
|
|
1861
|
+
|
|
1862
|
+
| Parameter | Type | Required | Description |
|
|
1863
|
+
|-----------|------|----------|-------------|
|
|
1864
|
+
| `name` | string | Yes | The saved template to delete |
|
|
1865
|
+
|
|
1866
|
+
**Returns:** `name`, `path`, and `deleted: true`.
|
|
1867
|
+
|
|
1868
|
+
---
|
|
1869
|
+
|
|
1788
1870
|
#### `get-checklist-state`
|
|
1789
1871
|
|
|
1790
1872
|
Reads checklist done/undone state for a note. This bypasses the AppleScript limitation where `body of note` strips checklist state, by reading directly from the NoteStore SQLite database.
|
|
@@ -2508,6 +2590,7 @@ All configuration is optional — the server works out of the box. Override beha
|
|
|
2508
2590
|
| `APPLE_NOTES_MCP_MAX_INLINE_IMAGE_BYTES` | `262144` (256 KB) | Per-image cap on the base64 payload kept inline in a [`get-note-content`](#get-note-content) response. Inline images over the cap are replaced with placeholders (with a warning appended) so an image-heavy note cannot exceed the MCP client's message limit and drop the connection; export the real files with [`save-attachment`](#save-attachment) or [`fetch-attachment`](#fetch-attachment). Raise it to keep bigger images inline. |
|
|
2509
2591
|
| `APPLE_NOTES_MCP_CONFIG_FILE` | `~/Library/Application Support/apple-notes-mcp/config.json` | Path to the JSON config file (see below). |
|
|
2510
2592
|
| `APPLE_NOTES_MCP_TIMEOUT_MS` | `30000` (30 s) | Total AppleScript operation timeout, including retry attempts and delays. Raise it if full-library operations (large searches, exports) time out on a big Notes library. Per-call `timeoutMs` options still win, and a write tool's `timeoutSeconds` argument overrides it for that call. |
|
|
2593
|
+
| `APPLE_NOTES_MCP_TEMPLATE_DIR` | `~/Library/Application Support/apple-notes-mcp/templates` | Absolute directory of the saved Markdown template library (`save-markdown-template` and friends). |
|
|
2511
2594
|
| `APPLE_NOTES_MCP_EXPORT_MAX_BYTES` | `8388608` (8 MB) | Largest response `export-notes-json` sends; a page closes early to stay under it. `export-notes-markdown` returns inline Markdown up to half of it. The default sits below the 10 MB per-message limit of MCP SDK stdio clients, which drop the connection on anything larger. Raise it only if your MCP client accepts bigger messages. |
|
|
2512
2595
|
| `APPLE_NOTES_MCP_BLOCKS_MAX_BYTES` | `4194304` (4 MB) | Largest block payload one [`get-note-blocks`](#get-note-blocks) page returns; the page closes early to stay under it, and a single oversized paragraph comes back with `textOmitted: true`. [`get-note-structure`](#get-note-structure) also omits note text larger than this. A [`list-note-paragraphs`](#list-note-paragraphs) or [`list-note-links`](#list-note-links) page also stops early to stay under it. |
|
|
2513
2596
|
| `APPLE_NOTES_MCP_MAX_RETRIES` | `2` | Maximum attempts for a read-only AppleScript call that fails with a **transient** error (Notes.app busy / not responding / lost connection). `2` means one retry; set `1` to fail fast with no retries. Retries share the single `APPLE_NOTES_MCP_TIMEOUT_MS` budget rather than each getting a fresh one, and a retry is skipped when under a second of that budget remains — so this is a ceiling, not a guarantee. In particular a call that exhausts the budget with a **timeout** has no time left to retry by construction. Mutating operations run once because a timeout can occur after Notes.app applied the change. Non-transient errors (e.g. "note not found") never retry. |
|
package/build/index.js
CHANGED
|
@@ -417,11 +417,11 @@ var require_codegen = __commonJS({
|
|
|
417
417
|
const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
|
|
418
418
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
419
419
|
}
|
|
420
|
-
optimizeNames(names,
|
|
420
|
+
optimizeNames(names, constants9) {
|
|
421
421
|
if (!names[this.name.str])
|
|
422
422
|
return;
|
|
423
423
|
if (this.rhs)
|
|
424
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
424
|
+
this.rhs = optimizeExpr(this.rhs, names, constants9);
|
|
425
425
|
return this;
|
|
426
426
|
}
|
|
427
427
|
get names() {
|
|
@@ -438,10 +438,10 @@ var require_codegen = __commonJS({
|
|
|
438
438
|
render({ _n }) {
|
|
439
439
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
440
440
|
}
|
|
441
|
-
optimizeNames(names,
|
|
441
|
+
optimizeNames(names, constants9) {
|
|
442
442
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
|
|
443
443
|
return;
|
|
444
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
444
|
+
this.rhs = optimizeExpr(this.rhs, names, constants9);
|
|
445
445
|
return this;
|
|
446
446
|
}
|
|
447
447
|
get names() {
|
|
@@ -502,8 +502,8 @@ var require_codegen = __commonJS({
|
|
|
502
502
|
optimizeNodes() {
|
|
503
503
|
return `${this.code}` ? this : void 0;
|
|
504
504
|
}
|
|
505
|
-
optimizeNames(names,
|
|
506
|
-
this.code = optimizeExpr(this.code, names,
|
|
505
|
+
optimizeNames(names, constants9) {
|
|
506
|
+
this.code = optimizeExpr(this.code, names, constants9);
|
|
507
507
|
return this;
|
|
508
508
|
}
|
|
509
509
|
get names() {
|
|
@@ -532,12 +532,12 @@ var require_codegen = __commonJS({
|
|
|
532
532
|
}
|
|
533
533
|
return nodes.length > 0 ? this : void 0;
|
|
534
534
|
}
|
|
535
|
-
optimizeNames(names,
|
|
535
|
+
optimizeNames(names, constants9) {
|
|
536
536
|
const { nodes } = this;
|
|
537
537
|
let i = nodes.length;
|
|
538
538
|
while (i--) {
|
|
539
539
|
const n = nodes[i];
|
|
540
|
-
if (n.optimizeNames(names,
|
|
540
|
+
if (n.optimizeNames(names, constants9))
|
|
541
541
|
continue;
|
|
542
542
|
subtractNames(names, n.names);
|
|
543
543
|
nodes.splice(i, 1);
|
|
@@ -590,12 +590,12 @@ var require_codegen = __commonJS({
|
|
|
590
590
|
return void 0;
|
|
591
591
|
return this;
|
|
592
592
|
}
|
|
593
|
-
optimizeNames(names,
|
|
593
|
+
optimizeNames(names, constants9) {
|
|
594
594
|
var _a;
|
|
595
|
-
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
596
|
-
if (!(super.optimizeNames(names,
|
|
595
|
+
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants9);
|
|
596
|
+
if (!(super.optimizeNames(names, constants9) || this.else))
|
|
597
597
|
return;
|
|
598
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
598
|
+
this.condition = optimizeExpr(this.condition, names, constants9);
|
|
599
599
|
return this;
|
|
600
600
|
}
|
|
601
601
|
get names() {
|
|
@@ -618,10 +618,10 @@ var require_codegen = __commonJS({
|
|
|
618
618
|
render(opts) {
|
|
619
619
|
return `for(${this.iteration})` + super.render(opts);
|
|
620
620
|
}
|
|
621
|
-
optimizeNames(names,
|
|
622
|
-
if (!super.optimizeNames(names,
|
|
621
|
+
optimizeNames(names, constants9) {
|
|
622
|
+
if (!super.optimizeNames(names, constants9))
|
|
623
623
|
return;
|
|
624
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
624
|
+
this.iteration = optimizeExpr(this.iteration, names, constants9);
|
|
625
625
|
return this;
|
|
626
626
|
}
|
|
627
627
|
get names() {
|
|
@@ -657,10 +657,10 @@ var require_codegen = __commonJS({
|
|
|
657
657
|
render(opts) {
|
|
658
658
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
659
659
|
}
|
|
660
|
-
optimizeNames(names,
|
|
661
|
-
if (!super.optimizeNames(names,
|
|
660
|
+
optimizeNames(names, constants9) {
|
|
661
|
+
if (!super.optimizeNames(names, constants9))
|
|
662
662
|
return;
|
|
663
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
663
|
+
this.iterable = optimizeExpr(this.iterable, names, constants9);
|
|
664
664
|
return this;
|
|
665
665
|
}
|
|
666
666
|
get names() {
|
|
@@ -702,11 +702,11 @@ var require_codegen = __commonJS({
|
|
|
702
702
|
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
|
|
703
703
|
return this;
|
|
704
704
|
}
|
|
705
|
-
optimizeNames(names,
|
|
705
|
+
optimizeNames(names, constants9) {
|
|
706
706
|
var _a, _b;
|
|
707
|
-
super.optimizeNames(names,
|
|
708
|
-
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
709
|
-
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names,
|
|
707
|
+
super.optimizeNames(names, constants9);
|
|
708
|
+
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants9);
|
|
709
|
+
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants9);
|
|
710
710
|
return this;
|
|
711
711
|
}
|
|
712
712
|
get names() {
|
|
@@ -1007,7 +1007,7 @@ var require_codegen = __commonJS({
|
|
|
1007
1007
|
function addExprNames(names, from) {
|
|
1008
1008
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
1009
1009
|
}
|
|
1010
|
-
function optimizeExpr(expr, names,
|
|
1010
|
+
function optimizeExpr(expr, names, constants9) {
|
|
1011
1011
|
if (expr instanceof code_1.Name)
|
|
1012
1012
|
return replaceName(expr);
|
|
1013
1013
|
if (!canOptimize(expr))
|
|
@@ -1022,14 +1022,14 @@ var require_codegen = __commonJS({
|
|
|
1022
1022
|
return items;
|
|
1023
1023
|
}, []));
|
|
1024
1024
|
function replaceName(n) {
|
|
1025
|
-
const c =
|
|
1025
|
+
const c = constants9[n.str];
|
|
1026
1026
|
if (c === void 0 || names[n.str] !== 1)
|
|
1027
1027
|
return n;
|
|
1028
1028
|
delete names[n.str];
|
|
1029
1029
|
return c;
|
|
1030
1030
|
}
|
|
1031
1031
|
function canOptimize(e) {
|
|
1032
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
1032
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants9[c.str] !== void 0);
|
|
1033
1033
|
}
|
|
1034
1034
|
}
|
|
1035
1035
|
function subtractNames(names, from) {
|
|
@@ -2991,7 +2991,7 @@ var require_compile = __commonJS({
|
|
|
2991
2991
|
const schOrFunc = root.refs[ref];
|
|
2992
2992
|
if (schOrFunc)
|
|
2993
2993
|
return schOrFunc;
|
|
2994
|
-
let _sch =
|
|
2994
|
+
let _sch = resolve9.call(this, root, ref);
|
|
2995
2995
|
if (_sch === void 0) {
|
|
2996
2996
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2997
2997
|
const { schemaId } = this.opts;
|
|
@@ -3018,7 +3018,7 @@ var require_compile = __commonJS({
|
|
|
3018
3018
|
function sameSchemaEnv(s1, s2) {
|
|
3019
3019
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3020
3020
|
}
|
|
3021
|
-
function
|
|
3021
|
+
function resolve9(root, ref) {
|
|
3022
3022
|
let sch;
|
|
3023
3023
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3024
3024
|
ref = sch;
|
|
@@ -3843,7 +3843,7 @@ var require_fast_uri = __commonJS({
|
|
|
3843
3843
|
}
|
|
3844
3844
|
return uri;
|
|
3845
3845
|
}
|
|
3846
|
-
function
|
|
3846
|
+
function resolve9(baseURI, relativeURI, options) {
|
|
3847
3847
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3848
3848
|
const {
|
|
3849
3849
|
parsed: baseParsed,
|
|
@@ -4205,7 +4205,7 @@ var require_fast_uri = __commonJS({
|
|
|
4205
4205
|
var fastUri = {
|
|
4206
4206
|
SCHEMES,
|
|
4207
4207
|
normalize,
|
|
4208
|
-
resolve:
|
|
4208
|
+
resolve: resolve9,
|
|
4209
4209
|
resolveComponent,
|
|
4210
4210
|
equal,
|
|
4211
4211
|
serialize,
|
|
@@ -7422,7 +7422,7 @@ var require_DOMException = __commonJS({
|
|
|
7422
7422
|
"INVALID_NODE_TYPE_ERR (24): the supplied node is invalid or has an invalid ancestor for this operation",
|
|
7423
7423
|
"DATA_CLONE_ERR (25): the object can not be cloned."
|
|
7424
7424
|
];
|
|
7425
|
-
var
|
|
7425
|
+
var constants9 = {
|
|
7426
7426
|
INDEX_SIZE_ERR,
|
|
7427
7427
|
DOMSTRING_SIZE_ERR: 2,
|
|
7428
7428
|
// historical
|
|
@@ -7461,8 +7461,8 @@ var require_DOMException = __commonJS({
|
|
|
7461
7461
|
this.name = names[code];
|
|
7462
7462
|
}
|
|
7463
7463
|
DOMException.prototype.__proto__ = Error.prototype;
|
|
7464
|
-
for (c in
|
|
7465
|
-
v = { value:
|
|
7464
|
+
for (c in constants9) {
|
|
7465
|
+
v = { value: constants9[c] };
|
|
7466
7466
|
Object.defineProperty(DOMException, c, v);
|
|
7467
7467
|
Object.defineProperty(DOMException.prototype, c, v);
|
|
7468
7468
|
}
|
|
@@ -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 join30(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 = join30(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 join30(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);
|
|
@@ -36608,7 +36608,7 @@ var Protocol = class {
|
|
|
36608
36608
|
return;
|
|
36609
36609
|
}
|
|
36610
36610
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
36611
|
-
await new Promise((
|
|
36611
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
36612
36612
|
options?.signal?.throwIfAborted();
|
|
36613
36613
|
}
|
|
36614
36614
|
} catch (error2) {
|
|
@@ -36625,7 +36625,7 @@ var Protocol = class {
|
|
|
36625
36625
|
*/
|
|
36626
36626
|
request(request, resultSchema, options) {
|
|
36627
36627
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
36628
|
-
return new Promise((
|
|
36628
|
+
return new Promise((resolve9, reject) => {
|
|
36629
36629
|
const earlyReject = (error2) => {
|
|
36630
36630
|
reject(error2);
|
|
36631
36631
|
};
|
|
@@ -36703,7 +36703,7 @@ var Protocol = class {
|
|
|
36703
36703
|
if (!parseResult.success) {
|
|
36704
36704
|
reject(parseResult.error);
|
|
36705
36705
|
} else {
|
|
36706
|
-
|
|
36706
|
+
resolve9(parseResult.data);
|
|
36707
36707
|
}
|
|
36708
36708
|
} catch (error2) {
|
|
36709
36709
|
reject(error2);
|
|
@@ -36964,12 +36964,12 @@ var Protocol = class {
|
|
|
36964
36964
|
}
|
|
36965
36965
|
} catch {
|
|
36966
36966
|
}
|
|
36967
|
-
return new Promise((
|
|
36967
|
+
return new Promise((resolve9, reject) => {
|
|
36968
36968
|
if (signal.aborted) {
|
|
36969
36969
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
36970
36970
|
return;
|
|
36971
36971
|
}
|
|
36972
|
-
const timeoutId = setTimeout(
|
|
36972
|
+
const timeoutId = setTimeout(resolve9, interval);
|
|
36973
36973
|
signal.addEventListener("abort", () => {
|
|
36974
36974
|
clearTimeout(timeoutId);
|
|
36975
36975
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -38282,7 +38282,7 @@ var McpServer = class {
|
|
|
38282
38282
|
let task = createTaskResult.task;
|
|
38283
38283
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
38284
38284
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
38285
|
-
await new Promise((
|
|
38285
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
38286
38286
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
38287
38287
|
if (!updatedTask) {
|
|
38288
38288
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -38970,12 +38970,12 @@ var StdioServerTransport = class {
|
|
|
38970
38970
|
this.onclose?.();
|
|
38971
38971
|
}
|
|
38972
38972
|
send(message) {
|
|
38973
|
-
return new Promise((
|
|
38973
|
+
return new Promise((resolve9) => {
|
|
38974
38974
|
const json2 = serializeMessage(message);
|
|
38975
38975
|
if (this._stdout.write(json2)) {
|
|
38976
|
-
|
|
38976
|
+
resolve9();
|
|
38977
38977
|
} else {
|
|
38978
|
-
this._stdout.once("drain",
|
|
38978
|
+
this._stdout.once("drain", resolve9);
|
|
38979
38979
|
}
|
|
38980
38980
|
});
|
|
38981
38981
|
}
|
|
@@ -47633,13 +47633,13 @@ var escapeSegment = escapeFolderName;
|
|
|
47633
47633
|
function resolveFolders(rows) {
|
|
47634
47634
|
const byPk = new Map(rows.map((row) => [row.pk, row]));
|
|
47635
47635
|
const resolved = /* @__PURE__ */ new Map();
|
|
47636
|
-
const
|
|
47636
|
+
const resolve9 = (pk, seen) => {
|
|
47637
47637
|
const cached2 = resolved.get(pk);
|
|
47638
47638
|
if (cached2) return cached2;
|
|
47639
47639
|
const row = byPk.get(pk);
|
|
47640
47640
|
if (!row || seen.has(pk)) return void 0;
|
|
47641
47641
|
seen.add(pk);
|
|
47642
|
-
const parent = row.parent !== null ?
|
|
47642
|
+
const parent = row.parent !== null ? resolve9(row.parent, seen) : void 0;
|
|
47643
47643
|
const name = row.name ?? "";
|
|
47644
47644
|
const path10 = parent ? `${parent.path}/${escapeSegment(name)}` : escapeSegment(name);
|
|
47645
47645
|
const plainPath = parent ? `${parent.plainPath}/${name}` : name;
|
|
@@ -47653,7 +47653,7 @@ function resolveFolders(rows) {
|
|
|
47653
47653
|
resolved.set(pk, info);
|
|
47654
47654
|
return info;
|
|
47655
47655
|
};
|
|
47656
|
-
for (const row of rows)
|
|
47656
|
+
for (const row of rows) resolve9(row.pk, /* @__PURE__ */ new Set());
|
|
47657
47657
|
return resolved;
|
|
47658
47658
|
}
|
|
47659
47659
|
var coreDataMs = (seconds) => seconds === null || !Number.isFinite(seconds) ? void 0 : CORE_DATA_EPOCH_MS2 + seconds * 1e3;
|
|
@@ -48860,6 +48860,19 @@ var FEATURES = [
|
|
|
48860
48860
|
tools: [],
|
|
48861
48861
|
minimumMacOSVersion: null,
|
|
48862
48862
|
requirements: [{ kind: "native_write_helper" }]
|
|
48863
|
+
},
|
|
48864
|
+
{
|
|
48865
|
+
name: "markdownTemplateLibrary",
|
|
48866
|
+
description: "Validate, save, list, show and delete Markdown export templates (local JSON files; exporting with one still needs Full Disk Access)",
|
|
48867
|
+
tools: [
|
|
48868
|
+
"list-markdown-templates",
|
|
48869
|
+
"show-markdown-template",
|
|
48870
|
+
"validate-markdown-template",
|
|
48871
|
+
"save-markdown-template",
|
|
48872
|
+
"delete-markdown-template"
|
|
48873
|
+
],
|
|
48874
|
+
minimumMacOSVersion: null,
|
|
48875
|
+
requirements: []
|
|
48863
48876
|
}
|
|
48864
48877
|
];
|
|
48865
48878
|
function requirementLabel(requirement) {
|
|
@@ -51653,16 +51666,16 @@ var NOTE_PLACEHOLDERS = [
|
|
|
51653
51666
|
"exportStem"
|
|
51654
51667
|
];
|
|
51655
51668
|
var PLACEHOLDER_MODIFIERS = ["raw", "yaml"];
|
|
51656
|
-
var wrap2 = (before, after = "",
|
|
51669
|
+
var wrap2 = (before, after = "", join30) => ({
|
|
51657
51670
|
mode: "wrap",
|
|
51658
51671
|
before,
|
|
51659
51672
|
after,
|
|
51660
|
-
...
|
|
51673
|
+
...join30 ? { join: join30 } : {}
|
|
51661
51674
|
});
|
|
51662
|
-
var pattern = (value,
|
|
51675
|
+
var pattern = (value, join30) => ({
|
|
51663
51676
|
mode: "pattern",
|
|
51664
51677
|
value,
|
|
51665
|
-
...
|
|
51678
|
+
...join30 ? { join: join30 } : {}
|
|
51666
51679
|
});
|
|
51667
51680
|
var STANDARD = {
|
|
51668
51681
|
schemaVersion: 1,
|
|
@@ -53686,9 +53699,426 @@ function registerFolderDelete(server2, manager, deps = defaultDeps) {
|
|
|
53686
53699
|
);
|
|
53687
53700
|
}
|
|
53688
53701
|
|
|
53702
|
+
// src/services/templateStore.ts
|
|
53703
|
+
import { randomBytes } from "node:crypto";
|
|
53704
|
+
import {
|
|
53705
|
+
closeSync as closeSync7,
|
|
53706
|
+
constants as constants7,
|
|
53707
|
+
fstatSync as fstatSync7,
|
|
53708
|
+
linkSync,
|
|
53709
|
+
lstatSync as lstatSync5,
|
|
53710
|
+
mkdirSync as mkdirSync7,
|
|
53711
|
+
openSync as openSync7,
|
|
53712
|
+
readdirSync as readdirSync4,
|
|
53713
|
+
readSync as readSync5,
|
|
53714
|
+
renameSync,
|
|
53715
|
+
unlinkSync as unlinkSync3,
|
|
53716
|
+
writeSync as writeSync5
|
|
53717
|
+
} from "node:fs";
|
|
53718
|
+
import { homedir as homedir19 } from "node:os";
|
|
53719
|
+
import { isAbsolute as isAbsolute5, join as join25, resolve as resolve5 } from "node:path";
|
|
53720
|
+
var TEMPLATE_SLUG = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
53721
|
+
var TemplateStoreError = class extends Error {
|
|
53722
|
+
constructor(code, message) {
|
|
53723
|
+
super(message);
|
|
53724
|
+
this.code = code;
|
|
53725
|
+
this.name = "TemplateStoreError";
|
|
53726
|
+
}
|
|
53727
|
+
code;
|
|
53728
|
+
};
|
|
53729
|
+
function templateDir(env = process.env) {
|
|
53730
|
+
const override = env.APPLE_NOTES_MCP_TEMPLATE_DIR?.trim();
|
|
53731
|
+
if (override) {
|
|
53732
|
+
if (!isAbsolute5(override))
|
|
53733
|
+
throw new TemplateStoreError(
|
|
53734
|
+
"unsafe-path",
|
|
53735
|
+
"APPLE_NOTES_MCP_TEMPLATE_DIR must be an absolute path."
|
|
53736
|
+
);
|
|
53737
|
+
return resolve5(override);
|
|
53738
|
+
}
|
|
53739
|
+
return join25(homedir19(), "Library/Application Support/apple-notes-mcp/templates");
|
|
53740
|
+
}
|
|
53741
|
+
function checkName(name, action) {
|
|
53742
|
+
if (isBuiltinTemplate(name) && action !== "read")
|
|
53743
|
+
throw new TemplateStoreError(
|
|
53744
|
+
"reserved-name",
|
|
53745
|
+
`"${name}" is a built-in template and cannot be ${action === "save" ? "saved over" : "deleted"}.`
|
|
53746
|
+
);
|
|
53747
|
+
if (!TEMPLATE_SLUG.test(name))
|
|
53748
|
+
throw new TemplateStoreError(
|
|
53749
|
+
"invalid-name",
|
|
53750
|
+
`Template names are 1-64 characters of a-z, 0-9, "-" and "_", starting with a letter or digit; got "${name}".`
|
|
53751
|
+
);
|
|
53752
|
+
}
|
|
53753
|
+
var TemplateStore = class {
|
|
53754
|
+
constructor(dir = templateDir()) {
|
|
53755
|
+
this.dir = dir;
|
|
53756
|
+
}
|
|
53757
|
+
dir;
|
|
53758
|
+
/** The library root, which must be a real directory (not a symlink) when it exists. */
|
|
53759
|
+
root(create) {
|
|
53760
|
+
let stat;
|
|
53761
|
+
try {
|
|
53762
|
+
stat = lstatSync5(this.dir);
|
|
53763
|
+
} catch {
|
|
53764
|
+
if (!create) return void 0;
|
|
53765
|
+
mkdirSync7(this.dir, { recursive: true, mode: 448 });
|
|
53766
|
+
stat = lstatSync5(this.dir);
|
|
53767
|
+
}
|
|
53768
|
+
if (!stat.isDirectory())
|
|
53769
|
+
throw new TemplateStoreError(
|
|
53770
|
+
"unsafe-path",
|
|
53771
|
+
`The template library ${this.dir} is not a directory (symlinks are refused).`
|
|
53772
|
+
);
|
|
53773
|
+
return this.dir;
|
|
53774
|
+
}
|
|
53775
|
+
file(name) {
|
|
53776
|
+
return join25(this.dir, `${name}.json`);
|
|
53777
|
+
}
|
|
53778
|
+
/** Read a saved template's text, refusing symlinks and non-regular files. */
|
|
53779
|
+
readText(path10) {
|
|
53780
|
+
let fd;
|
|
53781
|
+
try {
|
|
53782
|
+
fd = openSync7(path10, constants7.O_RDONLY | constants7.O_NOFOLLOW);
|
|
53783
|
+
} catch (error2) {
|
|
53784
|
+
if (error2.code === "ENOENT") return void 0;
|
|
53785
|
+
throw new TemplateStoreError("unsafe-path", `Refusing to read ${path10}: not a regular file.`);
|
|
53786
|
+
}
|
|
53787
|
+
try {
|
|
53788
|
+
const stat = fstatSync7(fd);
|
|
53789
|
+
if (!stat.isFile())
|
|
53790
|
+
throw new TemplateStoreError(
|
|
53791
|
+
"unsafe-path",
|
|
53792
|
+
`Refusing to read ${path10}: not a regular file.`
|
|
53793
|
+
);
|
|
53794
|
+
if (stat.size > MAX_TEMPLATE_BYTES)
|
|
53795
|
+
throw new TemplateValidationError([
|
|
53796
|
+
{
|
|
53797
|
+
path: "$",
|
|
53798
|
+
message: `template is ${stat.size} bytes; the limit is ${MAX_TEMPLATE_BYTES}`
|
|
53799
|
+
}
|
|
53800
|
+
]);
|
|
53801
|
+
const data = Buffer.alloc(stat.size);
|
|
53802
|
+
let read = 0;
|
|
53803
|
+
while (read < stat.size) {
|
|
53804
|
+
const n = readSync5(fd, data, read, stat.size - read, read);
|
|
53805
|
+
if (n <= 0) break;
|
|
53806
|
+
read += n;
|
|
53807
|
+
}
|
|
53808
|
+
return { text: data.subarray(0, read).toString("utf8"), bytes: read, mtime: stat.mtime };
|
|
53809
|
+
} finally {
|
|
53810
|
+
closeSync7(fd);
|
|
53811
|
+
}
|
|
53812
|
+
}
|
|
53813
|
+
/** Saved templates in name order; `skipped` counts entries that are not usable. */
|
|
53814
|
+
list() {
|
|
53815
|
+
const templates = [];
|
|
53816
|
+
let skipped = 0;
|
|
53817
|
+
if (!this.root(false)) return { templates, skipped, dir: this.dir };
|
|
53818
|
+
for (const entry of readdirSync4(this.dir).sort()) {
|
|
53819
|
+
if (!entry.endsWith(".json") || entry.startsWith(".")) continue;
|
|
53820
|
+
const name = entry.slice(0, -5);
|
|
53821
|
+
if (!TEMPLATE_SLUG.test(name) || isBuiltinTemplate(name)) {
|
|
53822
|
+
skipped++;
|
|
53823
|
+
continue;
|
|
53824
|
+
}
|
|
53825
|
+
try {
|
|
53826
|
+
const read = this.readText(this.file(name));
|
|
53827
|
+
if (!read) continue;
|
|
53828
|
+
const template = parseTemplate(read.text);
|
|
53829
|
+
templates.push({
|
|
53830
|
+
name,
|
|
53831
|
+
...template.name && template.name !== name ? { displayName: template.name } : {},
|
|
53832
|
+
...template.description ? { description: template.description } : {},
|
|
53833
|
+
...template.extends ? { extends: template.extends } : {},
|
|
53834
|
+
bytes: read.bytes,
|
|
53835
|
+
modified: read.mtime.toISOString()
|
|
53836
|
+
});
|
|
53837
|
+
} catch {
|
|
53838
|
+
skipped++;
|
|
53839
|
+
}
|
|
53840
|
+
}
|
|
53841
|
+
return { templates, skipped, dir: this.dir };
|
|
53842
|
+
}
|
|
53843
|
+
/**
|
|
53844
|
+
* A saved template in its portable (as stored) form, or undefined when
|
|
53845
|
+
* there is none. Throws {@link TemplateValidationError} for a corrupt file.
|
|
53846
|
+
*/
|
|
53847
|
+
find(name) {
|
|
53848
|
+
checkName(name, "read");
|
|
53849
|
+
if (!this.root(false)) return void 0;
|
|
53850
|
+
const read = this.readText(this.file(name));
|
|
53851
|
+
return read ? parseTemplate(read.text) : void 0;
|
|
53852
|
+
}
|
|
53853
|
+
/** Like {@link find}, but a missing template is an error. */
|
|
53854
|
+
get(name) {
|
|
53855
|
+
const template = this.find(name);
|
|
53856
|
+
if (!template)
|
|
53857
|
+
throw new TemplateStoreError(
|
|
53858
|
+
"template-not-found",
|
|
53859
|
+
`No saved template named "${name}". Built-in templates: ${BUILTIN_TEMPLATE_NAMES.join(", ")}.`
|
|
53860
|
+
);
|
|
53861
|
+
return template;
|
|
53862
|
+
}
|
|
53863
|
+
/**
|
|
53864
|
+
* Validate and save a template under `name`. Create-only unless `force`.
|
|
53865
|
+
* Returns the file path and whether an existing template was replaced.
|
|
53866
|
+
*/
|
|
53867
|
+
save(name, text2, { force = false } = {}) {
|
|
53868
|
+
checkName(name, "save");
|
|
53869
|
+
const template = parseTemplate(text2);
|
|
53870
|
+
const body = JSON.stringify(template, null, 2) + "\n";
|
|
53871
|
+
this.root(true);
|
|
53872
|
+
const path10 = this.file(name);
|
|
53873
|
+
let exists = false;
|
|
53874
|
+
try {
|
|
53875
|
+
const stat = lstatSync5(path10);
|
|
53876
|
+
if (!stat.isFile())
|
|
53877
|
+
throw new TemplateStoreError(
|
|
53878
|
+
"unsafe-path",
|
|
53879
|
+
`Refusing to replace ${path10}: it is not a regular file.`
|
|
53880
|
+
);
|
|
53881
|
+
exists = true;
|
|
53882
|
+
} catch (error2) {
|
|
53883
|
+
if (error2 instanceof TemplateStoreError) throw error2;
|
|
53884
|
+
}
|
|
53885
|
+
if (exists && !force)
|
|
53886
|
+
throw new TemplateStoreError(
|
|
53887
|
+
"template-exists",
|
|
53888
|
+
`A template named "${name}" already exists. Pass force: true to replace it.`
|
|
53889
|
+
);
|
|
53890
|
+
const temp = join25(this.dir, `.${name}.${randomBytes(6).toString("hex")}.tmp`);
|
|
53891
|
+
const fd = openSync7(
|
|
53892
|
+
temp,
|
|
53893
|
+
constants7.O_WRONLY | constants7.O_CREAT | constants7.O_EXCL | constants7.O_NOFOLLOW,
|
|
53894
|
+
384
|
|
53895
|
+
);
|
|
53896
|
+
try {
|
|
53897
|
+
const data = Buffer.from(body, "utf8");
|
|
53898
|
+
let written = 0;
|
|
53899
|
+
while (written < data.length) written += writeSync5(fd, data, written);
|
|
53900
|
+
} finally {
|
|
53901
|
+
closeSync7(fd);
|
|
53902
|
+
}
|
|
53903
|
+
try {
|
|
53904
|
+
if (force) renameSync(temp, path10);
|
|
53905
|
+
else {
|
|
53906
|
+
try {
|
|
53907
|
+
linkSync(temp, path10);
|
|
53908
|
+
} catch (error2) {
|
|
53909
|
+
if (error2.code === "EEXIST")
|
|
53910
|
+
throw new TemplateStoreError(
|
|
53911
|
+
"template-exists",
|
|
53912
|
+
`A template named "${name}" already exists. Pass force: true to replace it.`
|
|
53913
|
+
);
|
|
53914
|
+
throw error2;
|
|
53915
|
+
}
|
|
53916
|
+
}
|
|
53917
|
+
} finally {
|
|
53918
|
+
try {
|
|
53919
|
+
unlinkSync3(temp);
|
|
53920
|
+
} catch {
|
|
53921
|
+
}
|
|
53922
|
+
}
|
|
53923
|
+
return { path: path10, replaced: exists, bytes: Buffer.byteLength(body), template };
|
|
53924
|
+
}
|
|
53925
|
+
/** Delete a saved template. Built-ins cannot be deleted. */
|
|
53926
|
+
delete(name) {
|
|
53927
|
+
checkName(name, "delete");
|
|
53928
|
+
const path10 = this.file(name);
|
|
53929
|
+
let stat;
|
|
53930
|
+
try {
|
|
53931
|
+
if (!this.root(false)) throw new Error("absent");
|
|
53932
|
+
stat = lstatSync5(path10);
|
|
53933
|
+
} catch (error2) {
|
|
53934
|
+
if (error2 instanceof TemplateStoreError) throw error2;
|
|
53935
|
+
throw new TemplateStoreError("template-not-found", `No saved template named "${name}".`);
|
|
53936
|
+
}
|
|
53937
|
+
if (!stat.isFile())
|
|
53938
|
+
throw new TemplateStoreError(
|
|
53939
|
+
"unsafe-path",
|
|
53940
|
+
`Refusing to delete ${path10}: not a regular file.`
|
|
53941
|
+
);
|
|
53942
|
+
unlinkSync3(path10);
|
|
53943
|
+
return { path: path10 };
|
|
53944
|
+
}
|
|
53945
|
+
};
|
|
53946
|
+
|
|
53947
|
+
// src/tools/markdownTemplates.ts
|
|
53948
|
+
var ok = (text2, structured) => ({
|
|
53949
|
+
content: [{ type: "text", text: text2 }],
|
|
53950
|
+
structuredContent: structured
|
|
53951
|
+
});
|
|
53952
|
+
function failure(action, error2) {
|
|
53953
|
+
if (error2 instanceof TemplateValidationError)
|
|
53954
|
+
return errorResult(
|
|
53955
|
+
`Error ${action} [invalid-template]: the template is invalid:
|
|
53956
|
+
` + error2.errors.map((e) => `${e.path}: ${e.message}`).join("\n"),
|
|
53957
|
+
error2
|
|
53958
|
+
);
|
|
53959
|
+
if (error2 instanceof TemplateStoreError)
|
|
53960
|
+
return errorResult(`Error ${action} [${error2.code}]: ${error2.message}`, error2);
|
|
53961
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
53962
|
+
return errorResult(`Error ${action}: ${message}`, error2);
|
|
53963
|
+
}
|
|
53964
|
+
var nameInput = external_exports.string().min(1).max(64).describe("Template name: a built-in (standard-markdown, obsidian) or a saved template's name");
|
|
53965
|
+
var templateInput = external_exports.union([external_exports.record(external_exports.unknown()), external_exports.string().max(MAX_TEMPLATE_BYTES)]).optional().describe("The template itself, as a JSON object or JSON text (schemaVersion 1)");
|
|
53966
|
+
var templateFileInput = external_exports.string().min(1).max(4096).optional().describe(
|
|
53967
|
+
"Absolute path of a JSON template file ending in .json (home, a temp dir, or /Volumes; symlinks refused; at most 256 KiB)"
|
|
53968
|
+
);
|
|
53969
|
+
function sourceText(args) {
|
|
53970
|
+
if (args.template === void 0 === (args.templateFile === void 0))
|
|
53971
|
+
throw new Error("Provide exactly one of 'template' or 'templateFile'.");
|
|
53972
|
+
if (args.templateFile !== void 0) return readTemplateFile(args.templateFile);
|
|
53973
|
+
return typeof args.template === "string" ? args.template : JSON.stringify(args.template);
|
|
53974
|
+
}
|
|
53975
|
+
function registerMarkdownTemplates(server2, store = () => new TemplateStore()) {
|
|
53976
|
+
const loose = external_exports.object({}).passthrough();
|
|
53977
|
+
server2.registerTool(
|
|
53978
|
+
"list-markdown-templates",
|
|
53979
|
+
{
|
|
53980
|
+
description: "Use when: choosing a Markdown export template, or checking which saved templates exist.\nReturns: the built-in templates, the saved templates (name, display name, description, size, modified date), the library directory, and how many unusable files were skipped.\nDo not use when: you need a template's rules (show-markdown-template).\nSafety: read-only; never opens Notes.",
|
|
53981
|
+
inputSchema: {},
|
|
53982
|
+
outputSchema: loose,
|
|
53983
|
+
annotations: { readOnlyHint: true }
|
|
53984
|
+
},
|
|
53985
|
+
(async () => {
|
|
53986
|
+
try {
|
|
53987
|
+
const listing = store().list();
|
|
53988
|
+
const builtins = BUILTIN_TEMPLATE_NAMES.map((name) => ({
|
|
53989
|
+
name,
|
|
53990
|
+
description: builtinTemplate(name).description ?? ""
|
|
53991
|
+
}));
|
|
53992
|
+
return ok(
|
|
53993
|
+
`${builtins.length} built-in and ${listing.templates.length} saved template(s)` + (listing.skipped ? `; skipped ${listing.skipped} unusable file(s)` : "") + ".",
|
|
53994
|
+
{ builtins, ...listing }
|
|
53995
|
+
);
|
|
53996
|
+
} catch (error2) {
|
|
53997
|
+
return failure("listing templates", error2);
|
|
53998
|
+
}
|
|
53999
|
+
})
|
|
54000
|
+
);
|
|
54001
|
+
const showInput = {
|
|
54002
|
+
name: nameInput,
|
|
54003
|
+
expanded: external_exports.boolean().optional().describe("Also return the fully expanded template (every rule filled from its base)")
|
|
54004
|
+
};
|
|
54005
|
+
server2.registerTool(
|
|
54006
|
+
"show-markdown-template",
|
|
54007
|
+
{
|
|
54008
|
+
description: "Use when: reading a built-in or saved template, to copy it as the start of a new one or to see what an export will do.\nReturns: the template in its portable form (as saved, overrides only), its source, and with expanded: true every rule filled in.\nDo not use when: listing names (list-markdown-templates).\nSafety: read-only.",
|
|
54009
|
+
inputSchema: showInput,
|
|
54010
|
+
outputSchema: loose,
|
|
54011
|
+
annotations: { readOnlyHint: true }
|
|
54012
|
+
},
|
|
54013
|
+
(async ({ name, expanded }) => {
|
|
54014
|
+
try {
|
|
54015
|
+
const builtin = isBuiltinTemplate(name);
|
|
54016
|
+
const template = builtin ? builtinTemplate(name) : store().get(name);
|
|
54017
|
+
const structured = {
|
|
54018
|
+
name,
|
|
54019
|
+
source: builtin ? "builtin" : "saved",
|
|
54020
|
+
template,
|
|
54021
|
+
...expanded ? { expanded: resolveTemplate(template, name) } : {}
|
|
54022
|
+
};
|
|
54023
|
+
return ok(JSON.stringify(template, null, 2), structured);
|
|
54024
|
+
} catch (error2) {
|
|
54025
|
+
return failure("showing template", error2);
|
|
54026
|
+
}
|
|
54027
|
+
})
|
|
54028
|
+
);
|
|
54029
|
+
const validateInput = {
|
|
54030
|
+
name: nameInput.optional(),
|
|
54031
|
+
template: templateInput,
|
|
54032
|
+
templateFile: templateFileInput
|
|
54033
|
+
};
|
|
54034
|
+
server2.registerTool(
|
|
54035
|
+
"validate-markdown-template",
|
|
54036
|
+
{
|
|
54037
|
+
description: 'Use when: checking a template before saving it or exporting with it.\nReturns: valid true, or valid false with every problem as {path, message}, where path is a JSON path such as $.rules["inline.bold"].after.\nDo not use when: you want to store it (save-markdown-template validates too).\nSafety: read-only. Pass exactly one of name (a saved or built-in template), template (JSON object or text) or templateFile.',
|
|
54038
|
+
inputSchema: validateInput,
|
|
54039
|
+
outputSchema: loose,
|
|
54040
|
+
annotations: { readOnlyHint: true }
|
|
54041
|
+
},
|
|
54042
|
+
(async (args) => {
|
|
54043
|
+
try {
|
|
54044
|
+
const given = [args.name, args.template, args.templateFile].filter(
|
|
54045
|
+
(value) => value !== void 0
|
|
54046
|
+
).length;
|
|
54047
|
+
if (given !== 1)
|
|
54048
|
+
throw new Error("Provide exactly one of 'name', 'template' or 'templateFile'.");
|
|
54049
|
+
try {
|
|
54050
|
+
if (args.name !== void 0) {
|
|
54051
|
+
if (!isBuiltinTemplate(args.name)) store().get(args.name);
|
|
54052
|
+
} else parseTemplate(sourceText(args));
|
|
54053
|
+
} catch (error2) {
|
|
54054
|
+
if (!(error2 instanceof TemplateValidationError)) throw error2;
|
|
54055
|
+
return ok(
|
|
54056
|
+
`Invalid template (${error2.errors.length} problem(s)):
|
|
54057
|
+
` + error2.errors.map((e) => `${e.path}: ${e.message}`).join("\n"),
|
|
54058
|
+
{ valid: false, errors: error2.errors }
|
|
54059
|
+
);
|
|
54060
|
+
}
|
|
54061
|
+
return ok("The template is valid.", { valid: true, errors: [] });
|
|
54062
|
+
} catch (error2) {
|
|
54063
|
+
return failure("validating template", error2);
|
|
54064
|
+
}
|
|
54065
|
+
})
|
|
54066
|
+
);
|
|
54067
|
+
const saveInput = {
|
|
54068
|
+
name: external_exports.string().min(1).max(64).describe(
|
|
54069
|
+
'Name to save under: lowercase a-z, 0-9, "-" and "_", starting with a letter or digit'
|
|
54070
|
+
),
|
|
54071
|
+
template: templateInput,
|
|
54072
|
+
templateFile: templateFileInput,
|
|
54073
|
+
force: external_exports.boolean().optional().describe("Replace an existing saved template of this name (default false: create-only)")
|
|
54074
|
+
};
|
|
54075
|
+
server2.registerTool(
|
|
54076
|
+
"save-markdown-template",
|
|
54077
|
+
{
|
|
54078
|
+
description: "Use when: storing a template so export-notes-markdown can use it by name.\nReturns: name, path, bytes and whether an existing template was replaced.\nDo not use when: exporting once (pass templateFile to export-notes-markdown instead).\nSafety: writes one file in the template library only (APPLE_NOTES_MCP_TEMPLATE_DIR, default ~/Library/Application Support/apple-notes-mcp/templates, mode 0600). Validates first; an invalid template is refused with every JSON path. Create-only: an existing name is refused with [template-exists] unless force is true. Built-in names are reserved.",
|
|
54079
|
+
inputSchema: saveInput,
|
|
54080
|
+
outputSchema: loose,
|
|
54081
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
54082
|
+
},
|
|
54083
|
+
(async (args) => {
|
|
54084
|
+
try {
|
|
54085
|
+
const saved = store().save(args.name, sourceText(args), { force: args.force });
|
|
54086
|
+
return ok(
|
|
54087
|
+
`${saved.replaced ? "Replaced" : "Saved"} template "${args.name}" (${saved.bytes} bytes) at ${saved.path}.`,
|
|
54088
|
+
{ name: args.name, path: saved.path, bytes: saved.bytes, replaced: saved.replaced }
|
|
54089
|
+
);
|
|
54090
|
+
} catch (error2) {
|
|
54091
|
+
return failure("saving template", error2);
|
|
54092
|
+
}
|
|
54093
|
+
})
|
|
54094
|
+
);
|
|
54095
|
+
const deleteInput = { name: external_exports.string().min(1).max(64).describe("Saved template to delete") };
|
|
54096
|
+
server2.registerTool(
|
|
54097
|
+
"delete-markdown-template",
|
|
54098
|
+
{
|
|
54099
|
+
description: "Use when: removing a saved Markdown template the user no longer wants.\nReturns: the name and the path of the removed file.\nDo not use when: the template is built in (standard-markdown, obsidian cannot be deleted).\nSafety: permanently removes one file from the template library; never touches Notes.",
|
|
54100
|
+
inputSchema: deleteInput,
|
|
54101
|
+
outputSchema: loose,
|
|
54102
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false }
|
|
54103
|
+
},
|
|
54104
|
+
(async ({ name }) => {
|
|
54105
|
+
try {
|
|
54106
|
+
const removed = store().delete(name);
|
|
54107
|
+
return ok(`Deleted template "${name}" (${removed.path}).`, {
|
|
54108
|
+
name,
|
|
54109
|
+
path: removed.path,
|
|
54110
|
+
deleted: true
|
|
54111
|
+
});
|
|
54112
|
+
} catch (error2) {
|
|
54113
|
+
return failure("deleting template", error2);
|
|
54114
|
+
}
|
|
54115
|
+
})
|
|
54116
|
+
);
|
|
54117
|
+
}
|
|
54118
|
+
|
|
53689
54119
|
// src/utils/svgAnalyzer.ts
|
|
53690
54120
|
import { createHash as createHash5 } from "node:crypto";
|
|
53691
|
-
import { closeSync as
|
|
54121
|
+
import { closeSync as closeSync8, constants as constants8, fstatSync as fstatSync8, openSync as openSync8, readSync as readSync6 } from "node:fs";
|
|
53692
54122
|
|
|
53693
54123
|
// src/utils/svgColor.ts
|
|
53694
54124
|
var NAMED = {
|
|
@@ -54629,7 +55059,7 @@ function parseXml(source, limits) {
|
|
|
54629
55059
|
if (a.name === "xmlns") scope2.set("", a.value);
|
|
54630
55060
|
else if (a.name.startsWith("xmlns:")) scope2.set(a.name.slice(6), a.value);
|
|
54631
55061
|
}
|
|
54632
|
-
const
|
|
55062
|
+
const resolve9 = (qualified, isAttribute) => {
|
|
54633
55063
|
const colon = qualified.indexOf(":");
|
|
54634
55064
|
if (colon < 0) return { local: qualified, ns: isAttribute ? null : scope2.get("") ?? null };
|
|
54635
55065
|
const prefix = qualified.slice(0, colon);
|
|
@@ -54639,12 +55069,12 @@ function parseXml(source, limits) {
|
|
|
54639
55069
|
if (ns === void 0) fail(`Undeclared namespace prefix "${prefix.slice(0, 40)}"`);
|
|
54640
55070
|
return { local, ns };
|
|
54641
55071
|
};
|
|
54642
|
-
const resolved =
|
|
55072
|
+
const resolved = resolve9(name, false);
|
|
54643
55073
|
const element = {
|
|
54644
55074
|
name,
|
|
54645
55075
|
local: resolved.local,
|
|
54646
55076
|
ns: resolved.ns,
|
|
54647
|
-
attributes: rawAttributes.filter((a) => a.name !== "xmlns" && !a.name.startsWith("xmlns:")).map((a) => ({ name: a.name, value: a.value, ...
|
|
55077
|
+
attributes: rawAttributes.filter((a) => a.name !== "xmlns" && !a.name.startsWith("xmlns:")).map((a) => ({ name: a.name, value: a.value, ...resolve9(a.name, true) })),
|
|
54648
55078
|
children: [],
|
|
54649
55079
|
text: "",
|
|
54650
55080
|
line: tagLine
|
|
@@ -55657,14 +56087,14 @@ function analyzeSvgBuffer(source) {
|
|
|
55657
56087
|
function readSvgSource(path10) {
|
|
55658
56088
|
let fd;
|
|
55659
56089
|
try {
|
|
55660
|
-
fd =
|
|
56090
|
+
fd = openSync8(path10, constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK);
|
|
55661
56091
|
} catch (error2) {
|
|
55662
56092
|
if (error2.code === "ELOOP")
|
|
55663
56093
|
throw new SvgError("svg_file_invalid", "The SVG path is a symbolic link");
|
|
55664
56094
|
throw new SvgError("svg_file_invalid", "The SVG file does not exist or cannot be read");
|
|
55665
56095
|
}
|
|
55666
56096
|
try {
|
|
55667
|
-
const info =
|
|
56097
|
+
const info = fstatSync8(fd);
|
|
55668
56098
|
if (!info.isFile())
|
|
55669
56099
|
throw new SvgError("svg_file_invalid", "The SVG path is not a regular file");
|
|
55670
56100
|
if (info.size > SVG_LIMITS.maxSourceBytes)
|
|
@@ -55675,7 +56105,7 @@ function readSvgSource(path10) {
|
|
|
55675
56105
|
const buffer = Buffer.alloc(SVG_LIMITS.maxSourceBytes + 1);
|
|
55676
56106
|
let total = 0;
|
|
55677
56107
|
for (; ; ) {
|
|
55678
|
-
const n =
|
|
56108
|
+
const n = readSync6(fd, buffer, total, buffer.length - total, null);
|
|
55679
56109
|
if (n === 0) break;
|
|
55680
56110
|
total += n;
|
|
55681
56111
|
if (total > SVG_LIMITS.maxSourceBytes)
|
|
@@ -55686,7 +56116,7 @@ function readSvgSource(path10) {
|
|
|
55686
56116
|
}
|
|
55687
56117
|
return buffer.subarray(0, total);
|
|
55688
56118
|
} finally {
|
|
55689
|
-
|
|
56119
|
+
closeSync8(fd);
|
|
55690
56120
|
}
|
|
55691
56121
|
}
|
|
55692
56122
|
function analyzeSvgFile(path10) {
|
|
@@ -56257,7 +56687,7 @@ function registerNativeOperations(server2, manager) {
|
|
|
56257
56687
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
56258
56688
|
import { existsSync as existsSync13 } from "node:fs";
|
|
56259
56689
|
import { release as release2 } from "node:os";
|
|
56260
|
-
import { dirname as dirname7, resolve as
|
|
56690
|
+
import { dirname as dirname7, resolve as resolve6 } from "node:path";
|
|
56261
56691
|
import { fileURLToPath } from "node:url";
|
|
56262
56692
|
var OPTIONAL_BRIDGE_NOTE = "(optional \u2014 needed only for create-note format: markdown, macOS 26+)";
|
|
56263
56693
|
var MARKDOWN_MIN_DARWIN_MAJOR = 25;
|
|
@@ -56280,11 +56710,11 @@ function setupShortcuts(checkOnly, dependencies = {}) {
|
|
|
56280
56710
|
const result = spawnSync2("/usr/bin/open", [path10], { encoding: "utf8" });
|
|
56281
56711
|
return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || result.error?.message || "open failed" };
|
|
56282
56712
|
});
|
|
56283
|
-
const baseDirectory = dependencies.baseDirectory ||
|
|
56713
|
+
const baseDirectory = dependencies.baseDirectory || resolve6(dirname7(fileURLToPath(import.meta.url)), "../shortcuts");
|
|
56284
56714
|
const osRelease = (dependencies.osRelease || release2)();
|
|
56285
56715
|
const darwinMajor = Number.parseInt(osRelease.split(".")[0], 10);
|
|
56286
56716
|
const items = shortcutFiles.map(({ name, file, optional: optional2 }) => {
|
|
56287
|
-
const path10 =
|
|
56717
|
+
const path10 = resolve6(baseDirectory, file);
|
|
56288
56718
|
let installed = false;
|
|
56289
56719
|
let identifier;
|
|
56290
56720
|
let error2;
|
|
@@ -56348,15 +56778,15 @@ import { createHash as createHash6 } from "node:crypto";
|
|
|
56348
56778
|
import {
|
|
56349
56779
|
chmodSync,
|
|
56350
56780
|
existsSync as existsSync14,
|
|
56351
|
-
mkdirSync as
|
|
56781
|
+
mkdirSync as mkdirSync8,
|
|
56352
56782
|
mkdtempSync as mkdtempSync6,
|
|
56353
56783
|
readFileSync as readFileSync4,
|
|
56354
|
-
renameSync,
|
|
56784
|
+
renameSync as renameSync2,
|
|
56355
56785
|
rmSync as rmSync6,
|
|
56356
56786
|
writeFileSync as writeFileSync5
|
|
56357
56787
|
} from "node:fs";
|
|
56358
|
-
import { homedir as
|
|
56359
|
-
import { dirname as dirname8, join as
|
|
56788
|
+
import { homedir as homedir20, release as release3 } from "node:os";
|
|
56789
|
+
import { dirname as dirname8, join as join26, resolve as resolve7 } from "node:path";
|
|
56360
56790
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
56361
56791
|
var PUBLIC_HELPER_PROTOCOL = 1;
|
|
56362
56792
|
var PUBLIC_HELPER_DIR_ENV = "APPLE_NOTES_MCP_PUBLIC_HELPER_DIR";
|
|
@@ -56384,7 +56814,7 @@ var publicManifestSchema = external_exports.object({
|
|
|
56384
56814
|
function packageRoot(fromDir = dirname8(fileURLToPath2(import.meta.url))) {
|
|
56385
56815
|
let dir = fromDir;
|
|
56386
56816
|
for (; ; ) {
|
|
56387
|
-
const candidate =
|
|
56817
|
+
const candidate = join26(dir, "package.json");
|
|
56388
56818
|
if (existsSync14(candidate)) {
|
|
56389
56819
|
try {
|
|
56390
56820
|
if (JSON.parse(readFileSync4(candidate, "utf8")).name === "apple-notes-mcp")
|
|
@@ -56393,7 +56823,7 @@ function packageRoot(fromDir = dirname8(fileURLToPath2(import.meta.url))) {
|
|
|
56393
56823
|
}
|
|
56394
56824
|
}
|
|
56395
56825
|
const parent = dirname8(dir);
|
|
56396
|
-
if (parent === dir) return
|
|
56826
|
+
if (parent === dir) return resolve7(fromDir, "..");
|
|
56397
56827
|
dir = parent;
|
|
56398
56828
|
}
|
|
56399
56829
|
}
|
|
@@ -56401,7 +56831,7 @@ function defaultPublicHelperDeps(overrides = {}) {
|
|
|
56401
56831
|
return {
|
|
56402
56832
|
env: process.env,
|
|
56403
56833
|
platform: process.platform,
|
|
56404
|
-
sourcePath:
|
|
56834
|
+
sourcePath: join26(packageRoot(), PUBLIC_HELPER_SOURCE),
|
|
56405
56835
|
exists: existsSync14,
|
|
56406
56836
|
readFile: (path10) => readFileSync4(path10),
|
|
56407
56837
|
spawn: spawnSync3,
|
|
@@ -56412,14 +56842,14 @@ function defaultPublicHelperDeps(overrides = {}) {
|
|
|
56412
56842
|
function publicHelperInstallDir(env = process.env) {
|
|
56413
56843
|
const override = env[PUBLIC_HELPER_DIR_ENV]?.trim();
|
|
56414
56844
|
if (override) return override;
|
|
56415
|
-
return
|
|
56845
|
+
return join26(homedir20(), "Library", "Application Support", "apple-notes-mcp", "public-helper");
|
|
56416
56846
|
}
|
|
56417
56847
|
function sha256Hex(data) {
|
|
56418
56848
|
return createHash6("sha256").update(data).digest("hex");
|
|
56419
56849
|
}
|
|
56420
56850
|
function inspectPublicHelper(deps = defaultPublicHelperDeps()) {
|
|
56421
56851
|
const installDir = publicHelperInstallDir(deps.env);
|
|
56422
|
-
const binaryPath =
|
|
56852
|
+
const binaryPath = join26(installDir, PUBLIC_HELPER_BINARY);
|
|
56423
56853
|
const base = { installDir, binaryPath, sourcePath: deps.sourcePath, manifest: null };
|
|
56424
56854
|
const fail = (reason, detail) => ({
|
|
56425
56855
|
...base,
|
|
@@ -56430,7 +56860,7 @@ function inspectPublicHelper(deps = defaultPublicHelperDeps()) {
|
|
|
56430
56860
|
if (deps.platform !== "darwin") return fail("unsupported_platform", "macOS only");
|
|
56431
56861
|
if (!deps.exists(deps.sourcePath))
|
|
56432
56862
|
return fail("helper_not_installed", `Packaged helper source is missing: ${deps.sourcePath}`);
|
|
56433
|
-
const manifestPath =
|
|
56863
|
+
const manifestPath = join26(installDir, PUBLIC_HELPER_MANIFEST);
|
|
56434
56864
|
if (!deps.exists(binaryPath) || !deps.exists(manifestPath))
|
|
56435
56865
|
return fail(
|
|
56436
56866
|
"helper_not_installed",
|
|
@@ -56522,9 +56952,9 @@ async function callPublicHelperAsync(action, fields = {}, deps = defaultPublicHe
|
|
|
56522
56952
|
});
|
|
56523
56953
|
const chunks = [];
|
|
56524
56954
|
let size = 0;
|
|
56525
|
-
let
|
|
56955
|
+
let failure2 = null;
|
|
56526
56956
|
const stop = (error2) => {
|
|
56527
|
-
|
|
56957
|
+
failure2 ??= error2;
|
|
56528
56958
|
child2.kill("SIGKILL");
|
|
56529
56959
|
};
|
|
56530
56960
|
const timer = setTimeout(
|
|
@@ -56546,12 +56976,12 @@ async function callPublicHelperAsync(action, fields = {}, deps = defaultPublicHe
|
|
|
56546
56976
|
child2.on("error", (error2) => {
|
|
56547
56977
|
settle();
|
|
56548
56978
|
reject(
|
|
56549
|
-
|
|
56979
|
+
failure2 ?? new PublicHelperError("helper_unreachable", `Could not run the helper: ${error2.message}`)
|
|
56550
56980
|
);
|
|
56551
56981
|
});
|
|
56552
56982
|
child2.on("close", (status, exitSignal) => {
|
|
56553
56983
|
settle();
|
|
56554
|
-
if (
|
|
56984
|
+
if (failure2) return reject(failure2);
|
|
56555
56985
|
if (status === null && exitSignal)
|
|
56556
56986
|
return reject(
|
|
56557
56987
|
new PublicHelperError("helper_crashed", `The helper stopped on signal ${exitSignal}.`)
|
|
@@ -56669,8 +57099,8 @@ function publicHelperCompileArguments(sourcePath, digestPath, plistPath, outputP
|
|
|
56669
57099
|
}
|
|
56670
57100
|
function buildPublicHelper(checkOnly, deps = defaultPublicHelperBuildDeps()) {
|
|
56671
57101
|
const steps = [];
|
|
56672
|
-
const done = (
|
|
56673
|
-
ok,
|
|
57102
|
+
const done = (ok2) => ({
|
|
57103
|
+
ok: ok2,
|
|
56674
57104
|
checkOnly,
|
|
56675
57105
|
steps,
|
|
56676
57106
|
installation: inspectPublicHelper(deps)
|
|
@@ -56706,12 +57136,12 @@ function buildPublicHelper(checkOnly, deps = defaultPublicHelperBuildDeps()) {
|
|
|
56706
57136
|
const compiler = String(version3.stdout || version3.stderr || "").split("\n").find((line) => line.includes("Swift version"))?.trim() || "swiftc";
|
|
56707
57137
|
steps.push({ step: "find compiler", ok: true, detail: compiler });
|
|
56708
57138
|
const installDir = publicHelperInstallDir(deps.env);
|
|
56709
|
-
|
|
56710
|
-
const staging = mkdtempSync6(
|
|
57139
|
+
mkdirSync8(installDir, { recursive: true, mode: 448 });
|
|
57140
|
+
const staging = mkdtempSync6(join26(installDir, ".staging-"));
|
|
56711
57141
|
try {
|
|
56712
|
-
const stagedBinary =
|
|
56713
|
-
const digestPath =
|
|
56714
|
-
const plistPath =
|
|
57142
|
+
const stagedBinary = join26(staging, PUBLIC_HELPER_BINARY);
|
|
57143
|
+
const digestPath = join26(staging, "source-digest.swift");
|
|
57144
|
+
const plistPath = join26(staging, "Info.plist");
|
|
56715
57145
|
writeFileSync5(digestPath, sourceDigestSwift(sourceSha), { mode: 384 });
|
|
56716
57146
|
writeFileSync5(plistPath, publicHelperInfoPlist(), { mode: 384 });
|
|
56717
57147
|
const compile = deps.spawn(
|
|
@@ -56774,9 +57204,9 @@ function buildPublicHelper(checkOnly, deps = defaultPublicHelperBuildDeps()) {
|
|
|
56774
57204
|
compiler
|
|
56775
57205
|
};
|
|
56776
57206
|
chmodSync(stagedBinary, 448);
|
|
56777
|
-
|
|
57207
|
+
renameSync2(stagedBinary, join26(installDir, PUBLIC_HELPER_BINARY));
|
|
56778
57208
|
writeFileSync5(
|
|
56779
|
-
|
|
57209
|
+
join26(installDir, PUBLIC_HELPER_MANIFEST),
|
|
56780
57210
|
JSON.stringify(manifest, null, 2) + "\n",
|
|
56781
57211
|
{ mode: 384 }
|
|
56782
57212
|
);
|
|
@@ -56999,11 +57429,11 @@ function getNoteDrawings(noteId3, options = {}) {
|
|
|
56999
57429
|
if (!install.ready)
|
|
57000
57430
|
throw new PublicHelperError(install.reason ?? "helper_not_installed", install.detail ?? "");
|
|
57001
57431
|
const drawings = rows.map((row) => decodeRow(row, format, includePoints, deps));
|
|
57002
|
-
const
|
|
57432
|
+
const ok2 = drawings.filter((d) => d.status === "ok").length;
|
|
57003
57433
|
return {
|
|
57004
57434
|
id: noteId3,
|
|
57005
57435
|
drawingCount: drawings.length,
|
|
57006
|
-
status:
|
|
57436
|
+
status: ok2 === drawings.length ? "ok" : ok2 ? "partial" : "error",
|
|
57007
57437
|
drawings
|
|
57008
57438
|
};
|
|
57009
57439
|
}
|
|
@@ -57020,8 +57450,8 @@ function formatNoteDrawings(result) {
|
|
|
57020
57450
|
}
|
|
57021
57451
|
|
|
57022
57452
|
// src/utils/noteAudio.ts
|
|
57023
|
-
import { readdirSync as
|
|
57024
|
-
import { join as
|
|
57453
|
+
import { readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
|
|
57454
|
+
import { join as join27 } from "node:path";
|
|
57025
57455
|
var EXTRA_AUDIO_UTIS = [
|
|
57026
57456
|
"public.mp3",
|
|
57027
57457
|
"public.aiff-audio",
|
|
@@ -57046,7 +57476,7 @@ function accountDirsFor(containerDir, accountIdentifier) {
|
|
|
57046
57476
|
if (own) return [own];
|
|
57047
57477
|
let names;
|
|
57048
57478
|
try {
|
|
57049
|
-
names =
|
|
57479
|
+
names = readdirSync5(join27(containerDir, "Accounts")).sort();
|
|
57050
57480
|
} catch {
|
|
57051
57481
|
return [];
|
|
57052
57482
|
}
|
|
@@ -57058,8 +57488,8 @@ function resolveMediaPath(media, accountIdentifier = null, containerDir = NOTES_
|
|
|
57058
57488
|
if (!id2 || !filename) return null;
|
|
57059
57489
|
const generation = safeComponent(media?.generation);
|
|
57060
57490
|
for (const dir of accountDirsFor(containerDir, accountIdentifier)) {
|
|
57061
|
-
const base =
|
|
57062
|
-
const candidates = generation ? [
|
|
57491
|
+
const base = join27(dir, "Media", id2);
|
|
57492
|
+
const candidates = generation ? [join27(base, generation, filename), join27(base, filename)] : [join27(base, filename)];
|
|
57063
57493
|
for (const candidate of candidates) {
|
|
57064
57494
|
const real = realInside(candidate, dir);
|
|
57065
57495
|
if (real && isFile2(real)) return real;
|
|
@@ -57331,21 +57761,21 @@ import { spawnSync as spawnSync5 } from "node:child_process";
|
|
|
57331
57761
|
import {
|
|
57332
57762
|
chmodSync as chmodSync2,
|
|
57333
57763
|
existsSync as existsSync16,
|
|
57334
|
-
mkdirSync as
|
|
57764
|
+
mkdirSync as mkdirSync9,
|
|
57335
57765
|
mkdtempSync as mkdtempSync7,
|
|
57336
|
-
renameSync as
|
|
57766
|
+
renameSync as renameSync3,
|
|
57337
57767
|
rmSync as rmSync7,
|
|
57338
57768
|
writeFileSync as writeFileSync6
|
|
57339
57769
|
} from "node:fs";
|
|
57340
57770
|
import { release as release4 } from "node:os";
|
|
57341
|
-
import { join as
|
|
57771
|
+
import { join as join29 } from "node:path";
|
|
57342
57772
|
|
|
57343
57773
|
// src/services/privateHelper.ts
|
|
57344
57774
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
57345
57775
|
import { createHash as createHash7 } from "node:crypto";
|
|
57346
57776
|
import { existsSync as existsSync15, readFileSync as readFileSync5 } from "node:fs";
|
|
57347
|
-
import { homedir as
|
|
57348
|
-
import { dirname as dirname9, join as
|
|
57777
|
+
import { homedir as homedir21 } from "node:os";
|
|
57778
|
+
import { dirname as dirname9, join as join28, resolve as resolve8 } from "node:path";
|
|
57349
57779
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
57350
57780
|
var PRIVATE_HELPER_PROTOCOL = 1;
|
|
57351
57781
|
var ENABLE_ENV = "APPLE_NOTES_MCP_ENABLE_PRIVATE";
|
|
@@ -57373,7 +57803,7 @@ var manifestSchema = external_exports.object({
|
|
|
57373
57803
|
function packageRoot2(fromDir = dirname9(fileURLToPath3(import.meta.url))) {
|
|
57374
57804
|
let dir = fromDir;
|
|
57375
57805
|
for (; ; ) {
|
|
57376
|
-
const candidate =
|
|
57806
|
+
const candidate = join28(dir, "package.json");
|
|
57377
57807
|
if (existsSync15(candidate)) {
|
|
57378
57808
|
try {
|
|
57379
57809
|
const pkg = JSON.parse(readFileSync5(candidate, "utf8"));
|
|
@@ -57382,7 +57812,7 @@ function packageRoot2(fromDir = dirname9(fileURLToPath3(import.meta.url))) {
|
|
|
57382
57812
|
}
|
|
57383
57813
|
}
|
|
57384
57814
|
const parent = dirname9(dir);
|
|
57385
|
-
if (parent === dir) return
|
|
57815
|
+
if (parent === dir) return resolve8(fromDir, "..");
|
|
57386
57816
|
dir = parent;
|
|
57387
57817
|
}
|
|
57388
57818
|
}
|
|
@@ -57390,7 +57820,7 @@ function defaultDeps2(overrides = {}) {
|
|
|
57390
57820
|
return {
|
|
57391
57821
|
env: process.env,
|
|
57392
57822
|
platform: process.platform,
|
|
57393
|
-
sourcePath:
|
|
57823
|
+
sourcePath: join28(packageRoot2(), HELPER_SOURCE_RELATIVE),
|
|
57394
57824
|
exists: existsSync15,
|
|
57395
57825
|
readFile: (path10) => readFileSync5(path10),
|
|
57396
57826
|
spawn: spawnSync4,
|
|
@@ -57403,14 +57833,14 @@ function privateHelperEnabled(env = process.env) {
|
|
|
57403
57833
|
function helperInstallDir(env = process.env) {
|
|
57404
57834
|
const override = env[HELPER_DIR_ENV]?.trim();
|
|
57405
57835
|
if (override) return override;
|
|
57406
|
-
return
|
|
57836
|
+
return join28(homedir21(), "Library", "Application Support", "apple-notes-mcp", "private-helper");
|
|
57407
57837
|
}
|
|
57408
57838
|
function sha256Hex2(data) {
|
|
57409
57839
|
return createHash7("sha256").update(data).digest("hex");
|
|
57410
57840
|
}
|
|
57411
57841
|
function inspectInstallation(deps = defaultDeps2()) {
|
|
57412
57842
|
const installDir = helperInstallDir(deps.env);
|
|
57413
|
-
const binaryPath =
|
|
57843
|
+
const binaryPath = join28(installDir, HELPER_BINARY_NAME);
|
|
57414
57844
|
const base = {
|
|
57415
57845
|
installDir,
|
|
57416
57846
|
binaryPath,
|
|
@@ -57428,7 +57858,7 @@ function inspectInstallation(deps = defaultDeps2()) {
|
|
|
57428
57858
|
if (!deps.exists(deps.sourcePath))
|
|
57429
57859
|
return fail("helper_not_installed", `Packaged helper source is missing: ${deps.sourcePath}`);
|
|
57430
57860
|
base.expectedSourceSha256 = sha256Hex2(deps.readFile(deps.sourcePath));
|
|
57431
|
-
const manifestPath =
|
|
57861
|
+
const manifestPath = join28(installDir, MANIFEST_NAME);
|
|
57432
57862
|
if (!deps.exists(binaryPath) || !deps.exists(manifestPath))
|
|
57433
57863
|
return fail(
|
|
57434
57864
|
"helper_not_installed",
|
|
@@ -57699,8 +58129,8 @@ function compileArguments(sourcePath, outputPath, sourceSha) {
|
|
|
57699
58129
|
}
|
|
57700
58130
|
function buildPrivateHelper(checkOnly, deps = defaultBuildDeps()) {
|
|
57701
58131
|
const steps = [];
|
|
57702
|
-
const done = (
|
|
57703
|
-
ok,
|
|
58132
|
+
const done = (ok2) => ({
|
|
58133
|
+
ok: ok2,
|
|
57704
58134
|
checkOnly,
|
|
57705
58135
|
steps,
|
|
57706
58136
|
installation: inspectInstallation(deps)
|
|
@@ -57742,10 +58172,10 @@ function buildPrivateHelper(checkOnly, deps = defaultBuildDeps()) {
|
|
|
57742
58172
|
const compiler = String(clangVersion.stdout || "").split("\n")[0] || "clang";
|
|
57743
58173
|
steps.push({ step: "find compiler", ok: true, detail: compiler });
|
|
57744
58174
|
const installDir = helperInstallDir(deps.env);
|
|
57745
|
-
|
|
57746
|
-
const staging = mkdtempSync7(
|
|
58175
|
+
mkdirSync9(installDir, { recursive: true, mode: 448 });
|
|
58176
|
+
const staging = mkdtempSync7(join29(installDir, ".staging-"));
|
|
57747
58177
|
try {
|
|
57748
|
-
const stagedBinary =
|
|
58178
|
+
const stagedBinary = join29(staging, HELPER_BINARY_NAME);
|
|
57749
58179
|
const compile = deps.spawn(
|
|
57750
58180
|
"/usr/bin/xcrun",
|
|
57751
58181
|
compileArguments(deps.sourcePath, stagedBinary, sourceSha),
|
|
@@ -57822,8 +58252,8 @@ function buildPrivateHelper(checkOnly, deps = defaultBuildDeps()) {
|
|
|
57822
58252
|
compiler
|
|
57823
58253
|
};
|
|
57824
58254
|
chmodSync2(stagedBinary, 448);
|
|
57825
|
-
|
|
57826
|
-
writeFileSync6(
|
|
58255
|
+
renameSync3(stagedBinary, join29(installDir, HELPER_BINARY_NAME));
|
|
58256
|
+
writeFileSync6(join29(installDir, MANIFEST_NAME), JSON.stringify(manifest, null, 2) + "\n", {
|
|
57827
58257
|
mode: 384
|
|
57828
58258
|
});
|
|
57829
58259
|
steps.push({ step: "install", ok: true, detail: installDir });
|
|
@@ -57991,6 +58421,7 @@ var server = new McpServer({
|
|
|
57991
58421
|
var notesManager = new AppleNotesManager();
|
|
57992
58422
|
registerDirectOperations(server, notesManager);
|
|
57993
58423
|
registerFolderDelete(server, notesManager);
|
|
58424
|
+
registerMarkdownTemplates(server);
|
|
57994
58425
|
registerSvgAnalysis(server);
|
|
57995
58426
|
registerNativeTagsBridge(server, notesManager);
|
|
57996
58427
|
registerNativeOperations(server, notesManager);
|
|
@@ -60808,7 +61239,7 @@ registerTool(
|
|
|
60808
61239
|
assetsDir: exportPathInput("Directory that receives copies of attachment files"),
|
|
60809
61240
|
wrap: external_exports.number().int().min(0).max(1e3).optional().describe("Hard-wrap prose at this many columns (0 or omitted: no wrapping)"),
|
|
60810
61241
|
template: external_exports.string().min(1).max(64).optional().describe(
|
|
60811
|
-
`Render through this template: built-in ${BUILTIN_TEMPLATE_NAMES.map((n) => `'${n}'`).join(" or ")}. Exclusive with templateFile`
|
|
61242
|
+
`Render through this template: built-in ${BUILTIN_TEMPLATE_NAMES.map((n) => `'${n}'`).join(" or ")}, or a saved template's name (list-markdown-templates). Exclusive with templateFile`
|
|
60812
61243
|
),
|
|
60813
61244
|
templateFile: exportPathInput(
|
|
60814
61245
|
"JSON template file (.json) to render through (exclusive with template; at most 256 KiB)"
|
|
@@ -60837,6 +61268,7 @@ registerTool(
|
|
|
60837
61268
|
try {
|
|
60838
61269
|
receipt = exportNotesMarkdown(request, {
|
|
60839
61270
|
listNoteRefs: (account, folder, since, limit) => notesManager.listNoteRefs(account, folder, since, limit),
|
|
61271
|
+
findTemplate: (name) => TEMPLATE_SLUG.test(name) ? new TemplateStore().find(name) : void 0,
|
|
60840
61272
|
// The document travels twice (text and structuredContent).
|
|
60841
61273
|
maxInlineBytes: Math.floor(exportMaxResponseBytes() / 2) - 64 * 1024
|
|
60842
61274
|
});
|
|
@@ -11,11 +11,20 @@ Templates change only the exported Markdown. They never change a note.
|
|
|
11
11
|
|
|
12
12
|
## Choosing a template
|
|
13
13
|
|
|
14
|
+
Built-in and saved templates are managed with `list-markdown-templates`,
|
|
15
|
+
`show-markdown-template`, `validate-markdown-template`,
|
|
16
|
+
`save-markdown-template` and `delete-markdown-template`. Saved templates live
|
|
17
|
+
in `~/Library/Application Support/apple-notes-mcp/templates` (or the absolute
|
|
18
|
+
directory in `APPLE_NOTES_MCP_TEMPLATE_DIR`) as `<name>.json`, where the name
|
|
19
|
+
is a lowercase slug of `a-z`, `0-9`, `-` and `_`. Saving is create-only unless
|
|
20
|
+
`force` is true, and the built-in names are reserved. The library skips files
|
|
21
|
+
it cannot read or validate instead of trusting them.
|
|
22
|
+
|
|
14
23
|
Pass exactly one of these to `export-notes-markdown`:
|
|
15
24
|
|
|
16
25
|
| Parameter | Meaning |
|
|
17
26
|
| --- | --- |
|
|
18
|
-
| `template` | A built-in name
|
|
27
|
+
| `template` | A built-in name (`standard-markdown` or `obsidian`) or a saved template's name |
|
|
19
28
|
| `templateFile` | An absolute path to a JSON template file ending in `.json` (home, a temp directory, or `/Volumes`; at most 256 KiB; symlinks refused) |
|
|
20
29
|
|
|
21
30
|
The template is read and validated before any note is opened. An invalid
|
package/package.json
CHANGED