@fietiger/joplin-mcp 1.0.0 → 1.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.
package/dist/index.js CHANGED
@@ -4,8 +4,14 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4
4
  import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
5
5
  import { z } from "zod";
6
6
  import axios from "axios";
7
+ import fs from "node:fs";
8
+ import path from "node:path";
7
9
  const JOPLIN_BASE_URL = "http://localhost:41184";
8
10
  let JOPLIN_TOKEN = process.env.JOPLIN_TOKEN || "";
11
+ /**
12
+ * 笔记本缓存
13
+ */
14
+ let notebooksCache = null;
9
15
  /**
10
16
  * 通用 Joplin API 调用函数
11
17
  */
@@ -43,10 +49,10 @@ async function callJoplin(method, path, queryParams = {}, jsonData = null) {
43
49
  // 创建 McpServer 实例 (新 API)
44
50
  const server = new McpServer({
45
51
  name: "@fietiger/joplin-mcp",
46
- version: "1.0.0",
52
+ version: "1.0.1",
47
53
  });
48
54
  // 注册 get_version 工具
49
- server.tool("get_version", {}, async () => {
55
+ server.registerTool("get_version", { description: "获取 MCP 服务和 Joplin API 的版本信息" }, async () => {
50
56
  let apiStatus = "Unknown";
51
57
  let joplinResponse = "Unknown";
52
58
  try {
@@ -67,7 +73,7 @@ server.tool("get_version", {}, async () => {
67
73
  {
68
74
  type: "text",
69
75
  text: JSON.stringify({
70
- mcp_version: "1.0.0",
76
+ mcp_version: "1.0.1",
71
77
  joplin_api_url: JOPLIN_BASE_URL,
72
78
  joplin_api_status: apiStatus,
73
79
  joplin_response: joplinResponse,
@@ -77,23 +83,38 @@ server.tool("get_version", {}, async () => {
77
83
  };
78
84
  });
79
85
  // 注册 list_notebooks 工具
80
- server.tool("list_notebooks", {}, async () => {
86
+ server.registerTool("list_notebooks", { description: "列出所有笔记本" }, async () => {
87
+ if (notebooksCache) {
88
+ console.error("使用本地缓存的笔记本列表");
89
+ return {
90
+ content: [
91
+ {
92
+ type: "text",
93
+ text: JSON.stringify(notebooksCache, null, 2),
94
+ },
95
+ ],
96
+ };
97
+ }
81
98
  const results = await callJoplin("GET", "folders");
99
+ notebooksCache = results.items || [];
82
100
  return {
83
101
  content: [
84
102
  {
85
103
  type: "text",
86
- text: JSON.stringify(results.items || [], null, 2),
104
+ text: JSON.stringify(notebooksCache, null, 2),
87
105
  },
88
106
  ],
89
107
  };
90
108
  });
91
109
  // 注册 create_note 工具
92
- server.tool("create_note", {
93
- title: z.string().describe("笔记标题"),
94
- body: z.string().describe("笔记内容 (Markdown 格式)"),
95
- parent_id: z.string().optional().describe("笔记本 ID (可选)"),
96
- tags: z.array(z.string()).optional().describe("标签列表 (可选)"),
110
+ server.registerTool("create_note", {
111
+ description: "在指定笔记本中创建新笔记",
112
+ inputSchema: {
113
+ title: z.string().describe("笔记标题"),
114
+ body: z.string().describe("笔记内容 (Markdown 格式)"),
115
+ parent_id: z.string().optional().describe("笔记本 ID (可选)"),
116
+ tags: z.array(z.string()).optional().describe("标签列表 (可选)"),
117
+ }
97
118
  }, async ({ title, body, parent_id, tags }) => {
98
119
  const data = { title, body };
99
120
  if (parent_id) {
@@ -126,8 +147,11 @@ server.tool("create_note", {
126
147
  };
127
148
  });
128
149
  // 注册 search_notes 工具
129
- server.tool("search_notes", {
130
- query: z.string().describe("搜索关键词"),
150
+ server.registerTool("search_notes", {
151
+ description: "搜索笔记",
152
+ inputSchema: {
153
+ query: z.string().describe("搜索关键词"),
154
+ }
131
155
  }, async ({ query }) => {
132
156
  const results = await callJoplin("GET", "search", { query });
133
157
  return {
@@ -140,8 +164,11 @@ server.tool("search_notes", {
140
164
  };
141
165
  });
142
166
  // 注册 get_note 工具
143
- server.tool("get_note", {
144
- note_id: z.string().describe("笔记 ID"),
167
+ server.registerTool("get_note", {
168
+ description: "获取指定 ID 的笔记详情",
169
+ inputSchema: {
170
+ note_id: z.string().describe("笔记 ID"),
171
+ }
145
172
  }, async ({ note_id }) => {
146
173
  const result = await callJoplin("GET", `notes/${note_id}`, {
147
174
  fields: "id,title,body,parent_id,created_time,updated_time",
@@ -155,6 +182,131 @@ server.tool("get_note", {
155
182
  ],
156
183
  };
157
184
  });
185
+ // 注册 update_note 工具
186
+ server.registerTool("update_note", {
187
+ description: "修改已有的笔记",
188
+ inputSchema: {
189
+ note_id: z.string().describe("要修改的笔记 ID"),
190
+ title: z.string().optional().describe("新标题"),
191
+ body: z.string().optional().describe("新内容 (Markdown 格式)"),
192
+ parent_id: z.string().optional().describe("新笔记本 ID"),
193
+ tags: z.array(z.string()).optional().describe("新标签列表 (将覆盖旧标签)"),
194
+ }
195
+ }, async ({ note_id, title, body, parent_id, tags }) => {
196
+ const data = {};
197
+ if (title !== undefined)
198
+ data.title = title;
199
+ if (body !== undefined)
200
+ data.body = body;
201
+ if (parent_id !== undefined)
202
+ data.parent_id = parent_id;
203
+ const note = await callJoplin("PUT", `notes/${note_id}`, {}, data);
204
+ if (tags !== undefined && Array.isArray(tags)) {
205
+ // 在 Joplin API 中,设置标签通常是覆盖式的,但需要通过特定接口
206
+ const tagTitles = tags.join(",");
207
+ await callJoplin("PUT", `notes/${note_id}`, {}, { tags: tagTitles });
208
+ }
209
+ return {
210
+ content: [
211
+ {
212
+ type: "text",
213
+ text: JSON.stringify(note, null, 2),
214
+ },
215
+ ],
216
+ };
217
+ });
218
+ // 注册 delete_note 工具
219
+ server.registerTool("delete_note", {
220
+ description: "删除指定 ID 的笔记",
221
+ inputSchema: {
222
+ note_id: z.string().describe("要删除的笔记 ID"),
223
+ }
224
+ }, async ({ note_id }) => {
225
+ await callJoplin("DELETE", `notes/${note_id}`);
226
+ return {
227
+ content: [
228
+ {
229
+ type: "text",
230
+ text: `笔记 ${note_id} 已成功删除`,
231
+ },
232
+ ],
233
+ };
234
+ });
235
+ // 注册 list_tags 工具
236
+ server.registerTool("list_tags", { description: "列出所有标签" }, async () => {
237
+ const results = await callJoplin("GET", "tags");
238
+ return {
239
+ content: [
240
+ {
241
+ type: "text",
242
+ text: JSON.stringify(results.items || [], null, 2),
243
+ },
244
+ ],
245
+ };
246
+ });
247
+ // 注册 create_notebook 工具
248
+ server.registerTool("create_notebook", {
249
+ description: "创建新笔记本",
250
+ inputSchema: {
251
+ title: z.string().describe("笔记本名称"),
252
+ parent_id: z.string().optional().describe("父笔记本 ID"),
253
+ }
254
+ }, async ({ title, parent_id }) => {
255
+ const data = { title };
256
+ if (parent_id)
257
+ data.parent_id = parent_id;
258
+ const folder = await callJoplin("POST", "folders", {}, data);
259
+ notebooksCache = null; // 清除缓存
260
+ return {
261
+ content: [
262
+ {
263
+ type: "text",
264
+ text: JSON.stringify(folder, null, 2),
265
+ },
266
+ ],
267
+ };
268
+ });
269
+ // 注册 attach_file 工具
270
+ server.registerTool("attach_file", {
271
+ description: "上传文件并关联到笔记",
272
+ inputSchema: {
273
+ note_id: z.string().describe("笔记 ID"),
274
+ file_path: z.string().describe("本地文件路径"),
275
+ title: z.string().optional().describe("附件标题 (可选)"),
276
+ }
277
+ }, async ({ note_id, file_path, title }) => {
278
+ if (!fs.existsSync(file_path)) {
279
+ throw new McpError(ErrorCode.InvalidParams, `文件不存在: ${file_path}`);
280
+ }
281
+ const fileName = title || path.basename(file_path);
282
+ const fileStats = fs.statSync(file_path);
283
+ // 使用 FormData 发送文件
284
+ const formData = new FormData();
285
+ const fileBuffer = fs.readFileSync(file_path);
286
+ const blob = new Blob([fileBuffer]);
287
+ formData.append("data", blob, fileName);
288
+ // 如果有其他属性,可以作为 props 传入 (Joplin API 要求)
289
+ const props = JSON.stringify({ title: fileName, parent_id: note_id });
290
+ formData.append("props", props);
291
+ // 调用 resources 接口上传
292
+ const resource = await callJoplin("POST", "resources", {}, formData);
293
+ // 将资源链接添加到笔记末尾
294
+ const note = await callJoplin("GET", `notes/${note_id}`, { fields: "body" });
295
+ const updatedBody = `${note.body}\n\n![${fileName}](:/${resource.id})`;
296
+ await callJoplin("PUT", `notes/${note_id}`, {}, { body: updatedBody });
297
+ return {
298
+ content: [
299
+ {
300
+ type: "text",
301
+ text: JSON.stringify({
302
+ message: "文件已成功上传并关联到笔记",
303
+ resource_id: resource.id,
304
+ file_name: fileName
305
+ }, null, 2),
306
+ },
307
+ ],
308
+ };
309
+ });
158
310
  async function main() {
159
311
  const transport = new StdioServerTransport();
160
312
  await server.connect(transport);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fietiger/joplin-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -4,10 +4,17 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4
4
  import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
5
5
  import { z } from "zod";
6
6
  import axios from "axios";
7
+ import fs from "node:fs";
8
+ import path from "node:path";
7
9
 
8
10
  const JOPLIN_BASE_URL = "http://localhost:41184";
9
11
  let JOPLIN_TOKEN = process.env.JOPLIN_TOKEN || "";
10
12
 
13
+ /**
14
+ * 笔记本缓存
15
+ */
16
+ let notebooksCache: any[] | null = null;
17
+
11
18
  /**
12
19
  * 通用 Joplin API 调用函数
13
20
  */
@@ -52,11 +59,11 @@ async function callJoplin(method: string, path: string, queryParams: any = {}, j
52
59
  // 创建 McpServer 实例 (新 API)
53
60
  const server = new McpServer({
54
61
  name: "@fietiger/joplin-mcp",
55
- version: "1.0.0",
62
+ version: "1.0.1",
56
63
  });
57
64
 
58
65
  // 注册 get_version 工具
59
- server.tool("get_version", {}, async () => {
66
+ server.registerTool("get_version", { description: "获取 MCP 服务和 Joplin API 的版本信息" }, async () => {
60
67
  let apiStatus = "Unknown";
61
68
  let joplinResponse = "Unknown";
62
69
  try {
@@ -76,7 +83,7 @@ server.tool("get_version", {}, async () => {
76
83
  {
77
84
  type: "text",
78
85
  text: JSON.stringify({
79
- mcp_version: "1.0.0",
86
+ mcp_version: "1.0.1",
80
87
  joplin_api_url: JOPLIN_BASE_URL,
81
88
  joplin_api_status: apiStatus,
82
89
  joplin_response: joplinResponse,
@@ -87,26 +94,42 @@ server.tool("get_version", {}, async () => {
87
94
  });
88
95
 
89
96
  // 注册 list_notebooks 工具
90
- server.tool("list_notebooks", {}, async () => {
97
+ server.registerTool("list_notebooks", { description: "列出所有笔记本" }, async () => {
98
+ if (notebooksCache) {
99
+ console.error("使用本地缓存的笔记本列表");
100
+ return {
101
+ content: [
102
+ {
103
+ type: "text",
104
+ text: JSON.stringify(notebooksCache, null, 2),
105
+ },
106
+ ],
107
+ };
108
+ }
109
+
91
110
  const results = await callJoplin("GET", "folders");
111
+ notebooksCache = results.items || [];
92
112
  return {
93
113
  content: [
94
114
  {
95
115
  type: "text",
96
- text: JSON.stringify(results.items || [], null, 2),
116
+ text: JSON.stringify(notebooksCache, null, 2),
97
117
  },
98
118
  ],
99
119
  };
100
120
  });
101
121
 
102
122
  // 注册 create_note 工具
103
- server.tool(
123
+ server.registerTool(
104
124
  "create_note",
105
125
  {
106
- title: z.string().describe("笔记标题"),
107
- body: z.string().describe("笔记内容 (Markdown 格式)"),
108
- parent_id: z.string().optional().describe("笔记本 ID (可选)"),
109
- tags: z.array(z.string()).optional().describe("标签列表 (可选)"),
126
+ description: "在指定笔记本中创建新笔记",
127
+ inputSchema: {
128
+ title: z.string().describe("笔记标题"),
129
+ body: z.string().describe("笔记内容 (Markdown 格式)"),
130
+ parent_id: z.string().optional().describe("笔记本 ID (可选)"),
131
+ tags: z.array(z.string()).optional().describe("标签列表 (可选)"),
132
+ }
110
133
  },
111
134
  async ({ title, body, parent_id, tags }) => {
112
135
  const data: any = { title, body };
@@ -145,10 +168,13 @@ server.tool(
145
168
  );
146
169
 
147
170
  // 注册 search_notes 工具
148
- server.tool(
171
+ server.registerTool(
149
172
  "search_notes",
150
173
  {
151
- query: z.string().describe("搜索关键词"),
174
+ description: "搜索笔记",
175
+ inputSchema: {
176
+ query: z.string().describe("搜索关键词"),
177
+ }
152
178
  },
153
179
  async ({ query }) => {
154
180
  const results = await callJoplin("GET", "search", { query });
@@ -164,10 +190,13 @@ server.tool(
164
190
  );
165
191
 
166
192
  // 注册 get_note 工具
167
- server.tool(
193
+ server.registerTool(
168
194
  "get_note",
169
195
  {
170
- note_id: z.string().describe("笔记 ID"),
196
+ description: "获取指定 ID 的笔记详情",
197
+ inputSchema: {
198
+ note_id: z.string().describe("笔记 ID"),
199
+ }
171
200
  },
172
201
  async ({ note_id }) => {
173
202
  const result = await callJoplin("GET", `notes/${note_id}`, {
@@ -184,6 +213,157 @@ server.tool(
184
213
  }
185
214
  );
186
215
 
216
+ // 注册 update_note 工具
217
+ server.registerTool(
218
+ "update_note",
219
+ {
220
+ description: "修改已有的笔记",
221
+ inputSchema: {
222
+ note_id: z.string().describe("要修改的笔记 ID"),
223
+ title: z.string().optional().describe("新标题"),
224
+ body: z.string().optional().describe("新内容 (Markdown 格式)"),
225
+ parent_id: z.string().optional().describe("新笔记本 ID"),
226
+ tags: z.array(z.string()).optional().describe("新标签列表 (将覆盖旧标签)"),
227
+ }
228
+ },
229
+ async ({ note_id, title, body, parent_id, tags }) => {
230
+ const data: any = {};
231
+ if (title !== undefined) data.title = title;
232
+ if (body !== undefined) data.body = body;
233
+ if (parent_id !== undefined) data.parent_id = parent_id;
234
+
235
+ const note = await callJoplin("PUT", `notes/${note_id}`, {}, data);
236
+
237
+ if (tags !== undefined && Array.isArray(tags)) {
238
+ // 在 Joplin API 中,设置标签通常是覆盖式的,但需要通过特定接口
239
+ const tagTitles = tags.join(",");
240
+ await callJoplin("PUT", `notes/${note_id}`, {}, { tags: tagTitles });
241
+ }
242
+
243
+ return {
244
+ content: [
245
+ {
246
+ type: "text",
247
+ text: JSON.stringify(note, null, 2),
248
+ },
249
+ ],
250
+ };
251
+ }
252
+ );
253
+
254
+ // 注册 delete_note 工具
255
+ server.registerTool(
256
+ "delete_note",
257
+ {
258
+ description: "删除指定 ID 的笔记",
259
+ inputSchema: {
260
+ note_id: z.string().describe("要删除的笔记 ID"),
261
+ }
262
+ },
263
+ async ({ note_id }) => {
264
+ await callJoplin("DELETE", `notes/${note_id}`);
265
+ return {
266
+ content: [
267
+ {
268
+ type: "text",
269
+ text: `笔记 ${note_id} 已成功删除`,
270
+ },
271
+ ],
272
+ };
273
+ }
274
+ );
275
+
276
+ // 注册 list_tags 工具
277
+ server.registerTool("list_tags", { description: "列出所有标签" }, async () => {
278
+ const results = await callJoplin("GET", "tags");
279
+ return {
280
+ content: [
281
+ {
282
+ type: "text",
283
+ text: JSON.stringify(results.items || [], null, 2),
284
+ },
285
+ ],
286
+ };
287
+ });
288
+
289
+ // 注册 create_notebook 工具
290
+ server.registerTool(
291
+ "create_notebook",
292
+ {
293
+ description: "创建新笔记本",
294
+ inputSchema: {
295
+ title: z.string().describe("笔记本名称"),
296
+ parent_id: z.string().optional().describe("父笔记本 ID"),
297
+ }
298
+ },
299
+ async ({ title, parent_id }) => {
300
+ const data: any = { title };
301
+ if (parent_id) data.parent_id = parent_id;
302
+ const folder = await callJoplin("POST", "folders", {}, data);
303
+ notebooksCache = null; // 清除缓存
304
+ return {
305
+ content: [
306
+ {
307
+ type: "text",
308
+ text: JSON.stringify(folder, null, 2),
309
+ },
310
+ ],
311
+ };
312
+ }
313
+ );
314
+
315
+ // 注册 attach_file 工具
316
+ server.registerTool(
317
+ "attach_file",
318
+ {
319
+ description: "上传文件并关联到笔记",
320
+ inputSchema: {
321
+ note_id: z.string().describe("笔记 ID"),
322
+ file_path: z.string().describe("本地文件路径"),
323
+ title: z.string().optional().describe("附件标题 (可选)"),
324
+ }
325
+ },
326
+ async ({ note_id, file_path, title }) => {
327
+ if (!fs.existsSync(file_path)) {
328
+ throw new McpError(ErrorCode.InvalidParams, `文件不存在: ${file_path}`);
329
+ }
330
+
331
+ const fileName = title || path.basename(file_path);
332
+ const fileStats = fs.statSync(file_path);
333
+
334
+ // 使用 FormData 发送文件
335
+ const formData = new FormData();
336
+ const fileBuffer = fs.readFileSync(file_path);
337
+ const blob = new Blob([fileBuffer]);
338
+ formData.append("data", blob, fileName);
339
+
340
+ // 如果有其他属性,可以作为 props 传入 (Joplin API 要求)
341
+ const props = JSON.stringify({ title: fileName, parent_id: note_id });
342
+ formData.append("props", props);
343
+
344
+ // 调用 resources 接口上传
345
+ const resource = await callJoplin("POST", "resources", {}, formData);
346
+
347
+ // 将资源链接添加到笔记末尾
348
+ const note = await callJoplin("GET", `notes/${note_id}`, { fields: "body" });
349
+ const updatedBody = `${note.body}\n\n![${fileName}](:/${resource.id})`;
350
+ await callJoplin("PUT", `notes/${note_id}`, {}, { body: updatedBody });
351
+
352
+ return {
353
+ content: [
354
+ {
355
+ type: "text",
356
+ text: JSON.stringify({
357
+ message: "文件已成功上传并关联到笔记",
358
+ resource_id: resource.id,
359
+ file_name: fileName
360
+ }, null, 2),
361
+ },
362
+ ],
363
+ };
364
+ }
365
+ );
366
+
187
367
  async function main() {
188
368
  const transport = new StdioServerTransport();
189
369
  await server.connect(transport);