@chriscurrycc/memos-mcp 0.2.0 → 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.
@@ -1,40 +1,125 @@
1
1
  import { z } from "zod";
2
- export const registerRelationTools = (server, client) => {
3
- server.tool("list_memo_relations", "List relations of a memo (references and comments)", {
4
- id: z.number().int().describe("Memo ID"),
5
- }, async ({ id }) => {
2
+ async function resolveToNumericId(client, id) {
3
+ if (/^\d+$/.test(id)) {
4
+ return parseInt(id, 10);
5
+ }
6
+ const memo = await client.get(`/api/v1/memos:by-uid/${id}`);
7
+ const match = memo.name.match(/^memos\/(\d+)$/);
8
+ if (!match)
9
+ throw new Error(`Unexpected memo name format: ${memo.name}`);
10
+ return parseInt(match[1], 10);
11
+ }
12
+ function extractId(memoRef) {
13
+ const name = typeof memoRef === "string" ? memoRef : memoRef?.name;
14
+ if (!name || typeof name !== "string")
15
+ return undefined;
16
+ const match = name.match(/^memos\/(\d+)$/);
17
+ return match ? parseInt(match[1], 10) : undefined;
18
+ }
19
+ async function buildRelationGraph(client, startId, maxDepth) {
20
+ const visited = new Set();
21
+ const edges = [];
22
+ const edgeSet = new Set();
23
+ const queue = [{ id: startId, depth: 0 }];
24
+ while (queue.length > 0) {
25
+ const { id, depth } = queue.shift();
26
+ if (visited.has(id))
27
+ continue;
28
+ visited.add(id);
6
29
  const result = await client.get(`/api/v1/memos/${id}/relations`);
7
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
30
+ for (const r of result.relations || []) {
31
+ const fromId = extractId(r.memo);
32
+ const toId = extractId(r.relatedMemo);
33
+ if (fromId === undefined || toId === undefined)
34
+ continue;
35
+ const edgeKey = `${fromId}->${toId}:${r.type}`;
36
+ if (edgeSet.has(edgeKey))
37
+ continue;
38
+ edgeSet.add(edgeKey);
39
+ edges.push({ from: fromId, to: toId, type: r.type });
40
+ const neighborId = fromId === id ? toId : fromId;
41
+ if (depth < maxDepth && !visited.has(neighborId)) {
42
+ queue.push({ id: neighborId, depth: depth + 1 });
43
+ }
44
+ }
45
+ }
46
+ return { nodes: [...visited], edges };
47
+ }
48
+ export const registerRelationTools = (server, client) => {
49
+ server.registerTool("list_memo_relations", {
50
+ description: [
51
+ "List relations of a memo. Returns edges with direction (from → to) and type.",
52
+ "- depth=1 (default): direct relations only.",
53
+ "- depth=2+: recursively expand related memos to build a relation graph.",
54
+ "Each edge shows {from, to, type}. Use get_memo to inspect specific nodes.",
55
+ ].join(" "),
56
+ inputSchema: {
57
+ id: z.string().min(1).describe("Memo identifier. Numeric ID or UID string"),
58
+ depth: z.number().int().min(1).max(5).default(1).describe("How many levels deep to traverse. 1 = direct relations, 2+ = recursive graph"),
59
+ },
60
+ annotations: { readOnlyHint: true, openWorldHint: false },
61
+ }, async ({ id, depth }) => {
62
+ const numericId = await resolveToNumericId(client, id);
63
+ const { nodes, edges } = await buildRelationGraph(client, numericId, depth);
64
+ if (edges.length === 0) {
65
+ return { content: [{ type: "text", text: "No relations found." }] };
66
+ }
67
+ const output = {
68
+ startNode: numericId,
69
+ nodes,
70
+ edges,
71
+ };
72
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
8
73
  });
9
- server.tool("set_memo_relations", "Set relations for a memo (replaces all existing relations)", {
10
- id: z.number().int().describe("Memo ID"),
11
- relations: z
12
- .array(z.object({
13
- relatedMemoId: z.number().int().describe("Related memo ID"),
14
- type: z.enum(["REFERENCE", "COMMENT"]).describe("Relation type"),
15
- }))
16
- .describe("List of relations to set"),
17
- }, async ({ id, relations }) => {
74
+ server.registerTool("set_memo_relations", {
75
+ description: [
76
+ "Add or remove REFERENCE relations for a memo.",
77
+ "- action=\"add\": add new references without affecting existing ones.",
78
+ "- action=\"remove\": remove specific references.",
79
+ "Note: COMMENT relations are managed by the system and cannot be modified here.",
80
+ ].join(" "),
81
+ inputSchema: {
82
+ id: z.string().min(1).describe("Memo identifier. Numeric ID or UID string"),
83
+ action: z.enum(["add", "remove"]).describe("Whether to add or remove relations"),
84
+ targetIds: z.array(z.string().min(1)).min(1).describe("IDs of memos to add/remove as references. Numeric IDs or UID strings"),
85
+ },
86
+ annotations: { destructiveHint: false, idempotentHint: true, openWorldHint: false },
87
+ }, async ({ id, action, targetIds }) => {
88
+ const numericId = await resolveToNumericId(client, id);
89
+ const resolvedTargets = await Promise.all(targetIds.map((t) => resolveToNumericId(client, t)));
90
+ const current = await client.get(`/api/v1/memos/${numericId}/relations`);
91
+ const existing = (current.relations || []).filter((r) => extractId(r.memo) === numericId && r.type === "REFERENCE");
92
+ let finalIds;
93
+ if (action === "add") {
94
+ const existingIds = new Set(existing.map((r) => extractId(r.relatedMemo)));
95
+ finalIds = [
96
+ ...existing.map((r) => extractId(r.relatedMemo)),
97
+ ...resolvedTargets.filter((t) => !existingIds.has(t)),
98
+ ];
99
+ }
100
+ else {
101
+ const removeSet = new Set(resolvedTargets);
102
+ finalIds = existing
103
+ .map((r) => extractId(r.relatedMemo))
104
+ .filter((rid) => !removeSet.has(rid));
105
+ }
18
106
  const body = {
19
- relations: relations.map((r) => ({
20
- memo: `memos/${id}`,
21
- relatedMemo: `memos/${r.relatedMemoId}`,
22
- type: r.type,
107
+ relations: finalIds.map((targetId) => ({
108
+ memo: { name: `memos/${numericId}` },
109
+ relatedMemo: { name: `memos/${targetId}` },
110
+ type: "REFERENCE",
23
111
  })),
24
112
  };
25
- const result = await client.patch(`/api/v1/memos/${id}/relations`, body);
26
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
27
- });
28
- server.tool("delete_memo_relation", "Remove a specific relation from a memo", {
29
- id: z.number().int().describe("Memo ID"),
30
- relatedMemoId: z.number().int().describe("Related memo ID to unlink"),
31
- type: z.enum(["REFERENCE", "COMMENT"]).describe("Relation type to remove"),
32
- }, async ({ id, relatedMemoId, type }) => {
33
- // Get current relations, filter out the target, set remaining
34
- const current = await client.get(`/api/v1/memos/${id}/relations`);
35
- const remaining = (current.relations || []).filter((r) => !(r.relatedMemo === `memos/${relatedMemoId}` && r.type === type));
36
- const result = await client.patch(`/api/v1/memos/${id}/relations`, { relations: remaining });
37
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
113
+ await client.patch(`/api/v1/memos/${numericId}/relations`, body);
114
+ const verb = action === "add" ? "added" : "removed";
115
+ return {
116
+ content: [
117
+ {
118
+ type: "text",
119
+ text: `${resolvedTargets.length} reference(s) ${verb}. Memo ${id} now has ${finalIds.length} outgoing reference(s).`,
120
+ },
121
+ ],
122
+ };
38
123
  });
39
124
  };
40
125
  //# sourceMappingURL=relations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"relations.js","sourceRoot":"","sources":["../../src/tools/relations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"relations.js","sourceRoot":"","sources":["../../src/tools/relations.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/tools/resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,eAAO,MAAM,qBAAqB,GAAI,QAAQ,SAAS,EAAE,QAAQ,WAAW,SAmD3E,CAAC"}
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/tools/resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAkB3C,eAAO,MAAM,qBAAqB,GAAI,QAAQ,SAAS,EAAE,QAAQ,WAAW,SAiE3E,CAAC"}
@@ -1,28 +1,89 @@
1
1
  import { z } from "zod";
2
+ function summarizeResource(res) {
3
+ const name = res.name;
4
+ const id = name?.match(/^resources\/(\d+)$/)?.[1];
5
+ const summary = {
6
+ id: id ? Number(id) : undefined,
7
+ uid: res.uid,
8
+ filename: res.filename,
9
+ type: res.type,
10
+ size: res.size,
11
+ };
12
+ if (res.externalLink)
13
+ summary.externalLink = res.externalLink;
14
+ if (res.memo)
15
+ summary.memo = res.memo;
16
+ return summary;
17
+ }
2
18
  export const registerResourceTools = (server, client) => {
3
- server.tool("list_resources", "List all resources (attachments)", {}, async () => {
19
+ server.registerTool("list_resources", {
20
+ description: "List all resources (attachments) owned by the current user.",
21
+ annotations: { readOnlyHint: true, openWorldHint: false },
22
+ }, async () => {
4
23
  const result = await client.get("/api/v1/resources");
5
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
24
+ const summaries = (result.resources || []).map((r) => summarizeResource(r));
25
+ return { content: [{ type: "text", text: JSON.stringify(summaries, null, 2) }] };
6
26
  });
7
- server.tool("get_resource", "Get a resource by ID", {
8
- id: z.number().int().describe("Resource ID"),
9
- }, async ({ id }) => {
10
- const resource = await client.get(`/api/v1/resources/${id}`);
11
- return { content: [{ type: "text", text: JSON.stringify(resource, null, 2) }] };
27
+ server.registerTool("upload_resource", {
28
+ description: "Upload a file as a resource. The file content must be base64 encoded. Optionally link it to a memo.",
29
+ inputSchema: {
30
+ filename: z.string().min(1).describe("File name with extension, e.g. \"photo.jpg\""),
31
+ contentBase64: z.string().min(1).describe("File content as a base64 encoded string"),
32
+ type: z.string().optional().describe("MIME type, e.g. \"image/jpeg\". Auto-detected from extension if omitted"),
33
+ memoId: z.number().int().optional().describe("Memo ID to link this resource to"),
34
+ },
35
+ annotations: { destructiveHint: false, openWorldHint: false },
36
+ }, async ({ filename, contentBase64, type, memoId }) => {
37
+ const mimeType = type ?? guessMimeType(filename);
38
+ const body = {
39
+ filename,
40
+ content: contentBase64,
41
+ type: mimeType,
42
+ };
43
+ if (memoId !== undefined) {
44
+ body.memo = `memos/${memoId}`;
45
+ }
46
+ const resource = await client.post("/api/v1/resources", body);
47
+ const id = resource.name?.match(/^resources\/(\d+)$/)?.[1];
48
+ return {
49
+ content: [
50
+ {
51
+ type: "text",
52
+ text: `Resource uploaded: id=${id}, filename="${resource.filename}"`,
53
+ },
54
+ ],
55
+ };
12
56
  });
13
- server.tool("delete_resource", "Delete a resource", {
14
- id: z.number().int().describe("Resource ID"),
57
+ server.registerTool("delete_resource", {
58
+ description: "Delete a resource permanently.",
59
+ inputSchema: {
60
+ id: z.number().int().describe("Resource ID"),
61
+ },
62
+ annotations: { destructiveHint: true, idempotentHint: true, openWorldHint: false },
15
63
  }, async ({ id }) => {
16
64
  await client.delete(`/api/v1/resources/${id}`);
17
65
  return { content: [{ type: "text", text: `Resource ${id} deleted.` }] };
18
66
  });
19
- server.tool("link_resource_to_memo", "Link resources to a memo", {
20
- memoId: z.number().int().describe("Memo ID"),
21
- resourceIds: z.array(z.number().int()).min(1).describe("Resource IDs to link"),
22
- }, async ({ memoId, resourceIds }) => {
23
- const resources = resourceIds.map((id) => ({ name: `resources/${id}` }));
24
- const result = await client.patch(`/api/v1/memos/${memoId}/resources`, { resources });
25
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
26
- });
27
67
  };
68
+ const MIME_MAP = {
69
+ jpg: "image/jpeg",
70
+ jpeg: "image/jpeg",
71
+ png: "image/png",
72
+ gif: "image/gif",
73
+ webp: "image/webp",
74
+ svg: "image/svg+xml",
75
+ pdf: "application/pdf",
76
+ mp3: "audio/mpeg",
77
+ mp4: "video/mp4",
78
+ webm: "video/webm",
79
+ txt: "text/plain",
80
+ md: "text/markdown",
81
+ json: "application/json",
82
+ csv: "text/csv",
83
+ zip: "application/zip",
84
+ };
85
+ function guessMimeType(filename) {
86
+ const ext = filename.split(".").pop()?.toLowerCase() ?? "";
87
+ return MIME_MAP[ext] ?? "application/octet-stream";
88
+ }
28
89
  //# sourceMappingURL=resources.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/tools/resources.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAAiB,EAAE,MAAmB,EAAE,EAAE;IAC9E,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,kCAAkC,EAClC,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAA4B,mBAAmB,CAAC,CAAC;QAChF,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IACzF,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,cAAc,EACd,sBAAsB,EACtB;QACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;KAC7C,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QACf,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAW,qBAAqB,EAAE,EAAE,CAAC,CAAC;QACvE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAC3F,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,mBAAmB,EACnB;QACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;KAC7C,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QACf,MAAM,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;QAC/C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IACnF,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,uBAAuB,EACvB,0BAA0B,EAC1B;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC5C,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,sBAAsB,CAAC;KAC/E,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE;QAChC,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACzE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAC/B,iBAAiB,MAAM,YAAY,EACnC,EAAE,SAAS,EAAE,CACd,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IACzF,CAAC,CACF,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/tools/resources.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/tools/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,eAAO,MAAM,mBAAmB,GAAI,QAAQ,SAAS,EAAE,QAAQ,WAAW,SAiHzE,CAAC"}
1
+ {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/tools/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAS3C,eAAO,MAAM,mBAAmB,GAAI,QAAQ,SAAS,EAAE,QAAQ,WAAW,SAwIzE,CAAC"}
@@ -1,72 +1,101 @@
1
1
  import { z } from "zod";
2
+ import { summarizeMemo } from "./utils.js";
2
3
  export const registerReviewTools = (server, client) => {
3
- server.tool("list_review_memos", "Get memos due for spaced-repetition review", {
4
- force: z.boolean().default(false).describe("Force refresh the daily review cache"),
5
- }, async ({ force }) => {
4
+ server.registerTool("get_review_memos", {
5
+ description: "Get today's batch of memos for spaced-repetition review (回顾). Returns a daily batch selected by the review algorithm.",
6
+ inputSchema: {
7
+ refresh: z.boolean().optional().describe("Load a new batch even if today's review is already completed"),
8
+ },
9
+ annotations: { readOnlyHint: true, openWorldHint: false },
10
+ }, async ({ refresh }) => {
6
11
  const params = {};
7
- if (force)
12
+ if (refresh)
8
13
  params.force = "true";
9
14
  const result = await client.get("/api/v1/review/memos", params);
10
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
15
+ const summaries = (result.memos || []).map((m) => summarizeMemo(m));
16
+ const output = {
17
+ memos: summaries,
18
+ totalEligible: result.totalCount,
19
+ completed: result.completed,
20
+ };
21
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
11
22
  });
12
- server.tool("get_random_memo", "Get a random memo for surprise review", {}, async () => {
13
- const memo = await client.get("/api/v1/review/random");
14
- return { content: [{ type: "text", text: JSON.stringify(memo, null, 2) }] };
23
+ server.registerTool("complete_review", {
24
+ description: "Mark the current review batch as completed. Call this after the user has finished reviewing all memos from get_review_memos.",
25
+ inputSchema: {
26
+ memoIds: z.array(z.number().int()).min(1).describe("IDs of the reviewed memos"),
27
+ },
28
+ annotations: { destructiveHint: false, idempotentHint: true, openWorldHint: false },
29
+ }, async ({ memoIds }) => {
30
+ const result = await client.post("/api/v1/review/record", {
31
+ memoNames: memoIds.map((id) => `memos/${id}`),
32
+ source: "REVIEW_SOURCE_REVIEW",
33
+ });
34
+ return {
35
+ content: [
36
+ {
37
+ type: "text",
38
+ text: `Review completed: ${result.recordedCount} memo(s) recorded.`,
39
+ },
40
+ ],
41
+ };
15
42
  });
16
- server.tool("list_on_this_day_memos", "Get memos created on this day in previous years", {
17
- month: z.number().int().min(1).max(12).optional().describe("Month (1-12), defaults to current"),
18
- day: z.number().int().min(1).max(31).optional().describe("Day (1-31), defaults to current"),
19
- offset: z.number().int().min(0).default(0).describe("Pagination offset"),
20
- pageSize: z.number().int().min(1).max(100).default(20).describe("Page size"),
21
- }, async ({ month, day, offset, pageSize }) => {
43
+ server.registerTool("get_on_this_day_memos", {
44
+ description: "Get memos created on this day in previous years, grouped by year. Great for revisiting past thoughts and memories.",
45
+ inputSchema: {
46
+ month: z.number().int().min(1).max(12).optional().describe("Month (1-12), defaults to current month"),
47
+ day: z.number().int().min(1).max(31).optional().describe("Day (1-31), defaults to current day"),
48
+ },
49
+ annotations: { readOnlyHint: true, openWorldHint: false },
50
+ }, async ({ month, day }) => {
22
51
  const params = {
23
- offset: String(offset),
24
- pageSize: String(pageSize),
52
+ pageSize: "100",
25
53
  };
26
54
  if (month !== undefined)
27
55
  params.month = String(month);
28
56
  if (day !== undefined)
29
57
  params.day = String(day);
30
58
  const result = await client.get("/api/v1/review/on-this-day", params);
31
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
59
+ const groups = (result.groups || []).map((g) => ({
60
+ year: g.year,
61
+ memos: (g.memos || []).map((m) => summarizeMemo(m)),
62
+ }));
63
+ if (groups.length === 0) {
64
+ return { content: [{ type: "text", text: "No memos found on this day in previous years." }] };
65
+ }
66
+ const output = { groups, totalCount: result.totalCount };
67
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
32
68
  });
33
- server.tool("get_time_travel_memos", "Get memos from a random or specified time period", {
34
- pageSize: z.number().int().min(1).max(100).default(20).describe("Page size"),
35
- periodStart: z.string().optional().describe("ISO 8601 start date, e.g. 2024-01-01T00:00:00Z"),
36
- periodEnd: z.string().optional().describe("ISO 8601 end date"),
37
- offset: z.number().int().min(0).default(0).describe("Pagination offset"),
38
- }, async ({ pageSize, periodStart, periodEnd, offset }) => {
39
- const params = {
40
- pageSize: String(pageSize),
41
- offset: String(offset),
69
+ server.registerTool("update_review_setting", {
70
+ description: "Update review preferences: how many memos per batch, and which tags to include or exclude.",
71
+ inputSchema: {
72
+ sessionSize: z.number().int().min(1).max(50).optional().describe("Number of memos per review batch (default 10)"),
73
+ includeTags: z.array(z.string()).optional().describe("Only review memos with these tags. Empty array or omit for all tags"),
74
+ excludeTags: z.array(z.string()).optional().describe("Skip memos with these tags"),
75
+ },
76
+ annotations: { destructiveHint: false, idempotentHint: true, openWorldHint: false },
77
+ }, async ({ sessionSize, includeTags, excludeTags }) => {
78
+ const currentUser = await client.getCurrentUser();
79
+ const userId = currentUser.match(/^users\/(\d+)$/)?.[1];
80
+ if (!userId)
81
+ throw new Error(`Unexpected user format: ${currentUser}`);
82
+ const current = await client.get(`/api/v1/users/${userId}/setting`);
83
+ const reviewSetting = {
84
+ sessionSize: sessionSize ?? current.reviewSetting?.sessionSize ?? 10,
85
+ includeTags: includeTags ?? current.reviewSetting?.includeTags ?? [],
86
+ excludeTags: excludeTags ?? current.reviewSetting?.excludeTags ?? [],
42
87
  };
43
- if (periodStart)
44
- params.periodStart = periodStart;
45
- if (periodEnd)
46
- params.periodEnd = periodEnd;
47
- const result = await client.get("/api/v1/review/time-travel", params);
48
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
49
- });
50
- server.tool("record_review", "Record that memos have been reviewed", {
51
- memoIds: z.array(z.number().int()).min(1).describe("IDs of reviewed memos"),
52
- source: z
53
- .enum([
54
- "REVIEW_SOURCE_REVIEW",
55
- "REVIEW_SOURCE_ON_THIS_DAY",
56
- "REVIEW_SOURCE_SURPRISE",
57
- "REVIEW_SOURCE_TIME_TRAVEL",
58
- ])
59
- .describe("Review source"),
60
- }, async ({ memoIds, source }) => {
61
- const result = await client.post("/api/v1/review/record", {
62
- memoNames: memoIds.map((id) => `memos/${id}`),
63
- source,
88
+ await client.patch(`/api/v1/users/${userId}/setting`, { reviewSetting }, {
89
+ updateMask: "review_setting",
64
90
  });
65
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
66
- });
67
- server.tool("get_review_stats", "Get review statistics", {}, async () => {
68
- const stats = await client.get("/api/v1/review/stats");
69
- return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
91
+ return {
92
+ content: [
93
+ {
94
+ type: "text",
95
+ text: `Review setting updated: ${JSON.stringify(reviewSetting)}`,
96
+ },
97
+ ],
98
+ };
70
99
  });
71
100
  };
72
101
  //# sourceMappingURL=review.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/tools/review.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/tools/review.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../src/tools/tags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAyD3C,eAAO,MAAM,gBAAgB,GAAI,QAAQ,SAAS,EAAE,QAAQ,WAAW,SA+HtE,CAAC"}
1
+ {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../src/tools/tags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAyD3C,eAAO,MAAM,gBAAgB,GAAI,QAAQ,SAAS,EAAE,QAAQ,WAAW,SA2HtE,CAAC"}
@@ -46,27 +46,22 @@ async function fetchAllMemoMetadata(client) {
46
46
  return result.memos || [];
47
47
  }
48
48
  export const registerTagTools = (server, client) => {
49
- server.tool("list_tags", [
50
- "List tags with usage counts. Returns {name, count, hasChildren, pinned, emoji} for each tag.",
51
- "- No args: top-level tags only.",
52
- "- recursive=true: every tag (flat list, good for recommending tags).",
53
- "- parent=\"x\": direct children of tag \"x\".",
54
- "- parent=\"x\" + recursive=true: all descendants under \"x\".",
55
- "- pinnedOnly=true: only user-pinned tags.",
56
- "Tags use \"/\" for hierarchy (e.g. \"project/work/backend\").",
57
- ].join(" "), {
58
- parent: z
59
- .string()
60
- .optional()
61
- .describe("Parent tag to scope the listing (e.g. \"project\" or \"project/work\"). Omit to start from root"),
62
- recursive: z
63
- .boolean()
64
- .optional()
65
- .describe("When true, return all tags/descendants instead of only direct children"),
66
- pinnedOnly: z
67
- .boolean()
68
- .optional()
69
- .describe("When true, only return user-pinned tags"),
49
+ server.registerTool("list_tags", {
50
+ description: [
51
+ "List tags with usage counts. Returns {name, count, hasChildren, pinned, emoji} for each tag.",
52
+ "- No args: top-level tags only.",
53
+ "- recursive=true: every tag (flat list, good for recommending tags).",
54
+ "- parent=\"x\": direct children of tag \"x\".",
55
+ "- parent=\"x\" + recursive=true: all descendants under \"x\".",
56
+ "- pinnedOnly=true: only user-pinned tags.",
57
+ "Tags use \"/\" for hierarchy (e.g. \"project/work/backend\").",
58
+ ].join(" "),
59
+ inputSchema: {
60
+ parent: z.string().optional().describe("Parent tag to scope the listing (e.g. \"project\" or \"project/work\"). Omit to start from root"),
61
+ recursive: z.boolean().optional().describe("When true, return all tags/descendants instead of only direct children"),
62
+ pinnedOnly: z.boolean().optional().describe("When true, only return user-pinned tags"),
63
+ },
64
+ annotations: { readOnlyHint: true, openWorldHint: false },
70
65
  }, async ({ parent, recursive, pinnedOnly }) => {
71
66
  const [memos, pinnedResult] = await Promise.all([
72
67
  fetchAllMemoMetadata(client),
@@ -103,10 +98,14 @@ export const registerTagTools = (server, client) => {
103
98
  }
104
99
  return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
105
100
  });
106
- server.tool("update_tag", "Update a tag's pinned status or emoji. Pinned tags are highlighted in the UI for quick access.", {
107
- tagName: z.string().min(1).describe("Tag name (without #)"),
108
- emoji: z.string().optional().describe("Emoji to associate with the tag (e.g. \"📅\"). Pass empty string to remove"),
109
- pinned: z.boolean().optional().describe("Pin or unpin the tag"),
101
+ server.registerTool("update_tag", {
102
+ description: "Update a tag's pinned status or emoji. Pinned tags are highlighted in the UI for quick access.",
103
+ inputSchema: {
104
+ tagName: z.string().min(1).describe("Tag name (without #)"),
105
+ emoji: z.string().optional().describe("Emoji to associate with the tag (e.g. \"📅\"). Pass empty string to remove"),
106
+ pinned: z.boolean().optional().describe("Pin or unpin the tag"),
107
+ },
108
+ annotations: { destructiveHint: false, idempotentHint: true, openWorldHint: false },
110
109
  }, async ({ tagName, emoji, pinned }) => {
111
110
  const body = {};
112
111
  const updateMaskPaths = [];
@@ -130,11 +129,14 @@ export const registerTagTools = (server, client) => {
130
129
  content: [{ type: "text", text: `Tag "#${tagName}" updated: ${parts.join(", ")}.` }],
131
130
  };
132
131
  });
133
- server.tool("rename_tag", "Rename a tag across ALL memos. This is a destructive operation that modifies memo content globally — use with caution.", {
134
- oldTag: z.string().min(1).describe("Current tag name (without #)"),
135
- newTag: z.string().min(1).describe("New tag name (without #)"),
136
- }, { destructiveHint: true }, async ({ oldTag, newTag }) => {
137
- // parent = "memos/-" means rename across all memos
132
+ server.registerTool("rename_tag", {
133
+ description: "Rename a tag across ALL memos. This is a destructive operation that modifies memo content globally — use with caution.",
134
+ inputSchema: {
135
+ oldTag: z.string().min(1).describe("Current tag name (without #)"),
136
+ newTag: z.string().min(1).describe("New tag name (without #)"),
137
+ },
138
+ annotations: { destructiveHint: true, idempotentHint: true, openWorldHint: false },
139
+ }, async ({ oldTag, newTag }) => {
138
140
  await client.patch(`/api/v1/memos/-/tags:rename`, {
139
141
  parent: "memos/-",
140
142
  oldTag,