joplin-mcp-server 1.0.0

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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +384 -0
  3. package/dist/bin/cli.d.ts +2 -0
  4. package/dist/bin/cli.js +7 -0
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +204 -0
  7. package/dist/lib/joplin-api-client.d.ts +23 -0
  8. package/dist/lib/joplin-api-client.js +110 -0
  9. package/dist/lib/logger.d.ts +21 -0
  10. package/dist/lib/logger.js +68 -0
  11. package/dist/lib/parse-args.d.ts +2 -0
  12. package/dist/lib/parse-args.js +81 -0
  13. package/dist/lib/tools/base-tool.d.ts +27 -0
  14. package/dist/lib/tools/base-tool.js +24 -0
  15. package/dist/lib/tools/create-folder.d.ts +9 -0
  16. package/dist/lib/tools/create-folder.js +79 -0
  17. package/dist/lib/tools/create-note.d.ts +13 -0
  18. package/dist/lib/tools/create-note.js +88 -0
  19. package/dist/lib/tools/delete-folder.d.ts +10 -0
  20. package/dist/lib/tools/delete-folder.js +138 -0
  21. package/dist/lib/tools/delete-note.d.ts +9 -0
  22. package/dist/lib/tools/delete-note.js +92 -0
  23. package/dist/lib/tools/edit-folder.d.ts +10 -0
  24. package/dist/lib/tools/edit-folder.js +136 -0
  25. package/dist/lib/tools/edit-note.d.ts +15 -0
  26. package/dist/lib/tools/edit-note.js +153 -0
  27. package/dist/lib/tools/index.d.ts +12 -0
  28. package/dist/lib/tools/index.js +12 -0
  29. package/dist/lib/tools/list-notebooks.d.ts +7 -0
  30. package/dist/lib/tools/list-notebooks.js +59 -0
  31. package/dist/lib/tools/read-multi-note.d.ts +5 -0
  32. package/dist/lib/tools/read-multi-note.js +108 -0
  33. package/dist/lib/tools/read-note.d.ts +5 -0
  34. package/dist/lib/tools/read-note.js +80 -0
  35. package/dist/lib/tools/read-notebook.d.ts +5 -0
  36. package/dist/lib/tools/read-notebook.js +66 -0
  37. package/dist/lib/tools/search-notes.d.ts +5 -0
  38. package/dist/lib/tools/search-notes.js +68 -0
  39. package/dist/tests/integration/joplin-integration.test.d.ts +1 -0
  40. package/dist/tests/integration/joplin-integration.test.js +117 -0
  41. package/dist/tests/manual/create-folder.test.d.ts +1 -0
  42. package/dist/tests/manual/create-folder.test.js +81 -0
  43. package/dist/tests/manual/create-note.test.d.ts +1 -0
  44. package/dist/tests/manual/create-note.test.js +84 -0
  45. package/dist/tests/manual/delete-folder.test.d.ts +1 -0
  46. package/dist/tests/manual/delete-folder.test.js +118 -0
  47. package/dist/tests/manual/delete-note.test.d.ts +1 -0
  48. package/dist/tests/manual/delete-note.test.js +101 -0
  49. package/dist/tests/manual/edit-folder.test.d.ts +1 -0
  50. package/dist/tests/manual/edit-folder.test.js +104 -0
  51. package/dist/tests/manual/edit-note.test.d.ts +1 -0
  52. package/dist/tests/manual/edit-note.test.js +118 -0
  53. package/dist/tests/manual/list-notebooks.test.d.ts +1 -0
  54. package/dist/tests/manual/list-notebooks.test.js +42 -0
  55. package/dist/tests/manual/read-note.test.d.ts +1 -0
  56. package/dist/tests/manual/read-note.test.js +54 -0
  57. package/dist/tests/manual/search-notes.test.d.ts +1 -0
  58. package/dist/tests/manual/search-notes.test.js +43 -0
  59. package/dist/tests/unit/create-tools.test.d.ts +1 -0
  60. package/dist/tests/unit/create-tools.test.js +223 -0
  61. package/dist/tests/unit/delete-tools.test.d.ts +1 -0
  62. package/dist/tests/unit/delete-tools.test.js +225 -0
  63. package/dist/tests/unit/edit-tools.test.d.ts +1 -0
  64. package/dist/tests/unit/edit-tools.test.js +261 -0
  65. package/dist/tests/unit/joplin-api-client.test.d.ts +1 -0
  66. package/dist/tests/unit/joplin-api-client.test.js +154 -0
  67. package/dist/vitest.config.d.ts +2 -0
  68. package/dist/vitest.config.js +22 -0
  69. package/dist/vitest.setup.d.ts +1 -0
  70. package/dist/vitest.setup.js +24 -0
  71. package/package.json +58 -0
