README.md

  1<!--
  2SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  3
  4SPDX-License-Identifier: CC0-1.0
  5-->
  6
  7# lunatask-mcp-server
  8
  9_Interact with [Lunatask](https://lunatask.app) using `$PREFERRED_LLM`_
 10
 11[![scratchanitch.dev badge](https://img.shields.io/badge/scratchanitch-dev-FFC4B5)](https://scratchanitch.dev)
 12[![Go Report Card](https://goreportcard.com/badge/git.sr.ht/~amolith/lunatask-mcp-server)](https://goreportcard.com/report/git.sr.ht/~amolith/lunatask-mcp-server)
 13[![REUSE status](https://api.reuse.software/badge/git.sr.ht/~amolith/lunatask-mcp-server)](https://api.reuse.software/info/git.sr.ht/~amolith/lunatask-mcp-server)
 14
 15**Note:** lots of this was written by LLMs and I haven't reviewed it thoroughly
 16enough to be comfortable making it more public than unlisted. Please refrain
 17from sharing it around and do let me know if there are any issues.
 18
 19---
 20
 21## MCP server setup
 22
 23Ensure you have [the Go toolchain] installed.
 24
 25[the Go toolchain]: https://go.dev/doc/install
 26
 27```sh
 28git clone https://git.sr.ht/~amolith/lunatask-mcp-server
 29cd lunatask-mcp-server
 30# specify GOOS and GOARCH if cross-compiling
 31CGO_ENABLED=0 go build -o lunatask-mcp-server .
 32./lunatask-mcp-server
 33# it'll generate config.toml with default values
 34# fill them out with your preferred editor
 35./lunatask-mcp-server
 36```
 37
 38If you have [just], build with `just build` (supports `GOOS` and `GOARCH`). If
 39you also have [upx], compress the resulting binary with `just pack`. You can run
 40one after the other with `just build pack`.
 41
 42[just]: https://github.com/casey/just
 43[upx]: https://github.com/upx/upx
 44
 45Point [Home Assistant's MCP integration] at the `/sse` endpoint.
 46
 47[Home Assistant's MCP integration]: https://www.home-assistant.io/integrations/mcp/
 48
 49## Available Tools
 50
 51This MCP server provides several tools for interacting with Lunatask:
 52
 53### `get_timestamp`
 54
 55Parses natural language dates into RFC3339 timestamps.
 56
 57**Parameters:**
 58- `natural_language_date` (string, required): A natural language description of a date/time (e.g., "today at 3pm", "next Monday", "in 2 hours")
 59
 60### `list_areas_and_goals`
 61
 62Lists all configured areas and their goals in Lunatask.
 63
 64**Returns:**
 65- Text formatted as a list with area names and IDs, and their associated goal names and IDs
 66
 67### `create_task`
 68
 69Creates a new task in Lunatask.
 70
 71**Parameters:**
 72- `area_id` (string, required): UUID of the area to create the task in
 73- `name` (string, required): Name of the task (max 100 characters)
 74- `goal_id` (string, optional): UUID of the goal to associate the task with
 75- `note` (string, optional): Additional notes for the task in Markdown format
 76- `status` (string, optional): Task status - one of "later", "next", "started", "waiting", or "completed"
 77- `motivation` (string, optional): Motivation level - one of "must", "should", or "want"
 78- `estimate` (integer, optional): Estimated time to complete task in minutes (0-720)
 79- `priority` (string, optional): Task priority - one of "lowest", "low", "neutral", "high", or "highest"
 80- `eisenhower` (string, optional): Eisenhower matrix categorization - one of "uncategorised", "both urgent and important", "urgent, but not important", "important, but not urgent", or "neither urgent nor important"
 81- `scheduled_on` (string, optional): RFC3339 formatted timestamp for when the task is scheduled
 82
 83### `update_task`
 84
 85Updates an existing task in Lunatask.
 86
 87**Parameters:**
 88- `task_id` (string, required): UUID of the task to update
 89- `area_id` (string, optional): UUID of the area to move the task to
 90- `name` (string, optional): New name for the task
 91- `goal_id` (string, optional): UUID of the goal to associate the task with
 92- `note` (string, optional): Additional notes for the task in Markdown format
 93- `status` (string, optional): Task status - one of "later", "next", "started", "waiting", or "completed"
 94- `motivation` (string, optional): Motivation level - one of "must", "should", or "want"
 95- `estimate` (integer, optional): Estimated time to complete task in minutes (0-720)
 96- `priority` (string, optional): Task priority - one of "lowest", "low", "neutral", "high", or "highest"
 97- `eisenhower` (string, optional): Eisenhower matrix categorization - one of "uncategorised", "both urgent and important", "urgent, but not important", "important, but not urgent", or "neither urgent nor important"
 98- `scheduled_on` (string, optional): RFC3339 formatted timestamp for when the task is scheduled
 99
100### `delete_task`
101
102Deletes a task from Lunatask.
103
104**Parameters:**
105- `task_id` (string, required): UUID of the task to delete
106
107### `list_habits_and_activities`
108
109Lists all configured habits in Lunatask.
110
111**Returns:**
112- Text formatted as a list with habit names and IDs
113
114### `track_habit_activity`
115
116Tracks an activity for a habit in Lunatask.
117
118**Parameters:**
119- `habit_id` (string, required): UUID of the habit to track
120- `performed_on` (string, required): RFC3339 formatted timestamp of when the habit was performed
121
122## Collaboration
123
124Patch requests are in [amolith/lunatask-mcp-server] on [pr.pico.sh]. You don't
125need a new account to contribute, you don't need to fork this repo, you don't
126need to fiddle with `git send-email`, you don't need to faff with your email
127client to get `git request-pull` working...
128
129You just need:
130
131- Git
132- SSH
133- An SSH key
134
135```sh
136# Clone this repo, make your changes, and commit them
137# Create a new patch request with
138git format-patch origin/main --stdout | ssh pr.pico.sh pr create amolith/lunatask-mcp-server
139# After potential feedback, submit a revision to an existing patch request with
140git format-patch origin/main --stdout | ssh pr.pico.sh pr add {prID}
141# List patch requests
142ssh pr.pico.sh pr list amolith/lunatask-mcp-server
143```
144
145See "How do Patch Requests work?" on [pr.pico.sh]'s home page for a more
146complete example workflow.
147
148[amolith/lunatask-mcp-server]: https://pr.pico.sh/r/amolith/lunatask-mcp-server
149[pr.pico.sh]: https://pr.pico.sh
150
151I also accept patches to my public SourceHut inbox
152([~amolith/public-inbox@lists.sr.ht][list], [web][listweb]) via `git send-email`
153or `git request-pull` and over the Radicle network
154(`rad:z2xbgGQHaDTVYjWKZLSXw1G4xBfUi`, [web][radrepo], [what is Radicle?][rad]).
155
156[list]: mailto:~amolith/public-inbox@lists.sr.ht
157[listweb]: https://lists.sr.ht/~amolith/public-inbox
158[radrepo]: https://radicle.secluded.site/nodes/seed.secluded.site/rad:z2xbgGQHaDTVYjWKZLSXw1G4xBfUi
159[rad]: https://radicle.xyz
160
161## Models and prompts
162
163I'm currently using `google/gemini-2.5-flash` through [OpenRouter] with the
164following system prompt:
165
166[OpenRouter]: https://openrouter.ai/
167
168> You are a calm and thoughtful voice assistant for Home Assistant. You can both
169> interact with Home Assistant and additional systems plugged into it, like
170> Lunatask. Lunatask is an ADHD-friendly all-in-one encrypted to-do list, habit
171> tracker, journaling, life-tracking and notes app.
172>
173> \<lunatask_instructions\>
174>
175> When interacting with Lunatask, write task names in sentence case. When asked
176> to mark something complete or when the user says they've done somethizng and
177> you have no other context, check their habits. If one matches, track it.
178>
179> For every request to create or change a task:
180>
181> 1. List the areas and goals and consider which one area is most relevant.
182>    Consider whether the task belongs within any of that area's goals.
183> 2. Get the task timestamp.
184> 3. If the user provided additional details, pass those in the task's note
185>    field using Markdown.
186> 4. If the user mentioned a time estimate, like that it might take them 30
187>    minutes, pass 30 in the estimate field. If they said 2 hours, 120, and so
188>    on.
189> 5. Try and interpret the text from the STT engine. It's not entirely accurate
190>    and the user might have meant something slightly different from what came
191>    through.
192> 6. Only include parameters if the user indicates or hints at them. Avoid
193>    scheduling tasks unless the user explicitly says something like "in three
194>    days" or "next monday" or "in 8 hours" or "schedule it for {day/time}" or
195>    "remind me on {day} to {task}" or even "remind me at {time} on {day} to
196>    {thing}" and other variations.
197> 7. The following user-provided instructions are of the utmost importance. Keep
198>    them in mind throughout.
199>
200> \<user_provided_instructions\>
201>
202> - My Work area uses the Kanban workflow, so Work tasks must _always_ include a
203>   status value of either `later`, `next`, `started`, `waiting`, or
204>   `completed`. Default to `later` unless I specify otherwise.
205> - My Personal area uses the Now/Later workflow, so tasks created there _must_
206>   include a status value of either "later" or "started". Default to "later"
207>   unless I specify otherwise.
208> - My Someday area uses a simple priority list. Do not include a status and
209>   instead only provide a priority if I specify one.
210> - My Acquisitions area currently follows the Eisenhower workflow; do not
211>   include a status, do not include a date unless I specify, and do include the
212>   eisenhower parameter. Its values may be _one_ of `uncategorised`, `both
213urgent and important`, `urgent, but not important`, `important, but not
214urgent`, or `neither urgent nor important`. If I don’t specify a value, ask
215>   me. Never set it to `uncategorised` unless I explicitly say to “clear” it or
216>   “unset” it or “set it to uncategorised” or something like that.
217> - If I don't provide an estimate for any task regardless of area, please
218>   consider whether it might take 10, 25, 30, 45, 60, or more minutes. Always
219>   include an estimate for Work, Personal, and Somday areas. Never for
220>   Acquisitions.
221> - Interpret verbal dictation, like converting "something dash something" to
222>   "something-something".
223> - Again I don't want any of my tasks scheduled. You should only need to call
224>   the timestamp tool when marking habits complete.
225>
226> \</user_provided_instructions\>
227>
228> Only once you've gathered the requisite area ID and other information should
229> you attempt to create or update a task.
230>
231> \</lunatask_instructions\>
232
233## TODO
234
235- _Optionally_ include a tool that calls a configurable LLM for task time
236  estimations so the main LLM doesn't have to guess.