@davidwoods/skylight-mcp 1.7.0 → 1.8.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.
Files changed (57) hide show
  1. package/LICENSE +22 -22
  2. package/README.md +633 -584
  3. package/THIRD_PARTY_NOTICES.md +38 -38
  4. package/dist/api/credential-store.d.ts.map +1 -1
  5. package/dist/api/credential-store.js +2 -1
  6. package/dist/api/credential-store.js.map +1 -1
  7. package/dist/api/endpoints/categories.d.ts +6 -6
  8. package/dist/api/endpoints/categories.d.ts.map +1 -1
  9. package/dist/api/endpoints/categories.js +6 -13
  10. package/dist/api/endpoints/categories.js.map +1 -1
  11. package/dist/api/endpoints/chores.d.ts +8 -0
  12. package/dist/api/endpoints/chores.d.ts.map +1 -1
  13. package/dist/api/endpoints/chores.js +9 -0
  14. package/dist/api/endpoints/chores.js.map +1 -1
  15. package/dist/api/endpoints/taskbox.d.ts +8 -0
  16. package/dist/api/endpoints/taskbox.d.ts.map +1 -1
  17. package/dist/api/endpoints/taskbox.js +17 -0
  18. package/dist/api/endpoints/taskbox.js.map +1 -1
  19. package/dist/api/types.d.ts +14 -0
  20. package/dist/api/types.d.ts.map +1 -1
  21. package/dist/cli.d.ts.map +1 -1
  22. package/dist/cli.js +9 -8
  23. package/dist/cli.js.map +1 -1
  24. package/dist/config.js +23 -23
  25. package/dist/doctor.d.ts +10 -0
  26. package/dist/doctor.d.ts.map +1 -1
  27. package/dist/doctor.js +23 -7
  28. package/dist/doctor.js.map +1 -1
  29. package/dist/tools/calendar.d.ts +5 -0
  30. package/dist/tools/calendar.d.ts.map +1 -1
  31. package/dist/tools/calendar.js +107 -71
  32. package/dist/tools/calendar.js.map +1 -1
  33. package/dist/tools/chores.d.ts.map +1 -1
  34. package/dist/tools/chores.js +53 -81
  35. package/dist/tools/chores.js.map +1 -1
  36. package/dist/tools/family.js +17 -17
  37. package/dist/tools/lists.js +85 -85
  38. package/dist/tools/meals.js +102 -102
  39. package/dist/tools/messages.d.ts.map +1 -1
  40. package/dist/tools/messages.js +37 -49
  41. package/dist/tools/messages.js.map +1 -1
  42. package/dist/tools/misc.js +14 -14
  43. package/dist/tools/photos.js +6 -6
  44. package/dist/tools/planning.d.ts.map +1 -1
  45. package/dist/tools/planning.js +24 -23
  46. package/dist/tools/planning.js.map +1 -1
  47. package/dist/tools/registry.d.ts.map +1 -1
  48. package/dist/tools/registry.js +1 -0
  49. package/dist/tools/registry.js.map +1 -1
  50. package/dist/tools/rewards.d.ts.map +1 -1
  51. package/dist/tools/rewards.js +74 -90
  52. package/dist/tools/rewards.js.map +1 -1
  53. package/dist/tools/tasks.d.ts.map +1 -1
  54. package/dist/tools/tasks.js +45 -10
  55. package/dist/tools/tasks.js.map +1 -1
  56. package/dist/utils/errors.js +16 -16
  57. package/package.json +78 -78
@@ -27,9 +27,9 @@ async function resolveListId(listId, listName, defaultToGrocery = false) {
27
27
  }
28
28
  export function registerListTools(server) {
29
29
  // get_lists tool
30
- server.tool("get_lists", `Get all lists from Skylight (grocery lists, to-do lists, etc.).
31
-
32
- Use this to see what lists are available before adding items.
30
+ server.tool("get_lists", `Get all lists from Skylight (grocery lists, to-do lists, etc.).
31
+
32
+ Use this to see what lists are available before adding items.
33
33
  Returns list names, types (shopping/to_do), and item counts.`, {}, async () => {
34
34
  try {
35
35
  const lists = await getLists();
@@ -80,13 +80,13 @@ Returns list names, types (shopping/to_do), and item counts.`, {}, async () => {
80
80
  }
81
81
  });
