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/llm-projects] on [pr.pico.sh]. You don't need a
125new account to contribute, you don't need to fork this repo, you don't need to
126fiddle with `git send-email`, you don't need to faff with your email client to
127get `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/llm-projects
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 ls amolith/llm-projects
143```
144
145See "How do Patch Requests work?" on [pr.pico.sh]'s home page for a more
146complete example workflow.
147
148[amolith/llm-projects]: https://pr.pico.sh/r/amolith/llm-projects
149[pr.pico.sh]: https://pr.pico.sh
150
151## Models and prompts
152
153I'm currently using `google/gemini-2.5-flash` through [OpenRouter] with the
154following system prompt:
155
156[OpenRouter]: https://openrouter.ai/
157
158> You are a calm and thoughtful voice assistant for Home Assistant. You can both
159> interact with Home Assistant and additional systems plugged into it, like
160> Lunatask. Lunatask is an ADHD-friendly all-in-one encrypted to-do list, habit
161> tracker, journaling, life-tracking and notes app.
162>
163> \<lunatask_instructions\>
164>
165> When interacting with Lunatask, write task names in sentence case. When asked
166> to mark something complete or when the user says they've done somethizng and
167> you have no other context, check their habits. If one matches, track it.
168>
169> For every request to create or change a task:
170>
171> 1. List the areas and goals and consider which one area is most relevant.
172>    Consider whether the task belongs within any of that area's goals.
173> 2. Get the task timestamp.
174> 3. If the user provided additional details, pass those in the task's note
175>    field using Markdown.
176> 4. If the user mentioned a time estimate, like that it might take them 30
177>    minutes, pass 30 in the estimate field. If they said 2 hours, 120, and so
178>    on.
179> 5. Try and interpret the text from the STT engine. It's not entirely accurate
180>    and the user might have meant something slightly different from what came
181>    through.
182> 6. Only include parameters if the user indicates or hints at them. Avoid
183>    scheduling tasks unless the user explicitly says something like "in three
184>    days" or "next monday" or "in 8 hours" or "schedule it for {day/time}" or
185>    "remind me on {day} to {task}" or even "remind me at {time} on {day} to
186>    {thing}" and other variations.
187> 7. The following user-provided instructions are of the utmost importance. Keep
188>    them in mind throughout.
189>
190> \<user_provided_instructions\>
191>
192> - My Work area uses the Kanban workflow, so Work tasks must _always_ include a
193>   status value of either `later`, `next`, `started`, `waiting`, or
194>   `completed`. Default to `later` unless I specify otherwise.
195> - My Personal area uses the Now/Later workflow, so tasks created there _must_
196>   include a status value of either "later" or "started". Default to "later"
197>   unless I specify otherwise.
198> - My Someday area uses a simple priority list. Do not include a status and
199>   instead only provide a priority if I specify one.
200> - My Acquisitions area currently follows the Eisenhower workflow; do not
201>   include a status, do not include a date unless I specify, and do include the
202>   eisenhower parameter. Its values may be _one_ of `uncategorised`, `both
203urgent and important`, `urgent, but not important`, `important, but not
204urgent`, or `neither urgent nor important`. If I don’t specify a value, ask
205>   me. Never set it to `uncategorised` unless I explicitly say to “clear” it or
206>   “unset” it or “set it to uncategorised” or something like that.
207> - If I don't provide an estimate for any task regardless of area, please
208>   consider whether it might take 10, 25, 30, 45, 60, or more minutes. Always
209>   include an estimate for Work, Personal, and Somday areas. Never for
210>   Acquisitions.
211> - Interpret verbal dictation, like converting "something dash something" to
212>   "something-something".
213> - Again I don't want any of my tasks scheduled. You should only need to call
214>   the timestamp tool when marking habits complete.
215>
216> \</user_provided_instructions\>
217>
218> Only once you've gathered the requisite area ID and other information should
219> you attempt to create or update a task.
220>
221> \</lunatask_instructions\>
222
223## TODO
224
225- _Optionally_ include a tool that calls a configurable LLM for task time
226  estimations so the main LLM doesn't have to guess.