@sebamomann/plants-mcp 2.5.1 → 2.7.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/CHANGELOG.md +28 -0
- package/README.md +24 -6
- package/dist/index.js +5 -2
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,34 @@
|
|
|
3
3
|
Notable changes to `@sebamomann/plants-mcp`. Versioning is semver against the **tool surface** —
|
|
4
4
|
see the table in `AGENTS.md` for what counts as major, minor, and patch.
|
|
5
5
|
|
|
6
|
+
## 2.7.0 — 2026-09-24
|
|
7
|
+
|
|
8
|
+
**Nudges.** 77 tools registered overall (28 read, 31 write, 18 admin). A **minor** bump — one new
|
|
9
|
+
tool, nothing renamed or removed:
|
|
10
|
+
|
|
11
|
+
- `list_nudges` — the caller's whole ranked "gaps worth mentioning" queue: stale or missing photos,
|
|
12
|
+
plants without a type or location, long-unresolved health entries, and app features never tried.
|
|
13
|
+
Each entry is `{ kind, variant, count, href }`; the app itself only ever shows the first one, but
|
|
14
|
+
an assistant sees the whole queue. Read-only — snoozing a nudge stays an in-app action, deliberately;
|
|
15
|
+
see `docs/nudges.md`'s "MCP" section and `docs/mcp-server.md`'s roadmap for why `snooze_nudge` isn't
|
|
16
|
+
part of this.
|
|
17
|
+
|
|
18
|
+
## 2.6.0 — 2026-09-24
|
|
19
|
+
|
|
20
|
+
**Snooze until a date.** Tool count unchanged (76 overall). A **minor** bump — one new optional
|
|
21
|
+
argument, nothing renamed or removed: `snooze_care` takes `snoozedUntil` (`YYYY-MM-DD`, after today,
|
|
22
|
+
within a year) as an alternative to `durationDays`, which is now optional. Exactly one of the two is
|
|
23
|
+
required; passing both or neither is a 400. The app's own snooze picker gained the same date field.
|
|
24
|
+
Behaviour change without a shape change: a `durationDays` preset now counts from today once
|
|
25
|
+
`originalDueAt` has passed (it used to count from `originalDueAt` regardless, so "3 days" on a
|
|
26
|
+
week-overdue plant landed four days in the past and deferred nothing).
|
|
27
|
+
|
|
28
|
+
## 2.5.2 — 2026-09-24
|
|
29
|
+
|
|
30
|
+
**Dependency refresh.** Tool count unchanged (76 overall). A **patch** bump — nothing renamed or
|
|
31
|
+
removed: `@modelcontextprotocol/sdk` 1.29 → 1.30 and `zod` 4.4 → 4.6; the workspace's tests run
|
|
32
|
+
on Vitest 5.
|
|
33
|
+
|
|
6
34
|
## 2.5.1 — 2026-09-20
|
|
7
35
|
|
|
8
36
|
**Richer admin dashboard status.** Tool count unchanged (76 overall). A **patch** bump — the
|
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ needs a running Sprig instance and an API key from it. Start at the
|
|
|
60
60
|
|
|
61
61
|
## What an assistant can do
|
|
62
62
|
|
|
63
|
-
**
|
|
63
|
+
**77 tools** over your collection: 28 that read it, 31 that write to it. **18 admin tools** are also
|
|
64
64
|
available — but only to a key created by an admin — for finding and understanding duplicates in the
|
|
65
65
|
shared global plant type catalog, restructuring its genus/species/cultivar tree, reading, directly
|
|
66
66
|
editing, merging, and reviewing proposals and duplicate candidates in that catalog, and (7 read-only,
|
|
@@ -68,8 +68,9 @@ editing, merging, and reviewing proposals and duplicate candidates in that catal
|
|
|
68
68
|
|
|
69
69
|
Reads cover plants, the shared global plant type catalog, watering and fertilization history, the
|
|
70
70
|
full care timeline, photo metadata, health entries, the derived care schedule, collection-wide
|
|
71
|
-
activity,
|
|
72
|
-
(including pots). Writes cover recording
|
|
71
|
+
activity, nudges (gaps in the collection worth mentioning), the wishlist, watched types,
|
|
72
|
+
notifications, vacation status, and the lookup catalogs (including pots). Writes cover recording
|
|
73
|
+
waterings, fertilizings, repottings, reservoir refills and
|
|
73
74
|
hydro events, snoozing a due date, health notes, dismissing care recommendations, editing a plant's
|
|
74
75
|
identity/care schedule/settings/lifecycle status, adding a plant (identified or not), propagating
|
|
75
76
|
one, merging plants together and reversing that merge, deleting a watering or fertilization event,
|
|
@@ -109,7 +110,7 @@ healthy server, not a hang — press Ctrl-C.
|
|
|
109
110
|
|
|
110
111
|
The scope lives on the API key, is enforced by the app, and cannot be widened from this side.
|
|
111
112
|
|
|
112
|
-
- **`read`** — on every key. Gates all
|
|
113
|
+
- **`read`** — on every key. Gates all 28 read tools.
|
|
113
114
|
- **`write`** — opt-in when you create the key. Gates the 31 write tools.
|
|
114
115
|
- **`admin`** — only offered when the key's creator is themselves an admin, and only while they
|
|
115
116
|
still are one (the app re-checks this on every admin-tool call, not just at key creation). Gates
|
|
@@ -247,6 +248,20 @@ vacation in **either** mode (`sitter` or `shift`), with just `{ id, mode, starts
|
|
|
247
248
|
isUpcoming, plantCount }` — no per-plant detail or schedule. Use `sitter_briefing` for the full
|
|
248
249
|
sitter sheet; use this one just to know whether a vacation (of either mode) exists at all.
|
|
249
250
|
|
|
251
|
+
### Nudges
|
|
252
|
+
|
|
253
|
+
| Tool | Endpoint | Arguments |
|
|
254
|
+
|---|---|---|
|
|
255
|
+
| `list_nudges` | `GET /api/v1/nudges` | none |
|
|
256
|
+
|
|
257
|
+
Gaps in the collection worth mentioning, distinct from due/overdue care: stale or missing photos,
|
|
258
|
+
plants without a type or location, long-unresolved health entries, and app features the caller has
|
|
259
|
+
never tried. Returns the caller's whole ranked queue as `{ kind, variant, count, href }[]` — the app
|
|
260
|
+
itself only ever shows the first entry, but an assistant benefits from seeing everything pending.
|
|
261
|
+
`count` is an aggregate number for that kind (`0` for a feature-discovery entry); `href` is the
|
|
262
|
+
in-app page to send the user to. An empty list means nothing is currently worth nudging about.
|
|
263
|
+
Read-only — snoozing a nudge stays an in-app action by design.
|
|
264
|
+
|
|
250
265
|
### Wishlist
|
|
251
266
|
|
|
252
267
|
| Tool | Endpoint | Arguments |
|
|
@@ -336,7 +351,7 @@ description starts with `WRITE:` so a model cannot mistake one for a read.
|
|
|
336
351
|
| `record_repotting` | `POST /api/v1/plants/:id/repotting` | `plantId`, `potId` **(both required)**, plus `potSizeId`, `soilId`, `pottedAt`, `hasDrainage`, `hasClimbingAid`, `notes` |
|
|
337
352
|
| `record_refill` | `POST /api/v1/plants/:id/refill` | `plantId` **(required)**, plus `fertilizerId`, `fertilizerPercent`, `refilledAt` |
|
|
338
353
|
| `record_hydro_event` | `POST /api/v1/plants/:id/hydro` | `plantId` **(required)**, plus `kind` (`topup`\|`change`), `waterLevel` (0–100), `fertilizerId`, `fertilizerPercent`, `recordedAt`, `notes` |
|
|
339
|
-
| `snooze_care` | `POST /api/v1/care/snooze` | `plantId`, `careType` (`water`\|`fert`), `durationDays` (1, 3, 7, or 14)
|
|
354
|
+
| `snooze_care` | `POST /api/v1/care/snooze` | `plantId`, `careType` (`water`\|`fert`), `originalDueAt` **(all required)**, exactly one of `durationDays` (1, 3, 7, or 14) or `snoozedUntil` (`YYYY-MM-DD`), `note` |
|
|
340
355
|
|
|
341
356
|
`record_watering` mirrors a one-click watering in the app: reservoir and hydro plants are recorded
|
|
342
357
|
as a refill / top-up automatically, and plants configured to fertilize with watering also get a
|
|
@@ -360,7 +375,10 @@ hydro-cultured plant.
|
|
|
360
375
|
|
|
361
376
|
`snooze_care` postpones a plant's next watering or fertilizing due date — the same picker the care
|
|
362
377
|
hub offers. `originalDueAt` is the due date being pushed out (`nextDue` from `list_due_care`/
|
|
363
|
-
`get_care_calendar` for that plant/careType), not today's date.
|
|
378
|
+
`get_care_calendar` for that plant/careType), not today's date. Pass exactly one of `durationDays`
|
|
379
|
+
(a preset counted from `originalDueAt`, or from today once that date has passed — so "3 days" on
|
|
380
|
+
an overdue plant means three days from now) or `snoozedUntil` (an explicit day: tomorrow at the
|
|
381
|
+
earliest, at most a year out).
|
|
364
382
|
|
|
365
383
|
### Plant edits
|
|
366
384
|
|
package/dist/index.js
CHANGED
|
@@ -121,6 +121,8 @@ server.tool("sitter_briefing", "Printable care sheet for the caller's current or
|
|
|
121
121
|
locationId: z.number().int().optional().describe("Only include plants in this location (id from list_locations). Omit for every plant on the vacation."),
|
|
122
122
|
}, async (args) => apiGet("/api/v1/vacation/sitter-briefing", args));
|
|
123
123
|
server.tool("get_vacation_status", "Whether the caller has a current or upcoming vacation, in either mode ('sitter' or 'shift') — unlike sitter_briefing, which only ever reports a 'sitter'-mode one. Returns { hasVacation: false } when none is running or scheduled, otherwise the vacation's id, mode, date range, whether it has started yet (isActive/isUpcoming), and how many plants it covers. Read-only status only — for a sitter-mode vacation's full per-plant detail and schedule, use sitter_briefing.", async () => apiGet("/api/v1/vacation/status"));
|
|
124
|
+
// --- Nudges -------------------------------------------------------------
|
|
125
|
+
server.tool("list_nudges", "Gaps in the collection worth mentioning: stale or missing photos, plants without a type or location, long-unresolved health entries, and app features the caller has never tried. This is the low-pressure 'is there anything I should suggest?' tool — distinct from due/overdue care, which is deadline-driven. Returns the caller's whole ranked queue (not just the top one the app itself would show), each entry as { kind, variant, count, href }: count is an aggregate number for that kind (0 for a feature-discovery entry, which pitches a feature rather than a quantity), and href is the in-app page to send the user to. An empty list means nothing is currently worth nudging about.", async () => apiGet("/api/v1/nudges"));
|
|
124
126
|
// --- Care logging (writes) --------------------------------------------------
|
|
125
127
|
/**
|
|
126
128
|
* Mutating tools. These require an API key with the `write` scope; a read-only
|
|
@@ -174,10 +176,11 @@ server.tool("record_hydro_event", "WRITE: log a hydro-culture event for a hydro-
|
|
|
174
176
|
recordedAt: optionalPlantDate(""),
|
|
175
177
|
notes: z.string().max(2000).optional().describe("Free-text notes."),
|
|
176
178
|
}, async ({ plantId, ...body }) => apiSend("POST", `/api/v1/plants/${plantId}/hydro`, body));
|
|
177
|
-
server.tool("snooze_care", "WRITE: postpone a plant's next watering or fertilizing due date
|
|
179
|
+
server.tool("snooze_care", "WRITE: postpone a plant's next watering or fertilizing due date — the same snooze picker the care hub offers. originalDueAt is the due date being pushed out (the nextDue value from list_due_care/get_care_calendar for that plant/careType), not today's date. Pass exactly one of durationDays (a preset: 1, 3, 7, or 14 days, counted from originalDueAt or from today when that date has already passed) or snoozedUntil (an explicit YYYY-MM-DD day, tomorrow at the earliest and at most a year out). Requires a write-scoped API key.", {
|
|
178
180
|
plantId: z.number().int().positive().describe("Plant id."),
|
|
179
181
|
careType: z.enum(["water", "fert"]).describe("Which schedule to postpone."),
|
|
180
|
-
durationDays: z.union([z.literal(1), z.literal(3), z.literal(7), z.literal(14)]).describe("Snooze duration in days — one of the app's presets."),
|
|
182
|
+
durationDays: z.union([z.literal(1), z.literal(3), z.literal(7), z.literal(14)]).optional().describe("Snooze duration in days — one of the app's presets, counted from originalDueAt, or from today if that date has passed. Omit when passing snoozedUntil."),
|
|
183
|
+
snoozedUntil: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("The day the task should come back, as YYYY-MM-DD — after today, within the next 365 days. Omit when passing durationDays."),
|
|
181
184
|
originalDueAt: z.string().describe("The due date being pushed out, e.g. from list_due_care's nextDue for this plant/careType."),
|
|
182
185
|
note: z.string().max(160).optional().describe("Free-text reason, e.g. 'away for the week'."),
|
|
183
186
|
}, async (args) => apiSend("POST", "/api/v1/care/snooze", { ...args }));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sebamomann/plants-mcp",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "MCP server for the Sprig plant app:
|
|
3
|
+
"version": "2.7.0",
|
|
4
|
+
"description": "MCP server for the Sprig plant app: 77 tools to read a plant collection, browse the shared global plant type catalog, log care (watering, fertilizing, repotting, refills, hydro events), edit a plant's identity/care/lifecycle status, add, propagate or merge plants, snooze a due date, check for nudges worth mentioning, manage a wishlist and notifications, watch a plant type for changes, check vacation status, read past problem diagnoses, create a location/soil/fertilizer/pot, and contribute a new species to the catalog. Read-only by default; writes need a write-scoped API key. Two tools can delete a watering/fertilization event; nothing else deletes collection history. 18 more tools read, edit, merge and review the shared global plant type catalog and the instance's operational status (queues, catalog health, activity, growth, community, trade and content aggregates) for an admin-scoped key.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "sebamomann <github@sebamomann.de>",
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"prepublishOnly": "npm run build"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
53
|
-
"zod": "^4.
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.30.1",
|
|
53
|
+
"zod": "^4.6.5"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@types/node": "^26.
|
|
56
|
+
"@types/node": "^26.6.2",
|
|
57
57
|
"typescript": "^6.0.3",
|
|
58
|
-
"vitest": "^
|
|
58
|
+
"vitest": "^5.0.1"
|
|
59
59
|
}
|
|
60
60
|
}
|