AGENTS.md
MCP server exposing Lunatask (task/habit tracker) to LLMs via SSE. Primary use case: Home Assistant voice assistant says "remind me to call mom tomorrow" → HA's LLM calls this server's create_task tool → task appears in Lunatask.
All of Lunatask's API docs are local (uncommitted), so feel free to read them liberally while working on the client. The index, telling you which files to read for which topics, is in lunatask/docs/index.md
Commands
just # Run all checks (fmt, lint, staticcheck, test, vuln, reuse)
just build # Build with version info
just run # Build and run
Architecture
cmd/lunatask-mcp-server.go → Config, tool registration, SSE server
tools/ → MCP tool handlers
Data flow: SSE request → MCP server → tool handler → lunatask client → Lunatask API
Domain model: Areas contain Goals. Habits are separate. Tasks belong to an Area and optionally a Goal.
Adding New Functionality
New MCP tool:
- Add handler method to
tools/(onHandlersstruct) - Register tool with
mcpServer.AddTool()incmd/lunatask-mcp-server.go
New API endpoint: Add method to lunatask/Client with request/response types
Key Patterns
- Tool descriptions are prompts: The verbose
mcp.WithDescription()strings guide the calling LLM's behavior—they explain workflows, valid values, and when to use each tool - Error returns: Handlers return
(result, nil)withIsError: true—application errors go in the result, not the Go error - Enum translation: Human strings (
"high") → API integers (1) in handlers before calling client - Provider interfaces:
tools/defines interfaces (AreaProvider, etc.) thatcmd/types implement, keeping packages decoupled
Gotchas
- Area/goal/habit IDs are static in config, not fetched—Lunatask API has no list endpoint
CreateTaskreturns(nil, nil)on HTTP 204 (task already exists)—not an errorupdate_taskMCP tool requiresnameeven for partial updates- No tests exist yet
- All files need SPDX headers (
just reusechecks this)