apple-notes-mcp 2.5.2 → 2.5.4
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
|
@@ -6,6 +6,7 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that e
|
|
|
6
6
|
[](https://www.npmjs.com/package/apple-notes-mcp)
|
|
7
7
|
[](https://www.npmjs.com/package/apple-notes-mcp)
|
|
8
8
|
[](https://github.com/sweetrb/apple-notes-mcp/actions/workflows/ci.yml)
|
|
9
|
+
[](https://scorecard.dev/viewer/?uri=github.com/sweetrb/apple-notes-mcp)
|
|
9
10
|
[](https://www.apple.com/macos/)
|
|
10
11
|
[](https://opensource.org/licenses/MIT)
|
|
11
12
|
[](https://modelcontextprotocol.io)
|
package/build/index.js
CHANGED
|
@@ -1303,5 +1303,16 @@ server.registerTool("get-note-metadata", {
|
|
|
1303
1303
|
*/
|
|
1304
1304
|
// Register read-only resources and workflow prompts (#23).
|
|
1305
1305
|
registerResourcesAndPrompts(server, notesManager);
|
|
1306
|
+
// Defense-in-depth: an unhandled rejection or a stray EventEmitter "error" must
|
|
1307
|
+
// never take down this long-lived MCP server. EPIPE on stdout means the MCP
|
|
1308
|
+
// client disconnected — exit cleanly rather than crash.
|
|
1309
|
+
process.on("uncaughtException", (err) => {
|
|
1310
|
+
if (err?.code === "EPIPE")
|
|
1311
|
+
process.exit(0);
|
|
1312
|
+
console.error("[uncaughtException]", err);
|
|
1313
|
+
});
|
|
1314
|
+
process.on("unhandledRejection", (reason) => {
|
|
1315
|
+
console.error("[unhandledRejection]", reason);
|
|
1316
|
+
});
|
|
1306
1317
|
const transport = new StdioServerTransport();
|
|
1307
1318
|
await server.connect(transport);
|
|
@@ -2436,11 +2436,21 @@ export class AppleNotesManager {
|
|
|
2436
2436
|
* Simple HTML to plaintext conversion for export.
|
|
2437
2437
|
*/
|
|
2438
2438
|
htmlToPlaintext(html) {
|
|
2439
|
-
|
|
2439
|
+
// Convert block/line breaks to newlines first.
|
|
2440
|
+
let text = html
|
|
2440
2441
|
.replace(/<br\s*\/?>/gi, "\n")
|
|
2441
2442
|
.replace(/<\/div>/gi, "\n")
|
|
2442
|
-
.replace(/<\/p>/gi, "\n")
|
|
2443
|
-
|
|
2443
|
+
.replace(/<\/p>/gi, "\n");
|
|
2444
|
+
// Strip any remaining tags, looping until the string stabilizes. A single
|
|
2445
|
+
// pass can leave residue when removing one tag re-forms another (e.g.
|
|
2446
|
+
// "<<i>>"), so we repeat until there are no more matches — the recognized
|
|
2447
|
+
// fix for CodeQL js/incomplete-multi-character-sanitization.
|
|
2448
|
+
let prev;
|
|
2449
|
+
do {
|
|
2450
|
+
prev = text;
|
|
2451
|
+
text = text.replace(/<[^>]*>/g, "");
|
|
2452
|
+
} while (text !== prev);
|
|
2453
|
+
return (text
|
|
2444
2454
|
.replace(/ /g, " ")
|
|
2445
2455
|
.replace(/</g, "<")
|
|
2446
2456
|
.replace(/>/g, ">")
|
|
@@ -2402,4 +2402,11 @@ describe("htmlToPlaintext (export helper)", () => {
|
|
|
2402
2402
|
expect(toPlaintext("a<br><br><br><br>b")).toBe("a\n\nb");
|
|
2403
2403
|
expect(toPlaintext(" <div>x</div> ")).toBe("x");
|
|
2404
2404
|
});
|
|
2405
|
+
it("strips nested/overlapping tags without leaving a tag (iterated strip)", () => {
|
|
2406
|
+
// A single pass can leave residue when removing one tag re-forms another;
|
|
2407
|
+
// the loop keeps stripping until no <...> remains.
|
|
2408
|
+
expect(toPlaintext("a<<i>>b")).not.toMatch(/<[^>]*>/);
|
|
2409
|
+
expect(toPlaintext("<x<y>z>")).not.toMatch(/<[^>]*>/);
|
|
2410
|
+
expect(toPlaintext("plain <b>text</b> here")).toBe("plain text here");
|
|
2411
|
+
});
|
|
2405
2412
|
});
|
package/package.json
CHANGED