keepsake-mcp 1.3.0 → 1.5.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.
- package/README.md +8 -4
- package/build/tools.js +57 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,7 +100,7 @@ Add to `.cursor/mcp.json` in your project:
|
|
|
100
100
|
}
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
## Available tools (
|
|
103
|
+
## Available tools (62)
|
|
104
104
|
|
|
105
105
|
### Contacts
|
|
106
106
|
| Tool | Description |
|
|
@@ -165,12 +165,12 @@ Add to `.cursor/mcp.json` in your project:
|
|
|
165
165
|
### Tags
|
|
166
166
|
| Tool | Description |
|
|
167
167
|
|------|-------------|
|
|
168
|
-
| `list_tags` | List
|
|
169
|
-
| `get_tag` | Get a tag by ID with all properties
|
|
168
|
+
| `list_tags` | List tags (lightweight — ordering arrays omitted), with optional name search (`q`) |
|
|
169
|
+
| `get_tag` | Get a tag by ID with all properties, including `tasks_order` (section markers `h:<header_id>`) |
|
|
170
170
|
| `create_tag` | Create a new tag |
|
|
171
171
|
| `update_tag` | Update a tag (name, description, color, icon, view mode, favorite) |
|
|
172
172
|
| `delete_tag` | Permanently delete a tag and all its links |
|
|
173
|
-
| `get_tag_items` | Get
|
|
173
|
+
| `get_tag_items` | Get items linked to a tag — filter by `types`/`status`, `summary` mode, task `sections` included |
|
|
174
174
|
| `link_tag` | Link any entity to a tag |
|
|
175
175
|
| `unlink_tag` | Remove a tag link |
|
|
176
176
|
|
|
@@ -192,6 +192,10 @@ Add to `.cursor/mcp.json` in your project:
|
|
|
192
192
|
| `unlink_entry_contact` | Remove a contact link from an entry |
|
|
193
193
|
| `link_task_contact` | Link a contact to a task |
|
|
194
194
|
| `unlink_task_contact` | Remove a contact link from a task |
|
|
195
|
+
| `link_task_note` | Link a note to a task (non-destructive, the note survives) |
|
|
196
|
+
| `unlink_task_note` | Remove a note link from a task |
|
|
197
|
+
| `link_notes` | Link two notes together (symmetric, non-destructive) |
|
|
198
|
+
| `unlink_notes` | Remove the manual link between two notes |
|
|
195
199
|
|
|
196
200
|
### Utilities
|
|
197
201
|
| Tool | Description |
|
package/build/tools.js
CHANGED
|
@@ -488,17 +488,18 @@ export function registerAllTools(server, fetchApi) {
|
|
|
488
488
|
// TAGS
|
|
489
489
|
// ===========================================================================
|
|
490
490
|
server.registerTool("list_tags", {
|
|
491
|
-
description: "List
|
|
491
|
+
description: "List tags (lightweight: the manual tasks_order arrays are omitted — use get_tag for a tag's full ordering). Tags organize contacts, entries, tasks, notes, and companies. Use q to find a tag by name.",
|
|
492
492
|
inputSchema: {
|
|
493
|
+
q: z.string().optional().describe("Filter by name (case-insensitive substring match)"),
|
|
493
494
|
limit: z.number().int().positive().optional().describe("Max results (default 50)"),
|
|
494
495
|
offset: z.number().int().nonnegative().optional().describe("Pagination offset"),
|
|
495
496
|
},
|
|
496
497
|
annotations: { title: "List tags", readOnlyHint: true, openWorldHint: false },
|
|
497
|
-
}, async ({ limit, offset }) => {
|
|
498
|
-
return toContent(await fetchApi(`/tags${qs({ limit, offset })}`));
|
|
498
|
+
}, async ({ q, limit, offset }) => {
|
|
499
|
+
return toContent(await fetchApi(`/tags${qs({ q, limit, offset })}`));
|
|
499
500
|
});
|
|
500
501
|
server.registerTool("get_tag", {
|
|
501
|
-
description: "Get a single tag by ID with all its properties (name, description, color, icon, view mode, favorite status).",
|
|
502
|
+
description: "Get a single tag by ID with all its properties (name, description, color, icon, view mode, favorite status). Includes tasks_order, the manual ordering of the tag page's task list, where entries of the form \"h:<header_id>\" are section separators (see list_task_headers).",
|
|
502
503
|
inputSchema: {
|
|
503
504
|
id: z.string().uuid().describe("Tag UUID"),
|
|
504
505
|
},
|
|
@@ -507,13 +508,22 @@ export function registerAllTools(server, fetchApi) {
|
|
|
507
508
|
return toContent(await fetchApi(`/tags/${id}`));
|
|
508
509
|
});
|
|
509
510
|
server.registerTool("get_tag_items", {
|
|
510
|
-
description: "Get
|
|
511
|
+
description: "Get items linked to a tag. By default returns every type in full — on large tags that can be a huge response, so prefer types/status/summary to narrow it (e.g. types=['tasks'], status='pending', summary=true to review a project's open tasks). When tasks are included, the response also contains `sections`: the tag page's task sections with their task_ids (header_id null = tasks outside any section).",
|
|
511
512
|
inputSchema: {
|
|
512
513
|
id: z.string().uuid().describe("Tag UUID"),
|
|
514
|
+
types: z
|
|
515
|
+
.array(z.enum(["contacts", "entries", "tasks", "notes", "companies"]))
|
|
516
|
+
.optional()
|
|
517
|
+
.describe("Only fetch these item types (default: all)"),
|
|
518
|
+
status: z.enum(["pending", "completed"]).optional().describe("Filter tasks by status"),
|
|
519
|
+
summary: z
|
|
520
|
+
.boolean()
|
|
521
|
+
.optional()
|
|
522
|
+
.describe("Return lightweight items (ids, titles, excerpts) instead of full rows — recommended unless full content is needed"),
|
|
513
523
|
},
|
|
514
524
|
annotations: { title: "Get tag items", readOnlyHint: true, openWorldHint: false },
|
|
515
|
-
}, async ({ id }) => {
|
|
516
|
-
return toContent(await fetchApi(`/tags/${id}/items`));
|
|
525
|
+
}, async ({ id, types, status, summary }) => {
|
|
526
|
+
return toContent(await fetchApi(`/tags/${id}/items${qs({ types: types?.join(","), status, summary })}`));
|
|
517
527
|
});
|
|
518
528
|
server.registerTool("create_tag", {
|
|
519
529
|
description: "A TAG is a thematic grouping space. Syntax: #name# or [[name]]. Groups notes, entries, tasks, and contacts.\n\nCreate a new tag. If a tag with the same name already exists, returns the existing tag.",
|
|
@@ -692,6 +702,46 @@ export function registerAllTools(server, fetchApi) {
|
|
|
692
702
|
}, async ({ task_id, contact_id }) => {
|
|
693
703
|
return toContent(await fetchApi(`/tasks/${task_id}/contacts/${contact_id}`, "DELETE"));
|
|
694
704
|
});
|
|
705
|
+
server.registerTool("link_task_note", {
|
|
706
|
+
description: "Link an existing note to a task (non-destructive, N-N). The note stays in the inbox/notes list and the task keeps a live link back to it — use this instead of deleting a note after creating a task from it. The note shows its linked tasks; the task shows the source note.",
|
|
707
|
+
inputSchema: {
|
|
708
|
+
task_id: z.string().uuid().describe("Task UUID"),
|
|
709
|
+
note_id: z.string().uuid().describe("Note UUID"),
|
|
710
|
+
},
|
|
711
|
+
annotations: { title: "Link task note", destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
712
|
+
}, async ({ task_id, note_id }) => {
|
|
713
|
+
return toContent(await fetchApi(`/tasks/${task_id}/notes/${note_id}`, "POST"));
|
|
714
|
+
});
|
|
715
|
+
server.registerTool("unlink_task_note", {
|
|
716
|
+
description: "Remove the link between a note and a task. Both the task and the note survive.",
|
|
717
|
+
inputSchema: {
|
|
718
|
+
task_id: z.string().uuid().describe("Task UUID"),
|
|
719
|
+
note_id: z.string().uuid().describe("Note UUID"),
|
|
720
|
+
},
|
|
721
|
+
annotations: { title: "Unlink task note", destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
722
|
+
}, async ({ task_id, note_id }) => {
|
|
723
|
+
return toContent(await fetchApi(`/tasks/${task_id}/notes/${note_id}`, "DELETE"));
|
|
724
|
+
});
|
|
725
|
+
server.registerTool("link_notes", {
|
|
726
|
+
description: "Link two notes together (knowledge base, N-N, non-destructive). The link is symmetric: both notes list each other in their linked-notes section. Use this to connect related ideas — e.g. a note that made you think of another one. Notes can also be linked inline by writing [label](note:uuid) in a note's content.",
|
|
727
|
+
inputSchema: {
|
|
728
|
+
note_id: z.string().uuid().describe("First note UUID"),
|
|
729
|
+
target_note_id: z.string().uuid().describe("Second note UUID"),
|
|
730
|
+
},
|
|
731
|
+
annotations: { title: "Link notes", destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
732
|
+
}, async ({ note_id, target_note_id }) => {
|
|
733
|
+
return toContent(await fetchApi(`/notes/${note_id}/links/${target_note_id}`, "POST"));
|
|
734
|
+
});
|
|
735
|
+
server.registerTool("unlink_notes", {
|
|
736
|
+
description: "Remove the manual link between two notes (both notes survive). Links derived from inline [label](note:uuid) markdown in a note's content are preserved — edit the content to remove those.",
|
|
737
|
+
inputSchema: {
|
|
738
|
+
note_id: z.string().uuid().describe("First note UUID"),
|
|
739
|
+
target_note_id: z.string().uuid().describe("Second note UUID"),
|
|
740
|
+
},
|
|
741
|
+
annotations: { title: "Unlink notes", destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
742
|
+
}, async ({ note_id, target_note_id }) => {
|
|
743
|
+
return toContent(await fetchApi(`/notes/${note_id}/links/${target_note_id}`, "DELETE"));
|
|
744
|
+
});
|
|
695
745
|
// ===========================================================================
|
|
696
746
|
// CONTACT TIMELINE
|
|
697
747
|
// ===========================================================================
|