Skip to content

Commit 39df6c6

Browse files
committed
feat(mcp): upgrade to mcp v2
1 parent ca72503 commit 39df6c6

26 files changed

Lines changed: 326 additions & 531 deletions

‎apps/browser-extension/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"superjson": "^2.2.1",
4040
"tailwind-merge": "^2.2.1",
4141
"tailwindcss-animate": "^1.0.7",
42-
"zod": "^4.0.0"
42+
"zod": "^4.2.0"
4343
},
4444
"devDependencies": {
4545
"@crxjs/vite-plugin": "2.2.0",

‎apps/mcp/package.json‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://json.schemastore.org/package.json",
33
"name": "@karakeep/mcp",
4-
"version": "0.33.0",
4+
"version": "0.33.1",
55
"description": "MCP server for Karakeep",
66
"license": "GNU Affero General Public License version 3",
77
"type": "module",
@@ -41,8 +41,8 @@
4141
},
4242
"dependencies": {
4343
"@karakeep/sdk": "workspace:*",
44-
"@modelcontextprotocol/sdk": "^1.29.0",
44+
"@modelcontextprotocol/server": "^2.0.0",
4545
"turndown": "^7.2.0",
46-
"zod": "^4.0.0"
46+
"zod": "^4.2.0"
4747
}
4848
}

‎apps/mcp/src/assets.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CallToolResult } from "@modelcontextprotocol/sdk/types";
1+
import type { CallToolResult } from "@modelcontextprotocol/server";
22
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
33

44
const { mockClient, mockTool } = vi.hoisted(() => ({
@@ -10,7 +10,7 @@ const { mockClient, mockTool } = vi.hoisted(() => ({
1010

1111
vi.mock("./shared", () => ({
1212
karakeepClient: mockClient,
13-
mcpServer: { tool: mockTool },
13+
registerTool: mockTool,
1414
}));
1515

1616
import { getAssetHandler } from "./assets";

‎apps/mcp/src/assets.ts‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import type { CallToolResult } from "@modelcontextprotocol/sdk/types";
1+
import type { CallToolResult } from "@modelcontextprotocol/server";
22
import { z } from "zod";
33

4-
import { karakeepClient, mcpServer } from "./shared";
4+
import { karakeepClient, registerTool } from "./shared";
55
import { toMcpToolError } from "./utils";
66

77
export const getAssetInputSchema = {
@@ -36,9 +36,12 @@ Expires at: ${res.data.expiresAt}`,
3636
};
3737
}
3838

39-
mcpServer.tool(
39+
registerTool(
4040
"get-asset",
41-
"Get a temporary signed URL for downloading an asset by its asset ID.",
42-
getAssetInputSchema,
41+
{
42+
description:
43+
"Get a temporary signed URL for downloading an asset by its asset ID.",
44+
inputSchema: z.object(getAssetInputSchema),
45+
},
4346
getAssetHandler,
4447
);

‎apps/mcp/src/bookmarks.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CallToolResult } from "@modelcontextprotocol/sdk/types";
1+
import type { CallToolResult } from "@modelcontextprotocol/server";
22
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
33

44
const { mockClient, mockTool, mockTurndown } = vi.hoisted(() => ({
@@ -15,7 +15,7 @@ const { mockClient, mockTool, mockTurndown } = vi.hoisted(() => ({
1515

1616
vi.mock("./shared", () => ({
1717
karakeepClient: mockClient,
18-
mcpServer: { tool: mockTool },
18+
registerTool: mockTool,
1919
turndownService: mockTurndown,
2020
}));
2121

‎apps/mcp/src/bookmarks.ts‎

Lines changed: 86 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { CallToolResult } from "@modelcontextprotocol/sdk/types";
1+
import { CallToolResult } from "@modelcontextprotocol/server";
22
import { z } from "zod";
33

4-
import { karakeepClient, mcpServer, turndownService } from "./shared";
4+
import { karakeepClient, registerTool, turndownService } from "./shared";
55
import { compactBookmark, compactList, toMcpToolError } from "./utils";
66

77
// Tools
@@ -89,18 +89,22 @@ Next cursor: ${res.data.nextCursor ? `'${res.data.nextCursor}'` : "no more pages
8989
};
9090
}
9191

92-
mcpServer.tool(
92+
registerTool(
9393
"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+
},
9698
searchBookmarksHandler,
9799
);
98100

99-
mcpServer.tool(
101+
registerTool(
100102
"get-bookmark",
101-
`Get a bookmark by id.`,
102103
{
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+
}),
104108
},
105109
async ({ bookmarkId }): Promise<CallToolResult> => {
106110
const res = await karakeepClient.GET(`/bookmarks/{bookmarkId}`, {
@@ -158,25 +162,31 @@ export async function getBookmarkListsHandler({
158162
};
159163
}
160164

161-
mcpServer.tool(
165+
registerTool(
162166
"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+
},
166172
getBookmarkListsHandler,
167173
);
168174

169-
mcpServer.tool(
175+
registerTool(
170176
"create-bookmark",
171-
`Create a link bookmark or a text bookmark`,
172177
{
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+
}),
180190
},
181191
async ({ title, type, content }): Promise<CallToolResult> => {
182192
const res = await karakeepClient.POST(`/bookmarks`, {
@@ -207,51 +217,53 @@ mcpServer.tool(
207217
},
208218
);
209219

210-
mcpServer.tool(
220+
registerTool(
211221
"update-bookmark",
212-
`Update fields on an existing bookmark. Only the fields you pass are modified; omitted fields stay unchanged. Returns the updated bookmark.`,
213222
{
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+
}),
255267
},
256268
async ({ bookmarkId, ...fields }): Promise<CallToolResult> => {
257269
const patchRes = await karakeepClient.PATCH(`/bookmarks/{bookmarkId}`, {
@@ -326,10 +338,12 @@ export async function getBookmarkContentHandler({
326338
};
327339
}
328340

329-
mcpServer.tool(
341+
registerTool(
330342
"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+
},
333347
getBookmarkContentHandler,
334348
);
335349

@@ -369,9 +383,11 @@ export async function deleteBookmarkHandler({
369383
};
370384
}
371385

372-
mcpServer.tool(
386+
registerTool(
373387
"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+
},
376392
deleteBookmarkHandler,
377393
);

‎apps/mcp/src/highlights.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CallToolResult } from "@modelcontextprotocol/sdk/types";
1+
import type { CallToolResult } from "@modelcontextprotocol/server";
22
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
33

44
const { mockClient, mockTool } = vi.hoisted(() => ({
@@ -13,7 +13,7 @@ const { mockClient, mockTool } = vi.hoisted(() => ({
1313

1414
vi.mock("./shared", () => ({
1515
karakeepClient: mockClient,
16-
mcpServer: { tool: mockTool },
16+
registerTool: mockTool,
1717
}));
1818

1919
import {

0 commit comments

Comments
 (0)