<!--
SPDX-FileCopyrightText: Amolith <amolith@secluded.site>

SPDX-License-Identifier: CC0-1.0
-->

# 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

```sh
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)