82
82
  // get_list_items tool
83
- server.tool("get_list_items", `Get items from a specific Skylight list.
84
-
85
- Use this to answer:
86
- - "What's on the grocery list?"
87
- - "Show me my to-do list"
88
- - "What items are on [list name]?"
89
-
83
+ server.tool("get_list_items", `Get items from a specific Skylight list.
84
+
85
+ Use this to answer:
86
+ - "What's on the grocery list?"
87
+ - "Show me my to-do list"
88
+ - "What items are on [list name]?"
89
+
90
90
  Returns items organized by section with their completion status.`, {
91
91
  listName: z
92
92
  .string()
@@ -219,17 +219,17 @@ Returns items organized by section with their completion status.`, {
219
219
  }
220
220
  });
221
221
  // create_list tool
222
- server.tool("create_list", `Create a new list in Skylight.
223
-
224
- Use this when:
225
- - Creating a new shopping/grocery list
226
- - Creating a new to-do list
227
-
228
- Parameters:
229
- - label (required): Name of the list (e.g., "Vacation Packing", "Weekly Groceries")
230
- - kind (required): "shopping" for grocery/shopping lists, "to_do" for task lists
231
- - color: Optional color for the list
232
-
222
+ server.tool("create_list", `Create a new list in Skylight.
223
+
224
+ Use this when:
225
+ - Creating a new shopping/grocery list
226
+ - Creating a new to-do list
227
+
228
+ Parameters:
229
+ - label (required): Name of the list (e.g., "Vacation Packing", "Weekly Groceries")
230
+ - kind (required): "shopping" for grocery/shopping lists, "to_do" for task lists
231
+ - color: Optional color for the list
232
+
233
233
  Returns: The created list details.`, {
234
234
  label: z.string().describe("Name of the list (e.g., 'Vacation Packing')"),
235
235
  kind: z.enum(["shopping", "to_do"]).describe("Type of list: 'shopping' or 'to_do'"),
@@ -254,19 +254,19 @@ Returns: The created list details.`, {
254
254
  }
255
255
  });
256
256
  // update_list tool
257
- server.tool("update_list", `Update an existing list's name or settings.
258
-
259
- Use this when:
260
- - Renaming a list
261
- - Changing a list's type or color
262
-
263
- Parameters:
264
- - listId: ID of the list to update (use get_lists to find IDs)
265
- - listName: Name of the list to update (alternative to listId)
266
- - label: New name for the list
267
- - kind: New type ("shopping" or "to_do")
268
- - color: New color for the list
269
-
257
+ server.tool("update_list", `Update an existing list's name or settings.
258
+
259
+ Use this when:
260
+ - Renaming a list
261
+ - Changing a list's type or color
262
+
263
+ Parameters:
264
+ - listId: ID of the list to update (use get_lists to find IDs)
265
+ - listName: Name of the list to update (alternative to listId)
266
+ - label: New name for the list
267
+ - kind: New type ("shopping" or "to_do")
268
+ - color: New color for the list
269
+
270
270
  Returns: The updated list details.`, {
271
271
  listId: z.string().optional().describe("ID of the list to update"),
272
272
  listName: z.string().optional().describe("Name of the list to update (alternative to listId)"),
@@ -307,16 +307,16 @@ Returns: The updated list details.`, {
307
307
  }
308
308
  });
309
309
  // delete_list tool
310
- server.tool("delete_list", `Delete a list from Skylight.
311
-
312
- Use this when:
313
- - Removing an old or unused list
314
- - Deleting a temporary list
315
-
316
- Parameters:
317
- - listId: ID of the list to delete (use get_lists to find IDs)
318
- - listName: Name of the list to delete (alternative to listId)
319
-
310
+ server.tool("delete_list", `Delete a list from Skylight.
311
+
312
+ Use this when:
313
+ - Removing an old or unused list
314
+ - Deleting a temporary list
315
+
316
+ Parameters:
317
+ - listId: ID of the list to delete (use get_lists to find IDs)
318
+ - listName: Name of the list to delete (alternative to listId)
319
+
320
320
  Note: This permanently deletes the list and all its items.`, {
321
321
  listId: z.string().optional().describe("ID of the list to delete"),
322
322
  listName: z.string().optional().describe("Name of the list to delete (alternative to listId)"),
@@ -347,23 +347,23 @@ Note: This permanently deletes the list and all its items.`, {
347
347
  }
348
348
  });
