keepsake-mcp 1.14.2 → 1.15.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 CHANGED
@@ -114,7 +114,7 @@ rather than in the chat, which disappears.
114
114
  |--------|-----------|-------------|
115
115
  | `review_note` | `note_id` | Act as the editor of a note: read it, judge form and substance, leave anchored remarks in the margin, never rewrite the text |
116
116
 
117
- ## Available tools (83)
117
+ ## Available tools (84)
118
118
 
119
119
  ### Contacts
120
120
  | Tool | Description |
@@ -165,10 +165,11 @@ rather than in the chat, which disappears.
165
165
  ### QuickNotes
166
166
  | Tool | Description |
167
167
  |------|-------------|
168
- | `list_notes` | List notes — filter by pinned/archived, day, company, page (`tag_id`) |
168
+ | `list_notes` | List notes — filter by pinned/archived, day, company, page (`tag_id`), publication-flow stage (`status`) |
169
+ | `list_note_statuses` | The stages of the user's publication flow (Idea → In progress → To review → Ready → Published by default — the user can rename, add or remove them) |
169
170
  | `get_note` | Get one note by ID with its tags, contacts, tasks and linked notes |
170
- | `create_note` | Create a note — supports `#tag#` and `[[tag]]` syntax |
171
- | `update_note` | Update note content |
171
+ | `create_note` | Create a note — supports `#tag#` and `[[tag]]` syntax; `status` puts it straight into the publication flow |
172
+ | `update_note` | Update note content, links, days, or its publication-flow stage (`status`) |
172
173
  | `delete_note` | Soft-delete (or permanent) |
173
174
  | `pin_note` | Pin as a post-it (short reference always at hand) |
174
175
  | `archive_note` | Archive a note |
