AGENTS.md

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:

  1. Add handler method to tools/ (on Handlers struct)
  2. Register tool with mcpServer.AddTool() in cmd/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) with IsError: 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.) that cmd/ types implement, keeping packages decoupled

Gotchas

  • Area/goal/habit IDs are static in config, not fetched—Lunatask API has no list endpoint
  • CreateTask returns (nil, nil) on HTTP 204 (task already exists)—not an error
  • update_task MCP tool requires name even for partial updates
  • No tests exist yet
  • All files need SPDX headers (just reuse checks this)