|
1 | | -import { CallToolResult } from "@modelcontextprotocol/sdk/types"; |
| 1 | +import { CallToolResult } from "@modelcontextprotocol/server"; |
2 | 2 | import { z } from "zod"; |
3 | 3 |
|
4 | | -import { karakeepClient, mcpServer, turndownService } from "./shared"; |
| 4 | +import { karakeepClient, registerTool, turndownService } from "./shared"; |
5 | 5 | import { compactBookmark, compactList, toMcpToolError } from "./utils"; |
6 | 6 |
|
7 | 7 | // Tools |
@@ -89,18 +89,22 @@ Next cursor: ${res.data.nextCursor ? `'${res.data.nextCursor}'` : "no more pages |
89 | 89 | }; |
90 | 90 | } |
91 | 91 |
|
92 | | -mcpServer.tool( |
| 92 | +registerTool( |
93 | 93 | "search-bookmarks", |
94 | | - `Search for bookmarks matching a specific query using full-text, semantic, or hybrid search.`, |
95 | | - searchBookmarksInputSchema, |
| 94 | + { |
| 95 | + description: `Search for bookmarks matching a specific query using full-text, semantic, or hybrid search.`, |
| 96 | + inputSchema: z.object(searchBookmarksInputSchema), |
| 97 | + }, |
96 | 98 | searchBookmarksHandler, |
97 | 99 | ); |
98 | 100 |
|
99 | | -mcpServer.tool( |
| 101 | +registerTool( |
100 | 102 | "get-bookmark", |
101 | | - `Get a bookmark by id.`, |
102 | 103 | { |
103 | | - bookmarkId: z.string().describe(`The bookmarkId to get.`), |
| 104 | + description: `Get a bookmark by id.`, |
| 105 | + inputSchema: z.object({ |
| 106 | + bookmarkId: z.string().describe(`The bookmarkId to get.`), |
| 107 | + }), |
104 | 108 | }, |
105 | 109 | async ({ bookmarkId }): Promise<CallToolResult> => { |
106 | 110 | const res = await karakeepClient.GET(`/bookmarks/{bookmarkId}`, { |
@@ -158,25 +162,31 @@ export async function getBookmarkListsHandler({ |
158 | 162 | }; |
159 | 163 | } |
160 | 164 |
|
161 | | -mcpServer.tool( |
| 165 | +registerTool( |
162 | 166 | "get-bookmark-lists", |
163 | | - `List every list that contains a bookmark.`, |
164 | | - getBookmarkListsInputSchema, |
165 | | - { readOnlyHint: true }, |
| 167 | + { |
| 168 | + description: `List every list that contains a bookmark.`, |
| 169 | + inputSchema: z.object(getBookmarkListsInputSchema), |
| 170 | + annotations: { readOnlyHint: true }, |
| 171 | + }, |
166 | 172 | getBookmarkListsHandler, |
167 | 173 | ); |
168 | 174 |
|
169 | | -mcpServer.tool( |
| 175 | +registerTool( |
170 | 176 | "create-bookmark", |
171 | | - `Create a link bookmark or a text bookmark`, |
172 | 177 | { |
173 | | - type: z.enum(["link", "text"]).describe(`The type of bookmark to create.`), |
174 | | - title: z.string().optional().describe(`The title of the bookmark`), |
175 | | - content: z |
176 | | - .string() |
177 | | - .describe( |
178 | | - "If type is text, the text to be bookmarked. If the type is link, then it's the URL to be bookmarked.", |
179 | | - ), |
| 178 | + description: `Create a link bookmark or a text bookmark`, |
| 179 | + inputSchema: z.object({ |
| 180 | + type: z |
| 181 | + .enum(["link", "text"]) |
| 182 | + .describe(`The type of bookmark to create.`), |
| 183 | + title: z.string().optional().describe(`The title of the bookmark`), |
| 184 | + content: z |
| 185 | + .string() |
| 186 | + .describe( |
| 187 | + "If type is text, the text to be bookmarked. If the type is link, then it's the URL to be bookmarked.", |
| 188 | + ), |
| 189 | + }), |
180 | 190 | }, |
181 | 191 | async ({ title, type, content }): Promise<CallToolResult> => { |
182 | 192 | const res = await karakeepClient.POST(`/bookmarks`, { |
@@ -207,51 +217,53 @@ mcpServer.tool( |
207 | 217 | }, |
208 | 218 | ); |
209 | 219 |
|
210 | | -mcpServer.tool( |
| 220 | +registerTool( |
211 | 221 | "update-bookmark", |
212 | | - `Update fields on an existing bookmark. Only the fields you pass are modified; omitted fields stay unchanged. Returns the updated bookmark.`, |
213 | 222 | { |
214 | | - bookmarkId: z.string().describe(`The bookmarkId to update.`), |
215 | | - title: z |
216 | | - .string() |
217 | | - .nullable() |
218 | | - .optional() |
219 | | - .describe(`The bookmark's user-set title. Pass null to clear it.`), |
220 | | - note: z.string().optional().describe(`A free-form note on the bookmark.`), |
221 | | - summary: z |
222 | | - .string() |
223 | | - .nullable() |
224 | | - .optional() |
225 | | - .describe(`The bookmark's summary. Pass null to clear it.`), |
226 | | - archived: z |
227 | | - .boolean() |
228 | | - .optional() |
229 | | - .describe(`Whether the bookmark is archived.`), |
230 | | - favourited: z |
231 | | - .boolean() |
232 | | - .optional() |
233 | | - .describe(`Whether the bookmark is favourited.`), |
234 | | - url: z.string().url().optional().describe(`New URL for a link bookmark.`), |
235 | | - description: z |
236 | | - .string() |
237 | | - .nullable() |
238 | | - .optional() |
239 | | - .describe(`Link description. Pass null to clear it.`), |
240 | | - author: z |
241 | | - .string() |
242 | | - .nullable() |
243 | | - .optional() |
244 | | - .describe(`Link author. Pass null to clear it.`), |
245 | | - publisher: z |
246 | | - .string() |
247 | | - .nullable() |
248 | | - .optional() |
249 | | - .describe(`Link publisher. Pass null to clear it.`), |
250 | | - createdAt: z |
251 | | - .string() |
252 | | - .datetime() |
253 | | - .optional() |
254 | | - .describe(`Override the bookmark's createdAt timestamp (ISO 8601).`), |
| 223 | + description: `Update fields on an existing bookmark. Only the fields you pass are modified; omitted fields stay unchanged. Returns the updated bookmark.`, |
| 224 | + inputSchema: z.object({ |
| 225 | + bookmarkId: z.string().describe(`The bookmarkId to update.`), |
| 226 | + title: z |
| 227 | + .string() |
| 228 | + .nullable() |
| 229 | + .optional() |
| 230 | + .describe(`The bookmark's user-set title. Pass null to clear it.`), |
| 231 | + note: z.string().optional().describe(`A free-form note on the bookmark.`), |
| 232 | + summary: z |
| 233 | + .string() |
| 234 | + .nullable() |
| 235 | + .optional() |
| 236 | + .describe(`The bookmark's summary. Pass null to clear it.`), |
| 237 | + archived: z |
| 238 | + .boolean() |
| 239 | + .optional() |
| 240 | + .describe(`Whether the bookmark is archived.`), |
| 241 | + favourited: z |
| 242 | + .boolean() |
| 243 | + .optional() |
| 244 | + .describe(`Whether the bookmark is favourited.`), |
| 245 | + url: z.string().url().optional().describe(`New URL for a link bookmark.`), |
| 246 | + description: z |
| 247 | + .string() |
| 248 | + .nullable() |
| 249 | + .optional() |
| 250 | + .describe(`Link description. Pass null to clear it.`), |
| 251 | + author: z |
| 252 | + .string() |
| 253 | + .nullable() |
| 254 | + .optional() |
| 255 | + .describe(`Link author. Pass null to clear it.`), |
| 256 | + publisher: z |
| 257 | + .string() |
| 258 | + .nullable() |
| 259 | + .optional() |
| 260 | + .describe(`Link publisher. Pass null to clear it.`), |
| 261 | + createdAt: z |
| 262 | + .string() |
| 263 | + .datetime() |
| 264 | + .optional() |
| 265 | + .describe(`Override the bookmark's createdAt timestamp (ISO 8601).`), |
| 266 | + }), |
255 | 267 | }, |
256 | 268 | async ({ bookmarkId, ...fields }): Promise<CallToolResult> => { |
257 | 269 | const patchRes = await karakeepClient.PATCH(`/bookmarks/{bookmarkId}`, { |
@@ -326,10 +338,12 @@ export async function getBookmarkContentHandler({ |
326 | 338 | }; |
327 | 339 | } |
328 | 340 |
|
329 | | -mcpServer.tool( |
| 341 | +registerTool( |
330 | 342 | "get-bookmark-content", |
331 | | - `Get the content of the bookmark in markdown`, |
332 | | - getBookmarkContentInputSchema, |
| 343 | + { |
| 344 | + description: `Get the content of the bookmark in markdown`, |
| 345 | + inputSchema: z.object(getBookmarkContentInputSchema), |
| 346 | + }, |
333 | 347 | getBookmarkContentHandler, |
334 | 348 | ); |
335 | 349 |
|
@@ -369,9 +383,11 @@ export async function deleteBookmarkHandler({ |
369 | 383 | }; |
370 | 384 | } |
371 | 385 |
|
372 | | -mcpServer.tool( |
| 386 | +registerTool( |
373 | 387 | "delete-bookmark", |
374 | | - `Delete a bookmark by id. This is destructive — the bookmark, its highlights, and its assets are removed.`, |
375 | | - deleteBookmarkInputSchema, |
| 388 | + { |
| 389 | + description: `Delete a bookmark by id. This is destructive — the bookmark, its highlights, and its assets are removed.`, |
| 390 | + inputSchema: z.object(deleteBookmarkInputSchema), |
| 391 | + }, |
376 | 392 | deleteBookmarkHandler, |
377 | 393 | ); |
0 commit comments