package/build/tools.js CHANGED
@@ -97,7 +97,8 @@ function withNoteHint(result) {
97
97
  if (count > 0) {
98
98
  hints.push(`This note carries ${count} marginalia — read them with list_note_comments before adding your own, so you do not repeat what is already said.`);
99
99
  }
100
- if (note.workflow_status === "draft" || note.workflow_status === "review") {
100
+ const status = note.status;
101
+ if (status && (status.category === "unstarted" || status.category === "started")) {
101
102
  hints.push("The user is still working on this text: if they ask you to review it, put your remarks in the margin (create_note_comment, anchored with a verbatim quote) rather than in the chat, and do not rewrite the note itself.");
102
103
  }
103
104
  if (!hints.length)
@@ -546,8 +547,12 @@ export function registerAllTools(server, fetchApi) {
546
547
  // QUICK NOTES
547
548
  // ===========================================================================
548
549
  server.registerTool("list_notes", {
549
- description: "List notes. QuickNotes (inbox, not yet archived) and Notes (archived, permanent). Filter by pinned or archived status, by tag (page) with `tag_id`, or by the day(s) a note is linked to (`date`, or `date_from`/`date_to`) — e.g. \"what did I note for tomorrow?\". Each note carries `dates`, the days it is linked to.",
550
+ description: "List notes. QuickNotes (inbox, not yet archived) and Notes (archived, permanent). Filter by pinned or archived status, by tag (page) with `tag_id`, by the day(s) a note is linked to (`date`, or `date_from`/`date_to`) — e.g. \"what did I note for tomorrow?\" —, or by publication-flow stage with `status` — e.g. \"which notes are waiting for review?\". Each note carries `dates` (the days it is linked to) and `status` (its stage in the publication flow, or null).",
550
551
  inputSchema: {
552
+ status: z
553
+ .string()
554
+ .optional()
555
+ .describe("Publication-flow stage: a stage name, key or id from list_note_statuses (e.g. \"review\"), \"publication\" (every note in the flow that is not published yet), or \"none\" (notes outside the flow)"),
551
556
  pinned: z.boolean().optional().describe("Filter pinned notes only"),
552
557
  archived: z.boolean().optional().describe("Filter by status: true = Notes (archived/permanent), false = QuickNotes (inbox)"),
553
558
  date: z.string().optional().describe("Only notes linked to this day (YYYY-MM-DD)"),
@@ -559,8 +564,15 @@ export function registerAllTools(server, fetchApi) {
559
564
  offset: z.number().int().nonnegative().optional().describe("Pagination offset"),
560
565
  },
561
566
  annotations: { title: "List notes", readOnlyHint: true, openWorldHint: false },
562
- }, async ({ pinned, archived, date, date_from, date_to, company_id, tag_id, limit, offset }) => {
563
- return toContent(await fetchApi(`/notes${qs({ pinned, archived, date, date_from, date_to, company_id, tag_id, limit, offset })}`));
567
+ }, async ({ status, pinned, archived, date, date_from, date_to, company_id, tag_id, limit, offset }) => {
568
+ return toContent(await fetchApi(`/notes${qs({ status, pinned, archived, date, date_from, date_to, company_id, tag_id, limit, offset })}`));
569
+ });
570
+ server.registerTool("list_note_statuses", {
571
+ description: "List the stages of the user's publication flow, in order (by default: Idea → In progress → To review → Ready → Published). A note enters this flow when the user may publish it — most notes never do and have no stage. Stages are the user's own: they can rename, recolor, add or remove them, so read this list before setting a stage with update_note or create_note. `category` is fixed: unstarted, started, or published (exactly one stage). Names are in the user's app language.",
572
+ inputSchema: {},
573
+ annotations: { title: "List note statuses", readOnlyHint: true, openWorldHint: false },
574
+ }, async () => {
575
+ return toContent(await fetchApi("/note-statuses"));
564
576
  });
565
577
  server.registerTool("get_note", {
566
578
  description: "Get a single note by ID, including its tags, linked contacts, linked tasks, linked notes and the days it is linked to (`dates`). Use this when the user points you at one specific note (e.g. gives you its URL — the UUID is the last path segment) instead of listing everything.",
@@ -588,13 +600,17 @@ export function registerAllTools(server, fetchApi) {
588
600
  .array(z.string())
589
601
  .optional()
590
602
  .describe("Days to link the note to (YYYY-MM-DD each). The note appears in each day's view."),
603
+ status: z
604
+ .string()
605
+ .optional()
606
+ .describe("Put the note straight into the publication flow at this stage (name, key or id from list_note_statuses). The note is then created as a permanent Note, not an Inbox QuickNote — like \"Keep to publish\" in the app. Only when the user says they may publish it."),
591
607
  },
592
608
  annotations: { title: "Create note", destructiveHint: false, idempotentHint: false, openWorldHint: false },
593
609
  }, async (params) => {
594
610
  return toContent(await fetchApi("/notes", "POST", params));
595
611
  });
596
612
  server.registerTool("update_note", {
597
- description: "Update an existing QuickNote or Note. `dates` REPLACES the full set of days the note is linked to — the way to move a note to another day (\"not done, push it to tomorrow\"). To add or remove a single day without touching the others, prefer link_note_date / unlink_note_date.",
613
+ description: "Update an existing QuickNote or Note. `dates` REPLACES the full set of days the note is linked to — the way to move a note to another day (\"not done, push it to tomorrow\"). To add or remove a single day without touching the others, prefer link_note_date / unlink_note_date. `status` moves the note along the publication flow (\"this one is ready\"); setting a stage on an Inbox QuickNote also keeps it as a Note.",
598
614
  inputSchema: {
599
615
  id: z.string().uuid().describe("Note UUID"),
600
616
  content: z.string().optional().describe("Updated content"),
@@ -614,6 +630,11 @@ export function registerAllTools(server, fetchApi) {
614
630
  .array(z.string())
615
631
  .optional()
616
632
  .describe("Replace the days the note is linked to (YYYY-MM-DD each). Empty array = unlink from every day."),
633
+ status: z
634
+ .string()
635
+ .nullable()
636
+ .optional()
637
+ .describe("Publication-flow stage: name, key or id from list_note_statuses. null takes the note out of the flow."),
617
638
  },
618
639
  annotations: { title: "Update note", destructiveHint: false, idempotentHint: true, openWorldHint: false },
619
640
  }, async ({ id, ...body }) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "keepsake-mcp",
3
- "version": "1.14.2",
4
- "description": "MCP server for Keepsake personal CRM \u2014 connect your AI agent to your contacts, tasks, notes, and more",
3
+ "version": "1.15.0",
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
7
  "keepsake-mcp": "build/index.js"
@@ -31,11 +31,11 @@
31
31
  "license": "MIT",
32
32
  "mcpName": "io.github.nicolascroce/keepsake",
33
33
  "dependencies": {
34
- "@modelcontextprotocol/sdk": "^1.12.1",
35
- "zod": "^3.24.4"
34
+ "@modelcontextprotocol/sdk": "^1.32.1",
35
+ "zod": "^4.6.5"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^22.0.0",
39
- "typescript": "^5.8.0"
39
+ "typescript": "^7.0.2"
40
40
  }
41
41
  }