349
349
  // create_list_item tool
350
- server.tool("create_list_item", `Add an item to a Skylight list.
351
-
352
- Use this when:
353
- - Adding something to the grocery list: "Add milk to the shopping list"
354
- - Creating a to-do item: "Put 'call doctor' on my to-do list"
355
- - Adding items to any list
356
-
357
- Parameters:
358
- - label (required): The item text (e.g., "Milk", "Call doctor")
359
- - listId: ID of the list to add to
360
- - listName: Name of the list to add to (e.g., "Grocery List")
361
- - section: Category within the list (e.g., "Dairy", "Produce")
362
-
363
- If no list is specified, adds to the default grocery list.
364
-
365
- Returns: Confirmation of the added item.
366
-
350
+ server.tool("create_list_item", `Add an item to a Skylight list.
351
+
352
+ Use this when:
353
+ - Adding something to the grocery list: "Add milk to the shopping list"
354
+ - Creating a to-do item: "Put 'call doctor' on my to-do list"
355
+ - Adding items to any list
356
+
357
+ Parameters:
358
+ - label (required): The item text (e.g., "Milk", "Call doctor")
359
+ - listId: ID of the list to add to
360
+ - listName: Name of the list to add to (e.g., "Grocery List")
361
+ - section: Category within the list (e.g., "Dairy", "Produce")
362
+
363
+ If no list is specified, adds to the default grocery list.
364
+
365
+ Returns: Confirmation of the added item.
366
+
367
367
  Related: Use get_lists to see available lists and their IDs.`, {
368
368
  label: z.string().describe("The item text to add (e.g., 'Milk', 'Call doctor')"),
369
369
  listId: z.string().optional().describe("ID of the list to add to"),
@@ -397,20 +397,20 @@ Related: Use get_lists to see available lists and their IDs.`, {
397
397
  }
398
398
  });
399
399
  // update_list_item tool
400
- server.tool("update_list_item", `Update a list item (mark complete, rename, move to section).
401
-
402
- Use this when:
403
- - Marking an item as complete: "Check off milk from the list"
404
- - Renaming an item: "Change 'milk' to '2% milk'"
405
- - Moving an item to a different section
406
-
407
- Parameters:
408
- - itemId (required): ID of the item to update
409
- - listId (required): ID of the list containing the item
410
- - label: New text for the item
411
- - status: "completed" to check off, "pending" to uncheck
412
- - section: Move to a different section
413
-
400
+ server.tool("update_list_item", `Update a list item (mark complete, rename, move to section).
401
+
402
+ Use this when:
403
+ - Marking an item as complete: "Check off milk from the list"
404
+ - Renaming an item: "Change 'milk' to '2% milk'"
405
+ - Moving an item to a different section
406
+
407
+ Parameters:
408
+ - itemId (required): ID of the item to update
409
+ - listId (required): ID of the list containing the item
410
+ - label: New text for the item
411
+ - status: "completed" to check off, "pending" to uncheck
412
+ - section: Move to a different section
413
+
414
414
  Returns: The updated item details.`, {
415
415
  itemId: z.string().describe("ID of the item to update"),
416
416
  listId: z.string().describe("ID of the list containing the item"),
@@ -445,16 +445,16 @@ Returns: The updated item details.`, {
445
445
  }
446
446
  });
447
447
  // delete_list_item tool
