apple-notes-mcp 2.0.0 → 2.0.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.
|
@@ -634,7 +634,7 @@ export class AppleNotesManager {
|
|
|
634
634
|
*/
|
|
635
635
|
searchNotes(query, searchContent = false, account, folder, modifiedSince, limit) {
|
|
636
636
|
const targetAccount = this.resolveAccount(account);
|
|
637
|
-
const safeQuery =
|
|
637
|
+
const safeQuery = escapePlainStringForAppleScript(query);
|
|
638
638
|
const safeLimit = limit !== undefined && limit > 0 ? Math.floor(limit) : undefined;
|
|
639
639
|
// Build the where clause based on search type
|
|
640
640
|
// AppleScript uses 'name' for title and 'body' for content
|
|
@@ -738,7 +738,7 @@ export class AppleNotesManager {
|
|
|
738
738
|
*/
|
|
739
739
|
getNoteContent(title, account) {
|
|
740
740
|
const targetAccount = this.resolveAccount(account);
|
|
741
|
-
const safeTitle =
|
|
741
|
+
const safeTitle = escapePlainStringForAppleScript(title);
|
|
742
742
|
// Retrieve the body property of the note
|
|
743
743
|
const getCommand = `get body of note "${safeTitle}"`;
|
|
744
744
|
const script = buildAccountScopedScript({ account: targetAccount }, getCommand);
|
|
@@ -830,7 +830,7 @@ export class AppleNotesManager {
|
|
|
830
830
|
*/
|
|
831
831
|
getNoteDetails(title, account) {
|
|
832
832
|
const targetAccount = this.resolveAccount(account);
|
|
833
|
-
const safeTitle =
|
|
833
|
+
const safeTitle = escapePlainStringForAppleScript(title);
|
|
834
834
|
// Fetch multiple properties at once
|
|
835
835
|
const getCommand = `
|
|
836
836
|
set n to note "${safeTitle}"
|
|
@@ -875,7 +875,7 @@ export class AppleNotesManager {
|
|
|
875
875
|
*/
|
|
876
876
|
deleteNote(title, account) {
|
|
877
877
|
const targetAccount = this.resolveAccount(account);
|
|
878
|
-
const safeTitle =
|
|
878
|
+
const safeTitle = escapePlainStringForAppleScript(title);
|
|
879
879
|
const deleteCommand = `delete note "${safeTitle}"`;
|
|
880
880
|
const script = buildAccountScopedScript({ account: targetAccount }, deleteCommand);
|
|
881
881
|
const result = executeAppleScript(script);
|
|
@@ -931,7 +931,7 @@ export class AppleNotesManager {
|
|
|
931
931
|
validateLength(newTitle, MAX_TITLE_LENGTH, "Note title");
|
|
932
932
|
validateLength(newContent, MAX_CONTENT_LENGTH, "Note content");
|
|
933
933
|
const targetAccount = this.resolveAccount(account);
|
|
934
|
-
const safeCurrentTitle =
|
|
934
|
+
const safeCurrentTitle = escapePlainStringForAppleScript(title);
|
|
935
935
|
let fullBody;
|
|
936
936
|
if (format === "html") {
|
|
937
937
|
// HTML mode: content is the complete body, escaped only for AppleScript string
|
|
@@ -1251,7 +1251,7 @@ export class AppleNotesManager {
|
|
|
1251
1251
|
continue;
|
|
1252
1252
|
}
|
|
1253
1253
|
// Folder doesn't exist — create it
|
|
1254
|
-
const segmentName =
|
|
1254
|
+
const segmentName = escapePlainStringForAppleScript(parts[i]);
|
|
1255
1255
|
let createCommand;
|
|
1256
1256
|
if (i === 0) {
|
|
1257
1257
|
createCommand = `make new folder with properties {name:"${segmentName}"}`;
|
|
@@ -1699,7 +1699,7 @@ export class AppleNotesManager {
|
|
|
1699
1699
|
*/
|
|
1700
1700
|
listAttachments(title, account) {
|
|
1701
1701
|
const targetAccount = this.resolveAccount(account);
|
|
1702
|
-
const safeTitle =
|
|
1702
|
+
const safeTitle = escapePlainStringForAppleScript(title);
|
|
1703
1703
|
const script = `
|
|
1704
1704
|
tell application "Notes"
|
|
1705
1705
|
tell account "${targetAccount}"
|
|
@@ -1759,8 +1759,8 @@ export class AppleNotesManager {
|
|
|
1759
1759
|
return { success: false, error: e instanceof Error ? e.message : String(e) };
|
|
1760
1760
|
}
|
|
1761
1761
|
const safeNoteId = sanitizeId(noteId);
|
|
1762
|
-
const safeAttId =
|
|
1763
|
-
const safePath =
|
|
1762
|
+
const safeAttId = escapePlainStringForAppleScript(attachmentId);
|
|
1763
|
+
const safePath = escapePlainStringForAppleScript(abs);
|
|
1764
1764
|
const script = `
|
|
1765
1765
|
tell application "Notes"
|
|
1766
1766
|
set theNote to note id "${safeNoteId}"
|
|
@@ -633,6 +633,15 @@ describe("AppleNotesManager", () => {
|
|
|
633
633
|
const content = manager.getNoteContent("Missing Note");
|
|
634
634
|
expect(content).toBe("");
|
|
635
635
|
});
|
|
636
|
+
it("looks up titles containing & literally, not HTML-escaped (regression)", () => {
|
|
637
|
+
// Bug found in live testing: titles with "&" were HTML-escaped to "&"
|
|
638
|
+
// in the `note "..."` lookup, so the note could never be found.
|
|
639
|
+
mockExecuteAppleScript.mockReturnValue({ success: true, output: "<div>x</div>" });
|
|
640
|
+
manager.getNoteContent("Tom & Jerry", "iCloud");
|
|
641
|
+
const script = mockExecuteAppleScript.mock.calls[0][0];
|
|
642
|
+
expect(script).toContain("Tom & Jerry");
|
|
643
|
+
expect(script).not.toContain("Tom & Jerry");
|
|
644
|
+
});
|
|
636
645
|
it("uses specified account", () => {
|
|
637
646
|
mockExecuteAppleScript.mockReturnValue({
|
|
638
647
|
success: true,
|