@@ -0,0 +1,118 @@
1
+ import dotenv from "dotenv";
2
+ import JoplinAPIClient from "../../lib/joplin-api-client.js";
3
+ import { DeleteFolder, ListNotebooks, ReadNotebook } from "../../lib/tools/index.js";
4
+ // Load environment variables
5
+ dotenv.config();
6
+ // Check for required environment variables
7
+ const requiredEnvVars = ["JOPLIN_PORT", "JOPLIN_TOKEN"];
8
+ for (const envVar of requiredEnvVars) {
9
+ if (!process.env[envVar]) {
10
+ console.error(`Error: ${envVar} environment variable is required`);
11
+ process.exit(1);
12
+ }
13
+ }
14
+ // Create the Joplin API client
15
+ const apiClient = new JoplinAPIClient({
16
+ port: parseInt(process.env.JOPLIN_PORT),
17
+ token: process.env.JOPLIN_TOKEN,
18
+ });
19
+ // Create the tools
20
+ const deleteFolder = new DeleteFolder(apiClient);
21
+ const listNotebooks = new ListNotebooks(apiClient);
22
+ const readNotebook = new ReadNotebook(apiClient);
23
+ // Test the delete folder functionality
24
+ async function testDeleteFolder() {
25
+ try {
26
+ // Check if Joplin is available
27
+ const available = await apiClient.serviceAvailable();
28
+ if (!available) {
29
+ console.error("Error: Joplin service is not available");
30
+ process.exit(1);
31
+ }
32
+ // Parse command line arguments
33
+ const args = process.argv.slice(2);
34
+ const folderId = args[0];
35
+ const confirmFlag = args[1];
36
+ const forceFlag = args[2];
37
+ if (!folderId) {
38
+ console.log("No folder ID provided. Listing available notebooks:");
39
+ const notebooks = await listNotebooks.call();
40
+ console.log(notebooks);
41
+ console.log("\n⚠️ DANGER ZONE - DELETE OPERATIONS");
42
+ console.log("Please run again with a folder ID from the list above.");
43
+ console.log("Usage: tsx tests/manual/delete-folder.test.ts <folder_id> [confirm] [force]");
44
+ console.log("\nExamples:");
45
+ console.log(" tsx tests/manual/delete-folder.test.ts abc123 # Show deletion preview");
46
+ console.log(" tsx tests/manual/delete-folder.test.ts abc123 confirm # Delete if empty");
47
+ console.log(" tsx tests/manual/delete-folder.test.ts abc123 confirm force # Force delete with contents");
48
+ console.log("\n⚠️ DELETION IS PERMANENT AND CANNOT BE UNDONE!");
49
+ process.exit(0);
50
+ }
51
+ console.log("📁 Notebook to be deleted:");
52
+ const currentNotebook = await readNotebook.call(folderId);
53
+ console.log(currentNotebook);
54
+ console.log("\n" + "=".repeat(80) + "\n");
55
+ const deleteOptions = { folder_id: folderId };
56
+ // Check if user wants to actually confirm deletion
57
+ if (confirmFlag === "confirm") {
58
+ deleteOptions.confirm = true;
59
+ if (forceFlag === "force") {
60
+ deleteOptions.force = true;
61
+ console.log("🗑️ PROCEEDING WITH FORCE DELETION (INCLUDING ALL CONTENTS)...");
62
+ }
63
+ else {
64
+ console.log("🗑️ PROCEEDING WITH DELETION...");
65
+ }
66
+ }
67
+ else {
68
+ console.log("🔍 DELETION PREVIEW (not actually deleting):");
69
+ }
70
+ console.log("");
71
+ const result = await deleteFolder.call(deleteOptions);
72
+ console.log(result);
73
+ if (!confirmFlag || confirmFlag !== "confirm") {
74
+ console.log("\n💡 To actually delete this notebook:");
75
+ console.log(` Empty folder: tsx tests/manual/delete-folder.test.ts ${folderId} confirm`);
76
+ console.log(` With contents: tsx tests/manual/delete-folder.test.ts ${folderId} confirm force`);
77
+ console.log("\n⚠️ WARNING: This will permanently delete the notebook and all its contents!");
78
+ }
79
+ else {
80
+ console.log("\n📋 Updated notebook hierarchy:");
81
+ const updatedNotebooks = await listNotebooks.call();
82
+ console.log(updatedNotebooks);
83
+ }
84
+ }
85
+ catch (error) {
86
+ console.error("Error testing delete folder:", error);
87
+ }
88
+ }
89
+ // Show usage if help requested
90
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
91
+ console.log(`
92
+ ⚠️ DANGER: DELETE FOLDER TOOL
93
+
94
+ Usage: tsx tests/manual/delete-folder.test.ts <folder_id> [confirm] [force]
95
+
96
+ Arguments:
97
+ folder_id - ID of the folder/notebook to delete (required)
98
+ confirm - Actually perform the deletion (optional)
99
+ force - Force delete even if folder contains items (optional)
100
+
101
+ Examples:
102
+ tsx tests/manual/delete-folder.test.ts abc123 # Preview deletion
103
+ tsx tests/manual/delete-folder.test.ts abc123 confirm # Delete if empty
104
+ tsx tests/manual/delete-folder.test.ts abc123 confirm force # Force delete with all contents
105
+
106
+ ⚠️ WARNING:
107
+ - Deletion is permanent and cannot be undone!
108
+ - Force deletion will destroy ALL notes and subfolders inside!
109
+
110
+ First list notebooks to get their IDs:
111
+ npm run test:manual:list-notebooks
112
+ `);
113
+ process.exit(0);
114
+ }
115
+ // Run if called directly
116
+ if (import.meta.url === `file://${process.argv[1]}`) {
117
+ testDeleteFolder();
118
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,101 @@
1
+ import dotenv from "dotenv";
2
+ import JoplinAPIClient from "../../lib/joplin-api-client.js";
3
+ import { DeleteNote, SearchNotes, ReadNote } from "../../lib/tools/index.js";
4
+ // Load environment variables
5
+ dotenv.config();
6
+ // Check for required environment variables
7
+ const requiredEnvVars = ["JOPLIN_PORT", "JOPLIN_TOKEN"];
8
+ for (const envVar of requiredEnvVars) {
9
+ if (!process.env[envVar]) {
10
+ console.error(`Error: ${envVar} environment variable is required`);
11
+ process.exit(1);
12
+ }
13
+ }
14
+ // Create the Joplin API client
15
+ const apiClient = new JoplinAPIClient({
16
+ port: parseInt(process.env.JOPLIN_PORT),
17
+ token: process.env.JOPLIN_TOKEN,
18
+ });
19
+ // Create the tools
20
+ const deleteNote = new DeleteNote(apiClient);
21
+ const searchNotes = new SearchNotes(apiClient);
22
+ const readNote = new ReadNote(apiClient);
23
+ // Test the delete note functionality
24
+ async function testDeleteNote() {
25
+ try {
26
+ // Check if Joplin is available
27
+ const available = await apiClient.serviceAvailable();
28
+ if (!available) {
29
+ console.error("Error: Joplin service is not available");
30
+ process.exit(1);
31
+ }
32
+ // Parse command line arguments
33
+ const args = process.argv.slice(2);
34
+ const noteId = args[0];
35
+ const confirmFlag = args[1];
36
+ if (!noteId) {
37
+ console.log("No note ID provided. Searching for notes to delete:");
38
+ const searchQuery = args[1] || "test";
39
+ const searchResults = await searchNotes.call(searchQuery);
40
+ console.log(searchResults);
41
+ console.log("\n⚠️ DANGER ZONE - DELETE OPERATIONS");
42
+ console.log("Please run again with a note ID from the search results above.");
43
+ console.log("Usage: tsx tests/manual/delete-note.test.ts <note_id> [confirm]");
44
+ console.log("\nExamples:");
45
+ console.log(" tsx tests/manual/delete-note.test.ts abc123 # Show deletion preview");
46
+ console.log(" tsx tests/manual/delete-note.test.ts abc123 confirm # Actually delete");
47
+ console.log("\n⚠️ DELETION IS PERMANENT AND CANNOT BE UNDONE!");
48
+ process.exit(0);
49
+ }
50
+ console.log("📝 Note to be deleted:");
51
+ const currentNote = await readNote.call(noteId);
52
+ console.log(currentNote);
53
+ console.log("\n" + "=".repeat(80) + "\n");
54
+ const deleteOptions = { note_id: noteId };
55
+ // Check if user wants to actually confirm deletion
56
+ if (confirmFlag === "confirm") {
57
+ deleteOptions.confirm = true;
58
+ console.log("🗑️ PROCEEDING WITH DELETION...");
59
+ }
60
+ else {
61
+ console.log("🔍 DELETION PREVIEW (not actually deleting):");
62
+ }
63
+ console.log("");
64
+ const result = await deleteNote.call(deleteOptions);
65
+ console.log(result);
66
+ if (!confirmFlag || confirmFlag !== "confirm") {
67
+ console.log("\n💡 To actually delete this note, run:");
68
+ console.log(`tsx tests/manual/delete-note.test.ts ${noteId} confirm`);
69
+ console.log("\n⚠️ WARNING: This will permanently delete the note!");
70
+ }
71
+ }
72
+ catch (error) {
73
+ console.error("Error testing delete note:", error);
74
+ }
75
+ }
76
+ // Show usage if help requested
77
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
78
+ console.log(`
79
+ ⚠️ DANGER: DELETE NOTE TOOL
80
+
81
+ Usage: tsx tests/manual/delete-note.test.ts <note_id> [confirm]
82
+
83
+ Arguments:
84
+ note_id - ID of the note to delete (required)
85
+ confirm - Actually perform the deletion (optional)
86
+
87
+ Examples:
88
+ tsx tests/manual/delete-note.test.ts abc123 # Preview deletion
89
+ tsx tests/manual/delete-note.test.ts abc123 confirm # Actually delete
90
+
91
+ ⚠️ WARNING: Deletion is permanent and cannot be undone!
92
+
93
+ First search for notes to get their IDs:
94
+ tsx tests/manual/search-notes.test.ts "search term"
95
+ `);
96
+ process.exit(0);
97
+ }
98
+ // Run if called directly
99
+ if (import.meta.url === `file://${process.argv[1]}`) {
100
+ testDeleteNote();
101
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,104 @@
1
+ import dotenv from "dotenv";
2
+ import JoplinAPIClient from "../../lib/joplin-api-client.js";
3
+ import { EditFolder, ListNotebooks, ReadNotebook } from "../../lib/tools/index.js";
4
+ // Load environment variables
5
+ dotenv.config();
6
+ // Check for required environment variables
7
+ const requiredEnvVars = ["JOPLIN_PORT", "JOPLIN_TOKEN"];
8
+ for (const envVar of requiredEnvVars) {
9
+ if (!process.env[envVar]) {
10
+ console.error(`Error: ${envVar} environment variable is required`);
11
+ process.exit(1);
12
+ }
13
+ }
14
+ // Create the Joplin API client
15
+ const apiClient = new JoplinAPIClient({
16
+ port: parseInt(process.env.JOPLIN_PORT),
17
+ token: process.env.JOPLIN_TOKEN,
18
+ });
19
+ // Create the tools
20
+ const editFolder = new EditFolder(apiClient);
21
+ const listNotebooks = new ListNotebooks(apiClient);
22
+ const readNotebook = new ReadNotebook(apiClient);
23
+ // Test the edit folder functionality
24
+ async function testEditFolder() {
25
+ try {
26
+ // Check if Joplin is available
27
+ const available = await apiClient.serviceAvailable();
28
+ if (!available) {
29
+ console.error("Error: Joplin service is not available");
30
+ process.exit(1);
31
+ }
32
+ // Parse command line arguments
33
+ const args = process.argv.slice(2);
34
+ const folderId = args[0];
35
+ if (!folderId) {
36
+ console.log("No folder ID provided. Listing available notebooks:");
37
+ const notebooks = await listNotebooks.call();
38
+ console.log(notebooks);
39
+ console.log("\nPlease run again with a folder ID from the list above.");
40
+ console.log("Usage: tsx tests/manual/edit-folder.test.ts <folder_id> [field=value] [field=value]...");
41
+ console.log("\nExamples:");
42
+ console.log(' tsx tests/manual/edit-folder.test.ts abc123 title="New Name"');
43
+ console.log(' tsx tests/manual/edit-folder.test.ts abc123 title="Subfolder" parent_id="def456"');
44
+ process.exit(0);
45
+ }
46
+ // Parse edit parameters from remaining arguments
47
+ const editOptions = { folder_id: folderId };
48
+ for (let i = 1; i < args.length; i++) {
49
+ const arg = args[i];
50
+ const [key, value] = arg.split("=", 2);
51
+ if (!value) {
52
+ console.error(`Invalid argument: ${arg}. Use format key=value`);
53
+ continue;
54
+ }
55
+ // Remove quotes if present
56
+ editOptions[key] = value.replace(/^["']|["']$/g, "");
57
+ }
58
+ console.log("📁 Current notebook:");
59
+ const currentNotebook = await readNotebook.call(folderId);
60
+ console.log(currentNotebook);
61
+ console.log("\n" + "=".repeat(80) + "\n");
62
+ console.log("🔄 Editing notebook with:");
63
+ Object.entries(editOptions).forEach(([key, value]) => {
64
+ if (key !== "folder_id") {
65
+ console.log(` ${key}: ${JSON.stringify(value)}`);
66
+ }
67
+ });
68
+ console.log("");
69
+ const result = await editFolder.call(editOptions);
70
+ console.log(result);
71
+ console.log("\n📋 Updated notebook hierarchy:");
72
+ const updatedNotebooks = await listNotebooks.call();
73
+ console.log(updatedNotebooks);
74
+ }
75
+ catch (error) {
76
+ console.error("Error testing edit folder:", error);
77
+ }
78
+ }
79
+ // Show usage if help requested
80
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
81
+ console.log(`
82
+ Usage: tsx tests/manual/edit-folder.test.ts <folder_id> [field=value] [field=value]...
83
+
84
+ Arguments:
85
+ folder_id - ID of the folder/notebook to edit (required)
86
+
87
+ Available fields:
88
+ title="New Name" - Update folder title
89
+ parent_id="parent_id" - Move to different parent folder
90
+
91
+ Examples:
92
+ tsx tests/manual/edit-folder.test.ts abc123 title="New Name"
93
+ tsx tests/manual/edit-folder.test.ts abc123 title="Subfolder" parent_id="def456"
94
+ tsx tests/manual/edit-folder.test.ts abc123 parent_id="" # Move to top level
95
+
96
+ First list notebooks to get their IDs:
97
+ npm run test:manual:list-notebooks
98
+ `);
99
+ process.exit(0);
100
+ }
101
+ // Run if called directly
102
+ if (import.meta.url === `file://${process.argv[1]}`) {
103
+ testEditFolder();
104
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,118 @@
1
+ import dotenv from "dotenv";
2
+ import JoplinAPIClient from "../../lib/joplin-api-client.js";
3
+ import { EditNote, SearchNotes, ReadNote } from "../../lib/tools/index.js";
4
+ // Load environment variables
5
+ dotenv.config();
6
+ // Check for required environment variables
7
+ const requiredEnvVars = ["JOPLIN_PORT", "JOPLIN_TOKEN"];
8
+ for (const envVar of requiredEnvVars) {
9
+ if (!process.env[envVar]) {
10
+ console.error(`Error: ${envVar} environment variable is required`);
11
+ process.exit(1);
12
+ }
13
+ }
14
+ // Create the Joplin API client
15
+ const apiClient = new JoplinAPIClient({
16
+ port: parseInt(process.env.JOPLIN_PORT),
17
+ token: process.env.JOPLIN_TOKEN,
18
+ });
19
+ // Create the tools
20
+ const editNote = new EditNote(apiClient);
21
+ const searchNotes = new SearchNotes(apiClient);
22
+ const readNote = new ReadNote(apiClient);
23
+ // Test the edit note functionality
24
+ async function testEditNote() {
25
+ try {
26
+ // Check if Joplin is available
27
+ const available = await apiClient.serviceAvailable();
28
+ if (!available) {
29
+ console.error("Error: Joplin service is not available");
30
+ process.exit(1);
31
+ }
32
+ // Parse command line arguments
33
+ const args = process.argv.slice(2);
34
+ const noteId = args[0];
35
+ if (!noteId) {
36
+ console.log("No note ID provided. Searching for notes to edit:");
37
+ const searchQuery = args[1] || "test";
38
+ const searchResults = await searchNotes.call(searchQuery);
39
+ console.log(searchResults);
40
+ console.log("\nPlease run again with a note ID from the search results above.");
41
+ console.log("Usage: tsx tests/manual/edit-note.test.ts <note_id> [field=value] [field=value]...");
42
+ console.log("\nExamples:");
43
+ console.log(' tsx tests/manual/edit-note.test.ts abc123 title="Updated Title"');
44
+ console.log(' tsx tests/manual/edit-note.test.ts abc123 body="New content" is_todo=true');
45
+ console.log(" tsx tests/manual/edit-note.test.ts abc123 todo_completed=true");
46
+ process.exit(0);
47
+ }
48
+ // Parse edit parameters from remaining arguments
49
+ const editOptions = { note_id: noteId };
50
+ for (let i = 1; i < args.length; i++) {
51
+ const arg = args[i];
52
+ const [key, value] = arg.split("=", 2);
53
+ if (!value) {
54
+ console.error(`Invalid argument: ${arg}. Use format key=value`);
55
+ continue;
56
+ }
57
+ // Parse different types of values
58
+ if (key === "is_todo" || key === "todo_completed") {
59
+ editOptions[key] = value.toLowerCase() === "true";
60
+ }
61
+ else if (key === "todo_due") {
62
+ editOptions[key] = parseInt(value);
63
+ }
64
+ else {
65
+ // Remove quotes if present
66
+ editOptions[key] = value.replace(/^["']|["']$/g, "");
67
+ }
68
+ }
69
+ console.log("📝 Current note:");
70
+ const currentNote = await readNote.call(noteId);
71
+ console.log(currentNote);
72
+ console.log("\n" + "=".repeat(80) + "\n");
73
+ console.log("🔄 Editing note with:");
74
+ Object.entries(editOptions).forEach(([key, value]) => {
75
+ if (key !== "note_id") {
76
+ console.log(` ${key}: ${JSON.stringify(value)}`);
77
+ }
78
+ });
79
+ console.log("");
80
+ const result = await editNote.call(editOptions);
81
+ console.log(result);
82
+ }
83
+ catch (error) {
84
+ console.error("Error testing edit note:", error);
85
+ }
86
+ }
87
+ // Show usage if help requested
88
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
89
+ console.log(`
90
+ Usage: tsx tests/manual/edit-note.test.ts <note_id> [field=value] [field=value]...
91
+
92
+ Arguments:
93
+ note_id - ID of the note to edit (required)
94
+
95
+ Available fields:
96
+ title="New Title" - Update note title
97
+ body="New content" - Update note body (Markdown)
98
+ body_html="<p>HTML</p>" - Update note body (HTML)
99
+ parent_id="notebook_id" - Move to different notebook
100
+ is_todo=true/false - Convert to/from todo
101
+ todo_completed=true/false - Mark todo as completed/incomplete
102
+ todo_due=1234567890000 - Set due date (timestamp)
103
+
104
+ Examples:
105
+ tsx tests/manual/edit-note.test.ts abc123 title="Updated Title"
106
+ tsx tests/manual/edit-note.test.ts abc123 body="New content" is_todo=true
107
+ tsx tests/manual/edit-note.test.ts abc123 todo_completed=true
108
+ tsx tests/manual/edit-note.test.ts abc123 parent_id="def456"
109
+
110
+ First search for notes to get their IDs:
111
+ tsx tests/manual/search-notes.test.ts "search term"
112
+ `);
113
+ process.exit(0);
114
+ }
115
+ // Run if called directly
116
+ if (import.meta.url === `file://${process.argv[1]}`) {
117
+ testEditNote();
118
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,42 @@
1
+ import dotenv from "dotenv";
2
+ import JoplinAPIClient from "../../lib/joplin-api-client.js";
3
+ import { ListNotebooks } from "../../lib/tools/index.js";
4
+ // Load environment variables
5
+ dotenv.config();
6
+ // Check for required environment variables
7
+ const requiredEnvVars = ["JOPLIN_PORT", "JOPLIN_TOKEN"];
8
+ for (const envVar of requiredEnvVars) {
9
+ if (!process.env[envVar]) {
10
+ console.error(`Error: ${envVar} environment variable is required`);
11
+ process.exit(1);
12
+ }
13
+ }
14
+ // Create the Joplin API client
15
+ const apiClient = new JoplinAPIClient({
16
+ port: parseInt(process.env.JOPLIN_PORT),
17
+ token: process.env.JOPLIN_TOKEN,
18
+ });
19
+ // Create the tool
20
+ const listNotebooks = new ListNotebooks(apiClient);
21
+ // Test the list notebooks functionality
22
+ async function testListNotebooks() {
23
+ try {
24
+ // Check if Joplin is available
25
+ const available = await apiClient.serviceAvailable();
26
+ if (!available) {
27
+ console.error("Error: Joplin service is not available");
28
+ process.exit(1);
29
+ }
30
+ console.log("📋 Listing all notebooks:");
31
+ console.log("");
32
+ const result = await listNotebooks.call();
33
+ console.log(result);
34
+ }
35
+ catch (error) {
36
+ console.error("Error testing list notebooks:", error);
37
+ }
38
+ }
39
+ // Run if called directly
40
+ if (import.meta.url === `file://${process.argv[1]}`) {
41
+ testListNotebooks();
42
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,54 @@
1
+ import dotenv from "dotenv";
2
+ import JoplinAPIClient from "../../lib/joplin-api-client.js";
3
+ import { SearchNotes, ReadNote } from "../../lib/tools/index.js";
4
+ // Load environment variables
5
+ dotenv.config();
6
+ // Check for required environment variables
7
+ const requiredEnvVars = ["JOPLIN_PORT", "JOPLIN_TOKEN"];
8
+ for (const envVar of requiredEnvVars) {
9
+ if (!process.env[envVar]) {
10
+ console.error(`Error: ${envVar} environment variable is required`);
11
+ process.exit(1);
12
+ }
13
+ }
14
+ // Create the Joplin API client
15
+ const apiClient = new JoplinAPIClient({
16
+ port: parseInt(process.env.JOPLIN_PORT),
17
+ token: process.env.JOPLIN_TOKEN,
18
+ });
19
+ // Create the tools
20
+ const searchNotes = new SearchNotes(apiClient);
21
+ const readNote = new ReadNote(apiClient);
22
+ // Test the read note functionality
23
+ async function testReadNote() {
24
+ try {
25
+ // Check if Joplin is available
26
+ const available = await apiClient.serviceAvailable();
27
+ if (!available) {
28
+ console.error("Error: Joplin service is not available");
29
+ process.exit(1);
30
+ }
31
+ // If no note ID is provided, search for notes first
32
+ const noteId = process.argv[2];
33
+ if (!noteId) {
34
+ console.log("No note ID provided. Searching for a sample note:");
35
+ const searchQuery = process.argv[3] || "todo"; // Default search term if none provided
36
+ const searchResults = await searchNotes.call(searchQuery);
37
+ console.log(searchResults);
38
+ console.log("\nPlease run again with a note ID from the search results above.");
39
+ console.log("Example: tsx tests/manual/read-note.test.ts your-note-id");
40
+ process.exit(0);
41
+ }
42
+ // Read the specified note
43
+ console.log(`Reading note with ID: "${noteId}"`);
44
+ const result = await readNote.call(noteId);
45
+ console.log(result);
46
+ }
47
+ catch (error) {
48
+ console.error("Error testing read note:", error);
49
+ }
50
+ }
51
+ // Run if called directly
52
+ if (import.meta.url === `file://${process.argv[1]}`) {
53
+ testReadNote();
54
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,43 @@
1
+ import dotenv from "dotenv";
2
+ import JoplinAPIClient from "../../lib/joplin-api-client.js";
3
+ import { SearchNotes } from "../../lib/tools/index.js";
4
+ // Load environment variables
5
+ dotenv.config();
6
+ // Check for required environment variables
7
+ const requiredEnvVars = ["JOPLIN_PORT", "JOPLIN_TOKEN"];
8
+ for (const envVar of requiredEnvVars) {
9
+ if (!process.env[envVar]) {
10
+ console.error(`Error: ${envVar} environment variable is required`);
11
+ process.exit(1);
12
+ }
13
+ }
14
+ // Create the Joplin API client
15
+ const apiClient = new JoplinAPIClient({
16
+ port: parseInt(process.env.JOPLIN_PORT),
17
+ token: process.env.JOPLIN_TOKEN,
18
+ });
19
+ // Create the search tool
20
+ const searchNotes = new SearchNotes(apiClient);
21
+ // Test the search functionality
22
+ async function testSearch() {
23
+ try {
24
+ // Check if Joplin is available
25
+ const available = await apiClient.serviceAvailable();
26
+ if (!available) {
27
+ console.error("Error: Joplin service is not available");
28
+ process.exit(1);
29
+ }
30
+ // Perform a search
31
+ const query = process.argv[2] || "test";
32
+ console.log(`Searching for: "${query}"`);
33
+ const result = await searchNotes.call(query);
34
+ console.log(result);
35
+ }
36
+ catch (error) {
37
+ console.error("Error testing search:", error);
38
+ }
39
+ }
40
+ // Run if called directly
41
+ if (import.meta.url === `file://${process.argv[1]}`) {
42
+ testSearch();
43
+ }
@@ -0,0 +1 @@
1
+ export {};