448
- server.tool("delete_list_item", `Remove an item from a list.
449
-
450
- Use this when:
451
- - Removing an item that was added by mistake
452
- - Deleting an item instead of marking it complete
453
-
454
- Parameters:
455
- - itemId (required): ID of the item to delete
456
- - listId (required): ID of the list containing the item
457
-
448
+ server.tool("delete_list_item", `Remove an item from a list.
449
+
450
+ Use this when:
451
+ - Removing an item that was added by mistake
452
+ - Deleting an item instead of marking it complete
453
+
454
+ Parameters:
455
+ - itemId (required): ID of the item to delete
456
+ - listId (required): ID of the list containing the item
457
+
458
458
  Note: This permanently removes the item. Use update_list_item with status="completed" to check it off instead.`, {
459
459
  itemId: z.string().describe("ID of the item to delete"),
460
460
  listId: z.string().describe("ID of the list containing the item"),
@@ -6,12 +6,12 @@ import { formatErrorForMcp } from "../utils/errors.js";
6
6
  import { getConfig } from "../config.js";
7
7
  export function registerMealTools(server) {
8
8
  // get_meal_categories tool
9
- server.tool("get_meal_categories", `Get meal categories (Breakfast, Lunch, Dinner, etc.) - Plus subscription required.
10
-
11
- Use this when:
12
- - Finding category IDs for scheduling meals
13
- - Seeing what meal times are available
14
-
9
+ server.tool("get_meal_categories", `Get meal categories (Breakfast, Lunch, Dinner, etc.) - Plus subscription required.
10
+
11
+ Use this when:
12
+ - Finding category IDs for scheduling meals
13
+ - Seeing what meal times are available
14
+
15
15
  Returns: List of meal categories with IDs.`, {}, async () => {
16
16
  try {
17
17
  const categories = await getMealCategories();
@@ -35,12 +35,12 @@ Returns: List of meal categories with IDs.`, {}, async () => {
35
35
  }
36
36
  });
37
37
  // get_recipes tool
38
- server.tool("get_recipes", `Get all saved recipes - Plus subscription required.
39
-
40
- Use this when:
41
- - Browsing available recipes
42
- - Finding a recipe ID for meal planning
43
-
38
+ server.tool("get_recipes", `Get all saved recipes - Plus subscription required.
39
+
40
+ Use this when:
41
+ - Browsing available recipes
42
+ - Finding a recipe ID for meal planning
43
+
44
44
  Returns: List of recipes with their details.`, {}, async () => {
45
45
  try {
46
46
  const recipes = await getRecipes();
@@ -70,11 +70,11 @@ Returns: List of recipes with their details.`, {}, async () => {
70
70
  }
71
71
  });
72
72
  // get_recipe tool
73
- server.tool("get_recipe", `Get details for a specific recipe - Plus subscription required.
74
-
75
- Parameters:
76
- - recipeId (required): ID of the recipe
77
-
73
+ server.tool("get_recipe", `Get details for a specific recipe - Plus subscription required.
74
+
75
+ Parameters:
76
+ - recipeId (required): ID of the recipe
77
+
78
78
  Returns: Recipe details including description.`, {
79
79
  recipeId: z.string().describe("ID of the recipe"),
80
80
  }, async ({ recipeId }) => {
@@ -98,17 +98,17 @@ Returns: Recipe details including description.`, {
98
98
  }
99
99
  });
100
100
  // create_recipe tool
101
- server.tool("create_recipe", `Create a new recipe - Plus subscription required.
102
-
103
- Use this when:
104
- - Adding a new family recipe
105
- - Saving a meal for meal planning
106
-
107
- Parameters:
108
- - summary (required): Recipe name
109
- - description: Recipe details or instructions
110
- - mealCategoryId: Category ID (use get_meal_categories)
111
-
101
+ server.tool("create_recipe", `Create a new recipe - Plus subscription required.
102
+
103
+ Use this when:
104
+ - Adding a new family recipe
105
+ - Saving a meal for meal planning
106
+
107
+ Parameters:
108
+ - summary (required): Recipe name
109
+ - description: Recipe details or instructions
110
+ - mealCategoryId: Category ID (use get_meal_categories)
111
+
112
112
  Returns: The created recipe.`, {
113
113
  summary: z.string().describe("Recipe name (e.g., 'Spaghetti Bolognese')"),
114
114
  description: z.string().optional().describe("Recipe details or instructions"),
@@ -133,13 +133,13 @@ Returns: The created recipe.`, {
133
133
  }
134
134
  });
135
135
  // update_recipe tool
136
- server.tool("update_recipe", `Update an existing recipe - Plus subscription required.
137
-
138
- Parameters:
139
- - recipeId (required): ID of the recipe
140
- - summary: New name
141
- - description: New description
142
-
136
+ server.tool("update_recipe", `Update an existing recipe - Plus subscription required.
137
+
138
+ Parameters:
139
+ - recipeId (required): ID of the recipe
140
+ - summary: New name
141
+ - description: New description
142
+
143
143
  Returns: The updated recipe.`, {
144
144
  recipeId: z.string().describe("ID of the recipe to update"),
145
145
  summary: z.string().optional().describe("New recipe name"),
@@ -169,11 +169,11 @@ Returns: The updated recipe.`, {
169
169
  }
170
170
  });
171
171
  // delete_recipe tool
172
- server.tool("delete_recipe", `Delete a recipe - Plus subscription required.
173
-
174
- Parameters:
175
- - recipeId (required): ID of the recipe to delete
176
-
172
+ server.tool("delete_recipe", `Delete a recipe - Plus subscription required.
173
+
174
+ Parameters:
175
+ - recipeId (required): ID of the recipe to delete
176
+
177
177
  Note: This permanently removes the recipe.`, {
178
178
  recipeId: z.string().describe("ID of the recipe to delete"),
179
179
  }, async ({ recipeId }) => {
@@ -191,15 +191,15 @@ Note: This permanently removes the recipe.`, {
191
191
  }
192
192
  });
193
193
  // add_recipe_to_grocery_list tool
194
- server.tool("add_recipe_to_grocery_list", `Add a recipe's ingredients to the grocery list - Plus subscription required.
195
-
196
- Use this when:
197
- - Planning to make a recipe and need to buy ingredients
198
- - Adding meal ingredients to shopping list
199
-
200
- Parameters:
201
- - recipeId (required): ID of the recipe
202
-
194
+ server.tool("add_recipe_to_grocery_list", `Add a recipe's ingredients to the grocery list - Plus subscription required.
195
+
196
+ Use this when:
197
+ - Planning to make a recipe and need to buy ingredients
198
+ - Adding meal ingredients to shopping list
199
+
200
+ Parameters:
201
+ - recipeId (required): ID of the recipe
202
+
203
203
  Returns: Confirmation that ingredients were added.`, {
204
204
  recipeId: z.string().describe("ID of the recipe"),
205
205
  }, async ({ recipeId }) => {
@@ -222,16 +222,16 @@ Returns: Confirmation that ingredients were added.`, {
222
222
  }
223
223
  });
224
224
  // get_meal_sittings tool
225
- server.tool("get_meal_sittings", `Get scheduled meals for a date range - Plus subscription required.
226
-
227
- Use this when:
228
- - Viewing the meal plan for the week
229
- - Checking what's scheduled for dinner
230
-
231
- Parameters:
232
- - date: Start date (defaults to today)
233
- - dateEnd: End date (defaults to 7 days from start)
234
-
225
+ server.tool("get_meal_sittings", `Get scheduled meals for a date range - Plus subscription required.
226
+
227
+ Use this when:
228
+ - Viewing the meal plan for the week
229
+ - Checking what's scheduled for dinner
230
+
231
+ Parameters:
232
+ - date: Start date (defaults to today)
233
+ - dateEnd: End date (defaults to 7 days from start)
234
+
235
235
  Returns: List of scheduled meals.`, {
236
236
  date: z.string().optional().describe("Start date (YYYY-MM-DD or 'today')"),
237
237
  dateEnd: z.string().optional().describe("End date (YYYY-MM-DD)"),
@@ -291,20 +291,20 @@ Returns: List of scheduled meals.`, {
291
291
  }
292
292
  });
293
293
  // create_meal_sitting tool
294
- server.tool("create_meal_sitting", `Schedule a meal for a specific date - Plus subscription required.
295
-
296
- Use this when:
297
- - Planning meals for the week
298
- - Scheduling a recipe for dinner
299
-
300
- Parameters:
301
- - date (required): Date for the meal (YYYY-MM-DD)
302
- - mealCategoryId (required): Meal category ID (use get_meal_categories to find "Dinner" etc.)
303
- - summary: What the meal is, as free text, e.g. "Thai green curry". No recipe needed.
304
- - recipeId: A saved recipe to link instead of, or as well as, a summary.
305
-
306
- Give at least one of summary or recipeId.
307
-
294
+ server.tool("create_meal_sitting", `Schedule a meal for a specific date - Plus subscription required.
295
+
296
+ Use this when:
297
+ - Planning meals for the week
298
+ - Scheduling a recipe for dinner
299
+
300
+ Parameters:
301
+ - date (required): Date for the meal (YYYY-MM-DD)
302
+ - mealCategoryId (required): Meal category ID (use get_meal_categories to find "Dinner" etc.)
303
+ - summary: What the meal is, as free text, e.g. "Thai green curry". No recipe needed.
304
+ - recipeId: A saved recipe to link instead of, or as well as, a summary.
305
+
306
+ Give at least one of summary or recipeId.
307
+
308
308
  Returns: The created meal sitting.`, {
309
309
  date: z.string().describe("Date for the meal (YYYY-MM-DD or 'today', 'tomorrow')"),
310
310
  mealCategoryId: z.string().describe("Meal category ID (e.g., ID for 'Dinner')"),
@@ -344,23 +344,23 @@ Returns: The created meal sitting.`, {
344
344
  }
345
345
  });
346
346
  // update_meal_sitting tool
347
- server.tool("update_meal_sitting", `Edit or move a scheduled meal - Plus subscription required.
348
-
349
- Use this when:
350
- - Renaming a planned meal, or changing its note or category
351
- - Moving a meal to another day
352
-
353
- Parameters:
354
- - sittingId (required): from get_meal_sittings
355
- - date (required): the meal's CURRENT date (YYYY-MM-DD), also from get_meal_sittings
356
- - newDate: move the meal to this date
357
- - summary: new meal name
358
- - note: new note
359
- - mealCategoryId: move to another meal category (use get_meal_categories)
360
-
361
- Give at least one change. Only one-off meals can be edited here; repeating meals
362
- must be edited in the Skylight app.
363
-
347
+ server.tool("update_meal_sitting", `Edit or move a scheduled meal - Plus subscription required.
348
+
349
+ Use this when:
350
+ - Renaming a planned meal, or changing its note or category
351
+ - Moving a meal to another day
352
+
353
+ Parameters:
354
+ - sittingId (required): from get_meal_sittings
355
+ - date (required): the meal's CURRENT date (YYYY-MM-DD), also from get_meal_sittings
356
+ - newDate: move the meal to this date
357
+ - summary: new meal name
358
+ - note: new note
359
+ - mealCategoryId: move to another meal category (use get_meal_categories)
360
+
361
+ Give at least one change. Only one-off meals can be edited here; repeating meals
362
+ must be edited in the Skylight app.
363
+
364
364
  Returns: What changed.`, {
365
365
  sittingId: z.string().describe("Sitting ID from get_meal_sittings"),
366
366
  date: z.string().describe("The meal's current date (YYYY-MM-DD)"),
@@ -424,18 +424,18 @@ Returns: What changed.`, {
424
424
  }
425
425
  });
426
426
  // delete_meal_sitting tool
427
- server.tool("delete_meal_sitting", `Delete a scheduled meal - Plus subscription required. THIS CANNOT BE UNDONE.
428
-
429
- Use this when:
430
- - Removing a meal that was planned by mistake or is no longer happening
431
-
432
- Parameters:
433
- - sittingId (required): from get_meal_sittings
434
- - date (required): the meal's date (YYYY-MM-DD)
435
- - confirm: must be true to delete. Without it nothing is deleted and the meal that would be removed is shown.
436
-
437
- Only one-off meals can be deleted here; repeating meals must be handled in the Skylight app.
438
-
427
+ server.tool("delete_meal_sitting", `Delete a scheduled meal - Plus subscription required. THIS CANNOT BE UNDONE.
428
+
429
+ Use this when:
430
+ - Removing a meal that was planned by mistake or is no longer happening
431
+
432
+ Parameters:
433
+ - sittingId (required): from get_meal_sittings
434
+ - date (required): the meal's date (YYYY-MM-DD)
435
+ - confirm: must be true to delete. Without it nothing is deleted and the meal that would be removed is shown.
436
+
437
+ Only one-off meals can be deleted here; repeating meals must be handled in the Skylight app.
438
+
439
439
  Returns: Confirmation that Skylight removed the meal.`, {
440
440
  sittingId: z.string().describe("Sitting ID from get_meal_sittings"),
441
441
  date: z.string().describe("The meal's date (YYYY-MM-DD)"),
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/tools/messages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASzE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4L5D"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/tools/messages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASzE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA8K5D"}
@@ -1,18 +1,18 @@
1
1
  import { z } from "zod";
2
2
  import { assertNudgeId, createNudge, deleteNudge, findNudge, getNudges, updateNudge } from "../api/endpoints/messages.js";
3
- import { getCategories, findCategoryByName, resolveProfileIds } from "../api/endpoints/categories.js";
3
+ import { getCategories, resolveProfile, resolveProfileIds } from "../api/endpoints/categories.js";
4
4
  import { getConfig } from "../config.js";
5
5
  import { parseDate, getDateOffset } from "../utils/dates.js";
6
6
  import { dateInZone, formatDay, formatTime } from "../utils/schedule.js";
7
7
  import { formatErrorForMcp } from "../utils/errors.js";
8
8
  export function registerMessageTools(server) {
9
- server.tool("get_messages", `Get messages (Skylight calls them "nudges") shown on the family frame.
10
-
11
- Use this when:
12
- - Checking what has already been posted to the frame
13
- - Avoiding a duplicate before sending a reminder
14
-
15
- Returns: Messages grouped by day with time, text, recipients and sender.
9
+ server.tool("get_messages", `Get messages (Skylight calls them "nudges") shown on the family frame.
10
+
11
+ Use this when:
12
+ - Checking what has already been posted to the frame
13
+ - Avoiding a duplicate before sending a reminder
14
+
15
+ Returns: Messages grouped by day with time, text, recipients and sender.
16
16
  Defaults to the last 7 days through the next 7 days. Max 31 days.`, {
17
17
  date: z.string().optional().describe("Start date (YYYY-MM-DD or 'today'). Default: 7 days ago."),
18
18
  dateEnd: z.string().optional().describe("End date, inclusive. Default: 7 days ahead."),
@@ -51,17 +51,17 @@ Defaults to the last 7 days through the next 7 days. Max 31 days.`, {
51
51
  return { content: [{ type: "text", text: formatErrorForMcp(error) }], isError: true };
52
52
  }
53
53
  });
54
- server.tool("send_message", `Post a message to the family frame now, on all devices. Shown to everyone unless "to" names profiles.
55
-
56
- PERMANENT in practice: the Skylight app has no way to delete a message (delete_message
57
- is experimental), and it appears on the family frame for the whole
58
- household immediately.
59
-
60
- Two-step by design. Called without confirm it only previews and posts nothing.
61
- Show the user the exact text and get a clear yes, then call again with confirm: true.
62
- Never pass confirm: true on the first call, and never invent message text the user
63
- did not ask for. Read aloud is OFF by default; enable it only if the user asked.
64
-
54
+ server.tool("send_message", `Post a message to the family frame now, on all devices. Shown to everyone unless "to" names profiles.
55
+
56
+ PERMANENT in practice: the Skylight app has no way to delete a message (delete_message
57
+ is experimental), and it appears on the family frame for the whole
58
+ household immediately.
59
+
60
+ Two-step by design. Called without confirm it only previews and posts nothing.
61
+ Show the user the exact text and get a clear yes, then call again with confirm: true.
62
+ Never pass confirm: true on the first call, and never invent message text the user
63
+ did not ask for. Read aloud is OFF by default; enable it only if the user asked.
64
+
65
65
  Check get_messages first to avoid posting a duplicate.`, {
66
66
  body: z.string().min(1).max(300).describe("Message text, up to 300 characters."),
67
67
  readAloud: z.boolean().optional().describe("Speak the message on the frame when delivered. Default false."),
@@ -76,14 +76,8 @@ Check get_messages first to avoid posting a duplicate.`, {
76
76
  const recipients = to && to.length > 0 ? await resolveProfileIds(to) : undefined;
77
77
  const audience = recipients ? recipients.labels.join(", ") : "everyone";
78
78
  let senderCategoryId;
79
- if (from) {
80
- const match = await findCategoryByName(from);
81
- if (!match) {
82
- const known = (await getCategories()).map((c) => c.attributes.label).filter(Boolean).join(", ");
83
- throw new Error(`No profile called "${from}". Profiles: ${known}.`);
84
- }
85
- senderCategoryId = match.id;
86
- }
79
+ if (from)
80
+ senderCategoryId = (await resolveProfile(from)).id;
87
81
  const who = senderCategoryId ? ` from ${from}` : "";
88
82
  if (confirm !== true) {
89
83
  return {
@@ -105,11 +99,11 @@ Check get_messages first to avoid posting a duplicate.`, {
105
99
  return { content: [{ type: "text", text: formatErrorForMcp(error) }], isError: true };
106
100
  }
107
101
  });
108
- server.tool("update_message", `Edit the text (and optionally the sender) of a message already on the frame.
109
-
110
- Only messages sent to everyone on all devices and not repeating can be edited;
111
- anything else must be edited in the Skylight app. Delivery time and read-aloud
112
- setting are kept. The edit is visible on the frame immediately. Get the message
102
+ server.tool("update_message", `Edit the text (and optionally the sender) of a message already on the frame.
103
+
104
+ Only messages sent to everyone on all devices and not repeating can be edited;
105
+ anything else must be edited in the Skylight app. Delivery time and read-aloud
106
+ setting are kept. The edit is visible on the frame immediately. Get the message
113
107
  ID from get_messages (looks within 31 days either side of today).`, {
114
108
  messageId: z.string().describe("Numeric message ID from get_messages."),
115
109
  body: z.string().min(1).max(300).describe("New message text, up to 300 characters."),
@@ -125,14 +119,8 @@ ID from get_messages (looks within 31 days either side of today).`, {
125
119
  if (!existing)
126
120
  throw new Error(`No message with ID ${id} within 31 days of today.`);
127
121
  let senderCategoryId;
128
- if (from) {
129
- const match = await findCategoryByName(from);
130
- if (!match) {
131
- const known = (await getCategories()).map((c) => c.attributes.label).filter(Boolean).join(", ");
132
- throw new Error(`No profile called "${from}". Profiles: ${known}.`);
133
- }
134
- senderCategoryId = match.id;
135
- }
122
+ if (from)
123
+ senderCategoryId = (await resolveProfile(from)).id;
136
124
  await updateNudge({ existing, body: text, senderCategoryId, timeZone: timezone });
137
125
  return { content: [{ type: "text", text: `Updated message ${id}: "${text}"` }] };
138
126
  }
@@ -140,15 +128,15 @@ ID from get_messages (looks within 31 days either side of today).`, {
140
128
  return { content: [{ type: "text", text: formatErrorForMcp(error) }], isError: true };
141
129
  }
142
130
  });
143
- server.tool("delete_message", `EXPERIMENTAL: delete a message from the family frame.
144
-
145
- The Skylight app has no delete button for messages. This calls the delete endpoint
146
- that other Skylight clients use; it is unverified on this account until tested, so a
147
- failure is expected to be reported plainly, not retried.
148
-
149
- Two-step. Without confirm it only previews which message would go. Show the user the
150
- text and get a clear yes before calling with confirm: true. After deleting it reads the
151
- message list back and reports whether the message is really gone. Get the ID from
131
+ server.tool("delete_message", `EXPERIMENTAL: delete a message from the family frame.
132
+
133
+ The Skylight app has no delete button for messages. This calls the delete endpoint
134
+ that other Skylight clients use; it is unverified on this account until tested, so a
135
+ failure is expected to be reported plainly, not retried.
136
+
137
+ Two-step. Without confirm it only previews which message would go. Show the user the
138
+ text and get a clear yes before calling with confirm: true. After deleting it reads the
139
+ message list back and reports whether the message is really gone. Get the ID from
152
140
  get_messages (looks within 31 days either side of today).`, {
153
141
  messageId: z.string().describe("Numeric message ID from get_messages."),
154
142
  confirm: z.boolean().optional().describe("Must be true to actually delete. Default false: preview only."),