keepsake-mcp 1.0.0 → 1.2.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 +22 -1
- package/build/index.js +208 -17
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ Add to `.cursor/mcp.json` in your project:
|
|
|
66
66
|
}
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
## Available tools (
|
|
69
|
+
## Available tools (51)
|
|
70
70
|
|
|
71
71
|
### Contacts
|
|
72
72
|
| Tool | Description |
|
|
@@ -132,10 +132,23 @@ Add to `.cursor/mcp.json` in your project:
|
|
|
132
132
|
| Tool | Description |
|
|
133
133
|
|------|-------------|
|
|
134
134
|
| `list_tags` | List all tags |
|
|
135
|
+
| `get_tag` | Get a tag by ID with all properties (color, icon, view mode, etc.) |
|
|
136
|
+
| `create_tag` | Create a new tag |
|
|
137
|
+
| `update_tag` | Update a tag (name, description, color, icon, view mode, favorite) |
|
|
138
|
+
| `delete_tag` | Permanently delete a tag and all its links |
|
|
135
139
|
| `get_tag_items` | Get everything linked to a tag |
|
|
136
140
|
| `link_tag` | Link any entity to a tag |
|
|
137
141
|
| `unlink_tag` | Remove a tag link |
|
|
138
142
|
|
|
143
|
+
### Task Headers (Sections)
|
|
144
|
+
| Tool | Description |
|
|
145
|
+
|------|-------------|
|
|
146
|
+
| `list_task_headers` | List all task headers (section separators) |
|
|
147
|
+
| `get_task_header` | Get a task header by ID |
|
|
148
|
+
| `create_task_header` | Create a task header (section) |
|
|
149
|
+
| `update_task_header` | Update a task header (name, description, collapsed) |
|
|
150
|
+
| `delete_task_header` | Permanently delete a task header |
|
|
151
|
+
|
|
139
152
|
### Utilities
|
|
140
153
|
| Tool | Description |
|
|
141
154
|
|------|-------------|
|
|
@@ -152,6 +165,14 @@ All tools include MCP safety annotations:
|
|
|
152
165
|
- **Update tools**: marked `destructiveHint: false, idempotentHint: true`
|
|
153
166
|
- **Delete tools**: marked `destructiveHint: true, idempotentHint: true`
|
|
154
167
|
|
|
168
|
+
## Activity tracking
|
|
169
|
+
|
|
170
|
+
Every write operation (create, update, delete) performed through the API is recorded in an **Activity Feed** visible to the user inside Keepsake. Each action shows the entity type, a content preview, and which API key was used.
|
|
171
|
+
|
|
172
|
+
This means your user can see everything you do. Be transparent and precise. If you make a mistake, let the user know so they can verify in the activity feed.
|
|
173
|
+
|
|
174
|
+
Call `get_agent_instructions` at the start of each session for the full best practices guide.
|
|
175
|
+
|
|
155
176
|
## Environment variables
|
|
156
177
|
|
|
157
178
|
| Variable | Required | Description |
|
package/build/index.js
CHANGED
|
@@ -87,10 +87,10 @@ server.registerTool("get_contact", {
|
|
|
87
87
|
return toContent(await fetchApi(`/contacts/${id}${qs({ entries_limit })}`));
|
|
88
88
|
});
|
|
89
89
|
server.registerTool("create_contact", {
|
|
90
|
-
description: "Create a new contact. first_name
|
|
90
|
+
description: "Create a new contact. first_name is required. last_name is optional (useful for contacts where you only know the first name).",
|
|
91
91
|
inputSchema: {
|
|
92
92
|
first_name: z.string().describe("First name"),
|
|
93
|
-
last_name: z.string().describe("Last name"),
|
|
93
|
+
last_name: z.string().optional().describe("Last name (optional)"),
|
|
94
94
|
email: z.string().optional().describe("Email address"),
|
|
95
95
|
phone: z.string().optional().describe("Phone number"),
|
|
96
96
|
company: z.string().optional().describe("Company name"),
|
|
@@ -213,7 +213,7 @@ server.registerTool("list_entries", {
|
|
|
213
213
|
description: "List interaction entries (calls, emails, meetings, events, etc.). Supports filtering by type, contact, and date range.",
|
|
214
214
|
inputSchema: {
|
|
215
215
|
type: z
|
|
216
|
-
.enum(["call", "email", "meeting", "event", "gift", "letter", "message", "other"])
|
|
216
|
+
.enum(["call", "email", "meeting", "event", "gift", "letter", "message", "log", "other"])
|
|
217
217
|
.optional()
|
|
218
218
|
.describe("Filter by entry type"),
|
|
219
219
|
contact_id: z.string().uuid().optional().describe("Filter by associated contact ID"),
|
|
@@ -227,10 +227,10 @@ server.registerTool("list_entries", {
|
|
|
227
227
|
return toContent(await fetchApi(`/entries${qs({ type, contact_id, from, to, limit, offset })}`));
|
|
228
228
|
});
|
|
229
229
|
server.registerTool("create_entry", {
|
|
230
|
-
description: "
|
|
230
|
+
description: "An ENTRY is a dated interaction log tied to contacts. Records something that happened (call, meeting, email…) on a specific date.\n\nCreate a new interaction entry. Content supports #tag# and [[tag]] syntax for automatic tag linking.",
|
|
231
231
|
inputSchema: {
|
|
232
232
|
type: z
|
|
233
|
-
.enum(["call", "email", "meeting", "event", "gift", "letter", "message", "other"])
|
|
233
|
+
.enum(["call", "email", "meeting", "event", "gift", "letter", "message", "log", "other"])
|
|
234
234
|
.describe("Entry type"),
|
|
235
235
|
date: z.string().describe("Date (YYYY-MM-DD)"),
|
|
236
236
|
content: z.string().optional().describe("Entry content (supports #tag# and [[tag]])"),
|
|
@@ -238,6 +238,10 @@ server.registerTool("create_entry", {
|
|
|
238
238
|
.array(z.string().uuid())
|
|
239
239
|
.optional()
|
|
240
240
|
.describe("Array of contact UUIDs to associate"),
|
|
241
|
+
tag_ids: z
|
|
242
|
+
.array(z.string().uuid())
|
|
243
|
+
.optional()
|
|
244
|
+
.describe("Array of tag UUIDs to link"),
|
|
241
245
|
},
|
|
242
246
|
annotations: { title: "Create entry", destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
243
247
|
}, async (params) => {
|
|
@@ -248,7 +252,7 @@ server.registerTool("update_entry", {
|
|
|
248
252
|
inputSchema: {
|
|
249
253
|
id: z.string().uuid().describe("Entry UUID"),
|
|
250
254
|
type: z
|
|
251
|
-
.enum(["call", "email", "meeting", "event", "gift", "letter", "message", "other"])
|
|
255
|
+
.enum(["call", "email", "meeting", "event", "gift", "letter", "message", "log", "other"])
|
|
252
256
|
.optional()
|
|
253
257
|
.describe("Entry type"),
|
|
254
258
|
date: z.string().optional().describe("Date (YYYY-MM-DD)"),
|
|
@@ -257,6 +261,10 @@ server.registerTool("update_entry", {
|
|
|
257
261
|
.array(z.string().uuid())
|
|
258
262
|
.optional()
|
|
259
263
|
.describe("Replace associated contacts"),
|
|
264
|
+
tag_ids: z
|
|
265
|
+
.array(z.string().uuid())
|
|
266
|
+
.optional()
|
|
267
|
+
.describe("Replace associated tags"),
|
|
260
268
|
},
|
|
261
269
|
annotations: { title: "Update entry", destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
262
270
|
}, async ({ id, ...body }) => {
|
|
@@ -279,9 +287,9 @@ server.registerTool("list_tasks", {
|
|
|
279
287
|
inputSchema: {
|
|
280
288
|
status: z.enum(["pending", "completed"]).optional().describe("Filter by status"),
|
|
281
289
|
date_type: z
|
|
282
|
-
.enum(["specific", "
|
|
290
|
+
.enum(["specific", "asap", "one_day"])
|
|
283
291
|
.optional()
|
|
284
|
-
.describe("Filter by date type"),
|
|
292
|
+
.describe("Filter by date type: specific (has a due date), asap (do as soon as possible), one_day (someday/no rush)"),
|
|
285
293
|
date: z.string().optional().describe("Filter by specific date (YYYY-MM-DD)"),
|
|
286
294
|
limit: z.number().int().positive().optional().describe("Max results (default 20)"),
|
|
287
295
|
offset: z.number().int().nonnegative().optional().describe("Pagination offset"),
|
|
@@ -291,15 +299,15 @@ server.registerTool("list_tasks", {
|
|
|
291
299
|
return toContent(await fetchApi(`/tasks${qs({ status, date_type, date, limit, offset })}`));
|
|
292
300
|
});
|
|
293
301
|
server.registerTool("create_task", {
|
|
294
|
-
description: "
|
|
302
|
+
description: "A TASK is an action item to accomplish. Can have due date, recurrence, priority, linked to contacts and tags.\n\nCreate a new task. Title supports #tag# and [[tag]] for automatic tag linking.",
|
|
295
303
|
inputSchema: {
|
|
296
304
|
title: z.string().describe("Task title (supports #tag# and [[tag]])"),
|
|
297
305
|
description: z.string().optional().describe("Task description"),
|
|
298
306
|
date: z.string().optional().describe("Due date (YYYY-MM-DD)"),
|
|
299
307
|
date_type: z
|
|
300
|
-
.enum(["specific", "
|
|
308
|
+
.enum(["specific", "asap", "one_day"])
|
|
301
309
|
.optional()
|
|
302
|
-
.describe("Date type (default
|
|
310
|
+
.describe("Date type: specific (has a due date, default), asap (do as soon as possible, no date needed), one_day (someday/no rush, no date needed)"),
|
|
303
311
|
priority: z.enum(["low", "medium", "high"]).optional().describe("Priority level"),
|
|
304
312
|
recurrence_type: z
|
|
305
313
|
.enum(["daily", "weekly", "monthly", "yearly"])
|
|
@@ -311,7 +319,14 @@ server.registerTool("create_task", {
|
|
|
311
319
|
.positive()
|
|
312
320
|
.optional()
|
|
313
321
|
.describe("Recurrence interval (e.g., every N days)"),
|
|
314
|
-
|
|
322
|
+
contact_ids: z
|
|
323
|
+
.array(z.string().uuid())
|
|
324
|
+
.optional()
|
|
325
|
+
.describe("Array of contact UUIDs to associate"),
|
|
326
|
+
tag_ids: z
|
|
327
|
+
.array(z.string().uuid())
|
|
328
|
+
.optional()
|
|
329
|
+
.describe("Array of tag UUIDs to link"),
|
|
315
330
|
},
|
|
316
331
|
annotations: { title: "Create task", destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
317
332
|
}, async (params) => {
|
|
@@ -325,10 +340,18 @@ server.registerTool("update_task", {
|
|
|
325
340
|
description: z.string().optional().describe("Task description"),
|
|
326
341
|
date: z.string().optional().describe("Due date (YYYY-MM-DD)"),
|
|
327
342
|
date_type: z
|
|
328
|
-
.enum(["specific", "
|
|
343
|
+
.enum(["specific", "asap", "one_day"])
|
|
329
344
|
.optional()
|
|
330
|
-
.describe("Date type"),
|
|
345
|
+
.describe("Date type: specific (has a due date), asap (do as soon as possible), one_day (someday/no rush)"),
|
|
331
346
|
priority: z.enum(["low", "medium", "high"]).optional().describe("Priority level"),
|
|
347
|
+
contact_ids: z
|
|
348
|
+
.array(z.string().uuid())
|
|
349
|
+
.optional()
|
|
350
|
+
.describe("Replace associated contacts"),
|
|
351
|
+
tag_ids: z
|
|
352
|
+
.array(z.string().uuid())
|
|
353
|
+
.optional()
|
|
354
|
+
.describe("Replace associated tags"),
|
|
332
355
|
},
|
|
333
356
|
annotations: { title: "Update task", destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
334
357
|
}, async ({ id, ...body }) => {
|
|
@@ -367,9 +390,9 @@ server.registerTool("snooze_task", {
|
|
|
367
390
|
id: z.string().uuid().describe("Task UUID"),
|
|
368
391
|
date: z.string().describe("New date (YYYY-MM-DD)"),
|
|
369
392
|
date_type: z
|
|
370
|
-
.enum(["specific", "
|
|
393
|
+
.enum(["specific", "asap", "one_day"])
|
|
371
394
|
.optional()
|
|
372
|
-
.describe("New date type (default
|
|
395
|
+
.describe("New date type: specific (has a due date, default), asap (do as soon as possible), one_day (someday/no rush)"),
|
|
373
396
|
},
|
|
374
397
|
annotations: { title: "Snooze task", destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
375
398
|
}, async ({ id, ...body }) => {
|
|
@@ -391,7 +414,7 @@ server.registerTool("list_notes", {
|
|
|
391
414
|
return toContent(await fetchApi(`/notes${qs({ pinned, archived, limit, offset })}`));
|
|
392
415
|
});
|
|
393
416
|
server.registerTool("create_note", {
|
|
394
|
-
description: "
|
|
417
|
+
description: "A NOTE is a durable text document with no date. Use it for reference material, ideas, checklists, or anything to persist.\n\nCreate a new quick note. Content supports #tag# and [[tag]] for automatic tag linking.",
|
|
395
418
|
inputSchema: {
|
|
396
419
|
content: z.string().describe("Note content (supports #tag# and [[tag]])"),
|
|
397
420
|
is_pinned: z.boolean().optional().describe("Pin the note (default: false)"),
|
|
@@ -409,6 +432,14 @@ server.registerTool("update_note", {
|
|
|
409
432
|
inputSchema: {
|
|
410
433
|
id: z.string().uuid().describe("Note UUID"),
|
|
411
434
|
content: z.string().optional().describe("Updated content"),
|
|
435
|
+
contact_ids: z
|
|
436
|
+
.array(z.string().uuid())
|
|
437
|
+
.optional()
|
|
438
|
+
.describe("Replace associated contacts"),
|
|
439
|
+
tag_ids: z
|
|
440
|
+
.array(z.string().uuid())
|
|
441
|
+
.optional()
|
|
442
|
+
.describe("Replace associated tags"),
|
|
412
443
|
},
|
|
413
444
|
annotations: { title: "Update note", destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
414
445
|
}, async ({ id, ...body }) => {
|
|
@@ -535,6 +566,166 @@ server.registerTool("unlink_tag", {
|
|
|
535
566
|
return toContent(await fetchApi(`/tags/${id}/unlink`, "POST", body));
|
|
536
567
|
});
|
|
537
568
|
// ===========================================================================
|
|
569
|
+
// CONTACT LINKS (link/unlink contacts to notes, entries, tasks)
|
|
570
|
+
// ===========================================================================
|
|
571
|
+
server.registerTool("link_note_contact", {
|
|
572
|
+
description: "Link an existing contact to a note.",
|
|
573
|
+
inputSchema: {
|
|
574
|
+
note_id: z.string().uuid().describe("Note UUID"),
|
|
575
|
+
contact_id: z.string().uuid().describe("Contact UUID"),
|
|
576
|
+
},
|
|
577
|
+
annotations: { title: "Link note contact", destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
578
|
+
}, async ({ note_id, contact_id }) => {
|
|
579
|
+
return toContent(await fetchApi(`/notes/${note_id}/contacts/${contact_id}`, "POST"));
|
|
580
|
+
});
|
|
581
|
+
server.registerTool("unlink_note_contact", {
|
|
582
|
+
description: "Remove the link between a contact and a note.",
|
|
583
|
+
inputSchema: {
|
|
584
|
+
note_id: z.string().uuid().describe("Note UUID"),
|
|
585
|
+
contact_id: z.string().uuid().describe("Contact UUID"),
|
|
586
|
+
},
|
|
587
|
+
annotations: { title: "Unlink note contact", destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
588
|
+
}, async ({ note_id, contact_id }) => {
|
|
589
|
+
return toContent(await fetchApi(`/notes/${note_id}/contacts/${contact_id}`, "DELETE"));
|
|
590
|
+
});
|
|
591
|
+
server.registerTool("link_entry_contact", {
|
|
592
|
+
description: "Link an existing contact to an entry.",
|
|
593
|
+
inputSchema: {
|
|
594
|
+
entry_id: z.string().uuid().describe("Entry UUID"),
|
|
595
|
+
contact_id: z.string().uuid().describe("Contact UUID"),
|
|
596
|
+
},
|
|
597
|
+
annotations: { title: "Link entry contact", destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
598
|
+
}, async ({ entry_id, contact_id }) => {
|
|
599
|
+
return toContent(await fetchApi(`/entries/${entry_id}/contacts/${contact_id}`, "POST"));
|
|
600
|
+
});
|
|
601
|
+
server.registerTool("unlink_entry_contact", {
|
|
602
|
+
description: "Remove the link between a contact and an entry.",
|
|
603
|
+
inputSchema: {
|
|
604
|
+
entry_id: z.string().uuid().describe("Entry UUID"),
|
|
605
|
+
contact_id: z.string().uuid().describe("Contact UUID"),
|
|
606
|
+
},
|
|
607
|
+
annotations: { title: "Unlink entry contact", destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
608
|
+
}, async ({ entry_id, contact_id }) => {
|
|
609
|
+
return toContent(await fetchApi(`/entries/${entry_id}/contacts/${contact_id}`, "DELETE"));
|
|
610
|
+
});
|
|
611
|
+
server.registerTool("link_task_contact", {
|
|
612
|
+
description: "Link an existing contact to a task.",
|
|
613
|
+
inputSchema: {
|
|
614
|
+
task_id: z.string().uuid().describe("Task UUID"),
|
|
615
|
+
contact_id: z.string().uuid().describe("Contact UUID"),
|
|
616
|
+
},
|
|
617
|
+
annotations: { title: "Link task contact", destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
618
|
+
}, async ({ task_id, contact_id }) => {
|
|
619
|
+
return toContent(await fetchApi(`/tasks/${task_id}/contacts/${contact_id}`, "POST"));
|
|
620
|
+
});
|
|
621
|
+
server.registerTool("unlink_task_contact", {
|
|
622
|
+
description: "Remove the link between a contact and a task.",
|
|
623
|
+
inputSchema: {
|
|
624
|
+
task_id: z.string().uuid().describe("Task UUID"),
|
|
625
|
+
contact_id: z.string().uuid().describe("Contact UUID"),
|
|
626
|
+
},
|
|
627
|
+
annotations: { title: "Unlink task contact", destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
628
|
+
}, async ({ task_id, contact_id }) => {
|
|
629
|
+
return toContent(await fetchApi(`/tasks/${task_id}/contacts/${contact_id}`, "DELETE"));
|
|
630
|
+
});
|
|
631
|
+
server.registerTool("get_tag", {
|
|
632
|
+
description: "Get a single tag by ID with all its properties (name, description, color, icon, view mode, favorite status).",
|
|
633
|
+
inputSchema: {
|
|
634
|
+
id: z.string().uuid().describe("Tag UUID"),
|
|
635
|
+
},
|
|
636
|
+
annotations: { title: "Get tag", readOnlyHint: true, openWorldHint: false },
|
|
637
|
+
}, async ({ id }) => {
|
|
638
|
+
return toContent(await fetchApi(`/tags/${id}`));
|
|
639
|
+
});
|
|
640
|
+
server.registerTool("create_tag", {
|
|
641
|
+
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.",
|
|
642
|
+
inputSchema: {
|
|
643
|
+
name: z.string().describe("Tag name"),
|
|
644
|
+
description: z.string().optional().describe("Tag description"),
|
|
645
|
+
},
|
|
646
|
+
annotations: { title: "Create tag", destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
647
|
+
}, async (body) => {
|
|
648
|
+
return toContent(await fetchApi("/tags", "POST", body));
|
|
649
|
+
});
|
|
650
|
+
server.registerTool("update_tag", {
|
|
651
|
+
description: "Update an existing tag. Only send the fields you want to change. Supports name, description, color, icon, view mode, and favorite status.",
|
|
652
|
+
inputSchema: {
|
|
653
|
+
id: z.string().uuid().describe("Tag UUID"),
|
|
654
|
+
name: z.string().optional().describe("Tag name"),
|
|
655
|
+
description: z.string().optional().describe("Tag description"),
|
|
656
|
+
color: z.string().optional().describe("Tag color (e.g. 'blue', 'red', 'green')"),
|
|
657
|
+
icon: z.string().optional().describe("Tag icon (emoji or icon name)"),
|
|
658
|
+
view_mode: z.string().optional().describe("View mode for the tag page"),
|
|
659
|
+
is_favorite: z.boolean().optional().describe("Whether the tag is a favorite"),
|
|
660
|
+
},
|
|
661
|
+
annotations: { title: "Update tag", destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
662
|
+
}, async ({ id, ...body }) => {
|
|
663
|
+
return toContent(await fetchApi(`/tags/${id}`, "PATCH", body));
|
|
664
|
+
});
|
|
665
|
+
server.registerTool("delete_tag", {
|
|
666
|
+
description: "Permanently delete a tag and all its links to contacts, entries, tasks, notes, and companies.",
|
|
667
|
+
inputSchema: {
|
|
668
|
+
id: z.string().uuid().describe("Tag UUID"),
|
|
669
|
+
},
|
|
670
|
+
annotations: { title: "Delete tag", destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
671
|
+
}, async ({ id }) => {
|
|
672
|
+
return toContent(await fetchApi(`/tags/${id}`, "DELETE"));
|
|
673
|
+
});
|
|
674
|
+
// ===========================================================================
|
|
675
|
+
// TASK HEADERS (Sections)
|
|
676
|
+
// ===========================================================================
|
|
677
|
+
server.registerTool("list_task_headers", {
|
|
678
|
+
description: "List all task headers (section separators used to group tasks on tag pages and day views).",
|
|
679
|
+
inputSchema: {
|
|
680
|
+
limit: z.number().int().positive().optional().describe("Max results (default 50)"),
|
|
681
|
+
offset: z.number().int().nonnegative().optional().describe("Pagination offset"),
|
|
682
|
+
},
|
|
683
|
+
annotations: { title: "List task headers", readOnlyHint: true, openWorldHint: false },
|
|
684
|
+
}, async ({ limit, offset }) => {
|
|
685
|
+
return toContent(await fetchApi(`/task-headers${qs({ limit, offset })}`));
|
|
686
|
+
});
|
|
687
|
+
server.registerTool("get_task_header", {
|
|
688
|
+
description: "Get a single task header by ID.",
|
|
689
|
+
inputSchema: {
|
|
690
|
+
id: z.string().uuid().describe("Task header UUID"),
|
|
691
|
+
},
|
|
692
|
+
annotations: { title: "Get task header", readOnlyHint: true, openWorldHint: false },
|
|
693
|
+
}, async ({ id }) => {
|
|
694
|
+
return toContent(await fetchApi(`/task-headers/${id}`));
|
|
695
|
+
});
|
|
696
|
+
server.registerTool("create_task_header", {
|
|
697
|
+
description: "Create a new task header (section separator). Add it to a tag's items_order to position it.",
|
|
698
|
+
inputSchema: {
|
|
699
|
+
name: z.string().describe("Header name"),
|
|
700
|
+
description: z.string().optional().describe("Optional description below the header"),
|
|
701
|
+
collapsed: z.boolean().optional().describe("Whether the section is collapsed (default false)"),
|
|
702
|
+
},
|
|
703
|
+
annotations: { title: "Create task header", destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
704
|
+
}, async (body) => {
|
|
705
|
+
return toContent(await fetchApi("/task-headers", "POST", body));
|
|
706
|
+
});
|
|
707
|
+
server.registerTool("update_task_header", {
|
|
708
|
+
description: "Update a task header. Only send the fields you want to change.",
|
|
709
|
+
inputSchema: {
|
|
710
|
+
id: z.string().uuid().describe("Task header UUID"),
|
|
711
|
+
name: z.string().optional().describe("Header name"),
|
|
712
|
+
description: z.string().optional().describe("Description below the header"),
|
|
713
|
+
collapsed: z.boolean().optional().describe("Whether the section is collapsed"),
|
|
714
|
+
},
|
|
715
|
+
annotations: { title: "Update task header", destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
716
|
+
}, async ({ id, ...body }) => {
|
|
717
|
+
return toContent(await fetchApi(`/task-headers/${id}`, "PATCH", body));
|
|
718
|
+
});
|
|
719
|
+
server.registerTool("delete_task_header", {
|
|
720
|
+
description: "Permanently delete a task header.",
|
|
721
|
+
inputSchema: {
|
|
722
|
+
id: z.string().uuid().describe("Task header UUID"),
|
|
723
|
+
},
|
|
724
|
+
annotations: { title: "Delete task header", destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
725
|
+
}, async ({ id }) => {
|
|
726
|
+
return toContent(await fetchApi(`/task-headers/${id}`, "DELETE"));
|
|
727
|
+
});
|
|
728
|
+
// ===========================================================================
|
|
538
729
|
// CONTACT TIMELINE
|
|
539
730
|
// ===========================================================================
|
|
540
731
|
server.registerTool("get_contact_timeline", {
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keepsake-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "MCP server for Keepsake personal CRM — connect your AI agent to your contacts, tasks, notes, and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"keepsake-mcp": "
|
|
7
|
+
"keepsake-mcp": "build/index.js"
|
|
8
8
|
},
|
|
9
9
|
"main": "./build/index.js",
|
|
10
10
|
"scripts": {
|