apple-notes-mcp 1.3.0 → 1.3.1
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
CHANGED
|
@@ -107,10 +107,10 @@ Creates a new note in Apple Notes.
|
|
|
107
107
|
|
|
108
108
|
| Parameter | Type | Required | Description |
|
|
109
109
|
|-----------|------|----------|-------------|
|
|
110
|
-
| `title` | string | Yes | The title of the note
|
|
111
|
-
| `content` | string | Yes | The body content of the note |
|
|
110
|
+
| `title` | string | Yes | The title of the note. Automatically prepended as `<h1>` — do NOT include the title in `content` |
|
|
111
|
+
| `content` | string | Yes | The body content of the note (do not repeat the title here) |
|
|
112
112
|
| `tags` | string[] | No | Tags for organization (stored in metadata) |
|
|
113
|
-
| `format` | string | No | Content format: `"plaintext"` (default) or `"html"`.
|
|
113
|
+
| `format` | string | No | Content format: `"plaintext"` (default) or `"html"`. In both formats, the title is automatically prepended as `<h1>`. In plaintext mode, newlines become `<br>`, tabs become `<br>`, and backslashes are preserved as HTML entities |
|
|
114
114
|
|
|
115
115
|
**Example:**
|
|
116
116
|
```json
|
|
@@ -125,11 +125,13 @@ Creates a new note in Apple Notes.
|
|
|
125
125
|
```json
|
|
126
126
|
{
|
|
127
127
|
"title": "Status Report",
|
|
128
|
-
"content": "<
|
|
128
|
+
"content": "<h2>Summary</h2><p>All tasks <b>on track</b>.</p><ul><li>Feature A: complete</li><li>Feature B: in progress</li></ul>",
|
|
129
129
|
"format": "html"
|
|
130
130
|
}
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
> **Note:** The title is automatically prepended as `<h1>` in both plaintext and HTML formats. Do not include a `<h1>` title tag in the `content` parameter, or the title will appear twice.
|
|
134
|
+
|
|
133
135
|
**Returns:** Confirmation message with note title and ID. Save the ID for subsequent operations like `update-note`, `delete-note`, etc.
|
|
134
136
|
|
|
135
137
|
---
|
|
@@ -293,7 +295,7 @@ Updates an existing note's content and/or title.
|
|
|
293
295
|
```json
|
|
294
296
|
{
|
|
295
297
|
"id": "x-coredata://ABC123/ICNote/p456",
|
|
296
|
-
"newContent": "<
|
|
298
|
+
"newContent": "<p>New findings with <b>bold</b> emphasis.</p><pre><code>console.log('hello');</code></pre>",
|
|
297
299
|
"format": "html"
|
|
298
300
|
}
|
|
299
301
|
```
|
|
@@ -399,32 +399,41 @@ export class AppleNotesManager {
|
|
|
399
399
|
* // Create in a different account
|
|
400
400
|
* const gmail = manager.createNote("Draft", "...", [], undefined, "Gmail");
|
|
401
401
|
*
|
|
402
|
-
* // Create with HTML formatting
|
|
403
|
-
* const html = manager.createNote("Report", "<
|
|
402
|
+
* // Create with HTML formatting (no need for <h1> — title is auto-prepended)
|
|
403
|
+
* const html = manager.createNote("Report", "<p>Details here</p>",
|
|
404
404
|
* [], undefined, undefined, "html");
|
|
405
405
|
* ```
|
|
406
406
|
*/
|
|
407
407
|
createNote(title, content, tags = [], folder, account, format = "plaintext") {
|
|
408
408
|
const targetAccount = this.resolveAccount(account);
|
|
409
|
-
//
|
|
410
|
-
|
|
411
|
-
|
|
409
|
+
// Build body HTML: title as <h1>, content follows.
|
|
410
|
+
// We set only 'body' (not 'name') to avoid title duplication —
|
|
411
|
+
// Notes.app auto-uses the first line of body as the note's display title.
|
|
412
|
+
const htmlTitle = title.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
413
|
+
const bodyContent = format === "html"
|
|
414
|
+
? content
|
|
415
|
+
: content
|
|
416
|
+
.replace(/&/g, "&")
|
|
417
|
+
.replace(/\\/g, "\")
|
|
418
|
+
.replace(/</g, "<")
|
|
419
|
+
.replace(/>/g, ">")
|
|
420
|
+
.replace(/\n/g, "<br>")
|
|
421
|
+
.replace(/\t/g, "<br>");
|
|
422
|
+
const safeBody = escapeHtmlForAppleScript(`<h1>${htmlTitle}</h1>${bodyContent}`);
|
|
412
423
|
// Build the AppleScript command
|
|
413
|
-
// Notes.app uses 'name' for the title and 'body' for content
|
|
414
|
-
// We capture the ID of the newly created note
|
|
415
424
|
let createCommand;
|
|
416
425
|
if (folder) {
|
|
417
426
|
// Create note in specific folder
|
|
418
427
|
const safeFolder = escapeForAppleScript(folder);
|
|
419
428
|
createCommand = `
|
|
420
|
-
set newNote to make new note at folder "${safeFolder}" with properties {
|
|
429
|
+
set newNote to make new note at folder "${safeFolder}" with properties {body:"${safeBody}"}
|
|
421
430
|
return id of newNote
|
|
422
431
|
`;
|
|
423
432
|
}
|
|
424
433
|
else {
|
|
425
434
|
// Create note in default location
|
|
426
435
|
createCommand = `
|
|
427
|
-
set newNote to make new note with properties {
|
|
436
|
+
set newNote to make new note with properties {body:"${safeBody}"}
|
|
428
437
|
return id of newNote
|
|
429
438
|
`;
|
|
430
439
|
}
|
|
@@ -333,6 +333,62 @@ describe("AppleNotesManager", () => {
|
|
|
333
333
|
// Double quotes must be escaped for AppleScript string embedding
|
|
334
334
|
expect(mockExecuteAppleScript).toHaveBeenCalledWith(expect.stringContaining('<div class=\\"test\\">Content</div>'));
|
|
335
335
|
});
|
|
336
|
+
it("sets title as h1 in body, not as name property", () => {
|
|
337
|
+
mockExecuteAppleScript.mockReturnValue({
|
|
338
|
+
success: true,
|
|
339
|
+
output: "note id x-coredata://12345/ICNote/p203",
|
|
340
|
+
});
|
|
341
|
+
manager.createNote("My Title", "Body content");
|
|
342
|
+
const script = mockExecuteAppleScript.mock.calls[0][0];
|
|
343
|
+
// Title must appear as h1 in body
|
|
344
|
+
expect(script).toContain("<h1>My Title</h1>");
|
|
345
|
+
// name property must NOT be set (causes title duplication in Notes.app)
|
|
346
|
+
expect(script).not.toContain('name:"My Title"');
|
|
347
|
+
});
|
|
348
|
+
it("HTML-encodes special chars in title for h1 tag", () => {
|
|
349
|
+
mockExecuteAppleScript.mockReturnValue({
|
|
350
|
+
success: true,
|
|
351
|
+
output: "note id x-coredata://12345/ICNote/p204",
|
|
352
|
+
});
|
|
353
|
+
manager.createNote("Q&A: <Hello> World", "Content");
|
|
354
|
+
expect(mockExecuteAppleScript).toHaveBeenCalledWith(expect.stringContaining("<h1>Q&A: <Hello> World</h1>"));
|
|
355
|
+
});
|
|
356
|
+
it("HTML-encodes special chars in plaintext content", () => {
|
|
357
|
+
mockExecuteAppleScript.mockReturnValue({
|
|
358
|
+
success: true,
|
|
359
|
+
output: "note id x-coredata://12345/ICNote/p205",
|
|
360
|
+
});
|
|
361
|
+
manager.createNote("Title", "Price: <10 & >5\nNext line");
|
|
362
|
+
const script = mockExecuteAppleScript.mock.calls[0][0];
|
|
363
|
+
expect(script).toContain("Price: <10 & >5<br>Next line");
|
|
364
|
+
});
|
|
365
|
+
it("prepends h1 title before html content", () => {
|
|
366
|
+
mockExecuteAppleScript.mockReturnValue({
|
|
367
|
+
success: true,
|
|
368
|
+
output: "note id x-coredata://12345/ICNote/p206",
|
|
369
|
+
});
|
|
370
|
+
manager.createNote("Report", "<h2>Section</h2><div>Details</div>", [], undefined, undefined, "html");
|
|
371
|
+
const script = mockExecuteAppleScript.mock.calls[0][0];
|
|
372
|
+
expect(script).toContain("<h1>Report</h1><h2>Section</h2><div>Details</div>");
|
|
373
|
+
});
|
|
374
|
+
it("encodes backslashes as HTML entities in plaintext content", () => {
|
|
375
|
+
mockExecuteAppleScript.mockReturnValue({
|
|
376
|
+
success: true,
|
|
377
|
+
output: "note id x-coredata://12345/ICNote/p207",
|
|
378
|
+
});
|
|
379
|
+
manager.createNote("Title", "path\\to\\file");
|
|
380
|
+
const script = mockExecuteAppleScript.mock.calls[0][0];
|
|
381
|
+
expect(script).toContain("path\to\file");
|
|
382
|
+
});
|
|
383
|
+
it("converts tabs to br in plaintext content", () => {
|
|
384
|
+
mockExecuteAppleScript.mockReturnValue({
|
|
385
|
+
success: true,
|
|
386
|
+
output: "note id x-coredata://12345/ICNote/p208",
|
|
387
|
+
});
|
|
388
|
+
manager.createNote("Title", "col1\tcol2\tcol3");
|
|
389
|
+
const script = mockExecuteAppleScript.mock.calls[0][0];
|
|
390
|
+
expect(script).toContain("col1<br>col2<br>col3");
|
|
391
|
+
});
|
|
336
392
|
});
|
|
337
393
|
// ---------------------------------------------------------------------------
|
|
338
394
|
// Note Search
|