Skip to content

Repository files navigation

lite-task

A local-first task manager with projects, tasks, attachments (images, audio, video, voice memos), and a Telegram AI bot — built with Deno, Fresh 2, and SQLite (or Turso).

Designed to also act as an MCP server so Claude (or Claude Desktop) can read and manage your tasks directly from conversations.


Features

  • Projects — organize work into projects
  • Tasks — title, description, priority (low / medium / high), status (todo / in_progress / done)
  • Board & list views — toggle between a kanban board (with drag & drop) and a grouped list view
  • Attachments — drag & drop images, upload audio files (MP3, M4A), upload video files (MP4), or record voice memos per task
  • Image lightbox — click any image attachment to view it full-screen with prev/next navigation
  • Clickable links — URLs in task descriptions are automatically rendered as links
  • Calendar — interactive calendar (FullCalendar) with events, notes, and reminders; month/week/day/year views; per-day event panel with inline create/edit/delete; stats bar
  • Event notifications — Telegram message 10 min before timed events; optional phone call (xAI voice agent) 5 min before
  • SQLite or Turso — local SQLite by default; switch to Turso cloud database via env vars
  • REST API — clean JSON API for programmatic access
  • MCP server — two modes: direct DB (local) or HTTP client (remote/Docker)
  • Telegram bot — AI-powered bot (Claude or GPT-4o-mini) that manages tasks via natural language, supports voice transcription and media attachments

Quick start (local)

Requires Deno 2.2+.

git clone https://github.com/ddtch/lite-task
cd lite-task

deno task dev
# → http://localhost:8011

The SQLite database (data/task-light.db) and uploads (data/uploads/) are created automatically on first run inside the data/ directory.

Arc browser users: the dev server uses a custom Vite protocol scheme that Arc blocks. Use deno task preview (production build) for full functionality, or open in Chrome/Firefox.


Database

Local SQLite (default)

No configuration needed. The database is created at data/task-light.db relative to the working directory on first run.

Turso (cloud SQLite)

Turso is a libSQL-based cloud database. When TURSO_DB_URL and TURSO_API_KEY are both set, the app connects to Turso instead of local SQLite — no other changes needed.

TURSO_DB_URL=libsql://your-database.turso.io
TURSO_API_KEY=your-auth-token

Get these from the Turso dashboard or the turso CLI:

turso db create lite-task
turso db show lite-task --url
turso db tokens create lite-task

When using Turso in Docker, the data/task-light.db file is never written. Uploaded files (images, audio, video) still need the data/uploads/ volume — those are stored on disk regardless of DB mode.


Running in production (without Docker)

# 1. Install dependencies
deno install --allow-scripts=npm:@tailwindcss/oxide,npm:esbuild,npm:sharp

# 2. Build the Fresh app
deno task build

# 3. Serve
deno task start
# → http://localhost:8011

To change the port:

deno serve -A --port=3000 --host=0.0.0.0 _fresh/server.js

Deploying to a VPS

Every push to main publishes a multi-arch image to ghcr.io/ddtch/lite-task (linux/amd64 and linux/arm64), so a server never builds anything: deno task build runs Vite, Tailwind and esbuild and will exhaust a 1 GB VPS. Tagged releases publish vX.Y.Z and X.Y alongside latest.

1. Put two files on the server

mkdir -p ~/lite-task && cd ~/lite-task

curl -O https://raw.githubusercontent.com/ddtch/lite-task/main/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/ddtch/lite-task/main/.env.example

Fill in .env. At minimum set VOICE_API_TOKEN (openssl rand -hex 32) and APP_BASE_URL; the Telegram, xAI and Turso blocks are all optional.

2. Start it

docker compose pull
docker compose up -d

Four containers come up — the web app, the Telegram bot, and the two schedulers — all reading the same .env and sharing ./data/:

./data/
  task-light.db       ← SQLite DB (local mode only; unused when Turso is active)
  bot-messages.db     ← Telegram message history (always local SQLite)
  uploads/            ← uploaded images, audio, and video files

The directory and the SQLite files are created on first run.

3. Put a reverse proxy in front

The app has no login of its own. The web port is published on 127.0.0.1:8011 for exactly that reason: authentication and TLS belong to a proxy on the host. Ready-made configs are in deploy/Caddy, nginx. Both do the same two things:

  • password-protect the UI, the JSON API and /mcp with basic auth;
  • let /api/voice/* and /mcp through without it, because the voice agent reaches those directly and cannot log in. They are guarded by VOICE_API_TOKEN instead, which the app accepts as Authorization: Bearer, X-Lite-Task-Token, or a ?token= query parameter — prefer a header, since a query parameter lands in access logs. Leave the variable unset and those routes are open to anyone who finds the domain; the app logs a warning at startup when that is the case.

Giving the agent its tools

Two ways, both printed by deno task calls:tools:

  • As an MCP server (recommended). In the Voice Agent Builder choose Add custom MCP server, point it at https://your-domain/mcp and add the header Authorization: Bearer $VOICE_API_TOKEN, marked Secret. That is one entry for all sixteen tools, and anything added to mcp/toolkit.ts later shows up without touching the agent. xAI supports Streamable HTTP, which is what /mcp speaks.
  • As HTTP tools. Nine voice-shaped tools declared one by one against https://your-domain/api/voice/tool, with the same bearer header. They take project and task names instead of ids, which suits speech; the MCP tools are id-based, so over MCP the agent lists first and acts second.

Once the domain resolves, set APP_BASE_URL=https://your-domain and run deno task calls:tools to get tool URLs with the token already embedded.

Updating

docker compose pull && docker compose up -d

Pin a release instead of tracking main by setting LITE_TASK_TAG=v1.2.3 in .env; rolling back is then editing that line and re-running the same command.

Building the image yourself

For local development, or to run a patched build on a machine with ~2 GB of free memory:

git clone https://github.com/ddtch/lite-task
cd lite-task
cp .env.example .env

docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build

MCP Server — integrate with AI tools

lite-task ships three MCP server modes. Pick one based on your setup.

Mode 1: Built-in HTTP endpoint (easiest — recommended for Cursor)

The app exposes an MCP endpoint at /mcp using the Streamable HTTP transport. No subprocess, no env vars — just point your tool at the URL.

Works whenever the app is running (locally or in Docker).

Configure Cursor

~/.cursor/mcp.json:

{
  "mcpServers": {
    "lite-task": {
      "url": "http://localhost:8011/mcp"
    }
  }
}

That's it. Reload MCP servers in Cursor (Cmd+Shift+P → "MCP: Reload Servers").


Mode 2: HTTP client (binary / Deno)

Connects to any running lite-task instance over HTTP via stdio. Works whether the app runs locally, in Docker, or on a remote server. Can be compiled to a standalone binary — no Deno required on the machine running the AI tool.

Step 1 — compile the binary (once) — optional

cd task-light
deno task compile-mcp
# → produces ./lite-task-mcp

# Move somewhere permanent
mv lite-task-mcp ~/.local/bin/          # Linux / macOS

Or skip compilation and use Deno directly — see configs below.


Configure Claude Desktop

Config file locations:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
  • Linux: ~/.config/Claude/claude_desktop_config.json

With compiled binary:

{
  "mcpServers": {
    "lite-task": {
      "command": "/Users/you/.local/bin/lite-task-mcp",
      "env": {
        "LITE_TASK_URL": "http://localhost:8011"
      }
    }
  }
}

With Deno (no compilation needed):

{
  "mcpServers": {
    "lite-task": {
      "command": "deno",
      "args": ["run", "-A", "/path/to/task-light/mcp/http-client.ts"],
      "env": {
        "LITE_TASK_URL": "http://localhost:8011"
      }
    }
  }
}

After editing, restart Claude Desktop. You should see the lite-task tools listed under the hammer icon in a new conversation.


Configure Cursor

Recommended: use Mode 1 (built-in HTTP endpoint) — just "url": "http://localhost:8011/mcp" with no subprocess.

Alternatively, with the compiled binary or Deno:

{
  "mcpServers": {
    "lite-task": {
      "command": "/Users/you/.local/bin/lite-task-mcp",
      "env": {
        "LITE_TASK_URL": "http://localhost:8011"
      }
    }
  }
}

Configure Claude Code (CLI)

Add to ~/.claude/settings.json:

{
  "mcpServers": {
    "lite-task": {
      "command": "/Users/you/.local/bin/lite-task-mcp",
      "env": {
        "LITE_TASK_URL": "http://localhost:8011"
      }
    }
  }
}

You can also scope it to a single project by placing the same config in .claude/settings.json at the project root.

Restart Claude Code after editing. Run /mcp in a session to confirm the server is connected.


Mode 3: Direct DB access (local only)

When lite-task and the AI tool run on the same machine, this mode skips HTTP entirely and reads SQLite directly — no web server needed.

{
  "mcpServers": {
    "lite-task": {
      "command": "deno",
      "args": ["run", "-A", "/path/to/task-light/mcp/server.ts"],
      "cwd": "/path/to/task-light"
    }
  }
}

The cwd must point to task-light/ so the server resolves data/task-light.db correctly. This mode does not support Turso — it always reads the local SQLite file.


Available MCP tools

Tool Description
list_projects List all projects with task counts
create_project Create a new project
get_project Get a project with its tasks
delete_project Delete a project and all its tasks
list_tasks List tasks — filter by project_id, status, priority
create_task Create a task in a project
get_task Get task details including attachments
update_task Update title, description, status, or priority
delete_task Delete a task
get_attachment Download an attachment image and return it as base64
list_events List calendar events — filter by month, project_id
create_event Create a calendar event, note, or reminder (+ notify_call for phone reminder)
get_event Get a calendar event by ID
update_event Update event fields including notify_call
delete_event Delete a calendar event

Publishing the MCP binary

The MCP HTTP client (mcp/http-client.ts) compiles to a standalone binary. The .github/workflows/release.yml workflow automates this: push a version tag and it cross-compiles for all five targets and creates a GitHub Release.

git tag v0.1.0
git push origin v0.1.0
# → GitHub Actions builds and publishes the release automatically

npm

cd npm-package
npm publish

Users install with:

npm install -g lite-task-mcp

Homebrew

Homebrew requires a tap repo named homebrew-lite-task. After each release, update the sha256 values in the formula using checksums.txt from the GitHub Release.

brew tap ddtch/lite-task
brew install lite-task-mcp

Shell script (universal)

curl -fsSL https://raw.githubusercontent.com/ddtch/lite-task/main/install.sh | sh

Telegram Bot

lite-task ships a Telegram bot that accepts natural-language messages and uses an AI agent (Claude or GPT-4o-mini) to read and manage your tasks.

Examples:

  • "List my projects"
  • "Create a task called Fix login bug in project Personal with high priority"
  • "What tasks are in progress?"
  • "Add an event called Team standup on 2026-03-10 at 10:00"
  • "Create a reminder for March 15 — submit tax forms"
  • "What events do I have this month?"
  • Send a voice message — it gets transcribed and the agent acts on the spoken words
  • Send a photo or file — the agent can attach it to any task

Setup

1. Create a bot on Telegram

Talk to @BotFather, send /newbot, follow the prompts, copy the token. To find your user ID, message @userinfobot.

2. Configure environment variables

cp .env.example .env

Edit .env:

LITE_TASK_URL=http://localhost:8011    # or wherever lite-task runs
TELEGRAM_BOT_TOKEN=<token from BotFather>
BOT_HOST_ID=<your Telegram user ID>   # restricts bot to you only
ANTHROPIC_API_KEY=<your key>          # takes priority over OpenAI
OPENAI_API_KEY=<your key>             # fallback agent; also enables voice transcription (Whisper)

BOT_HOST_ID is required — the bot only responds to messages from this user ID, even in groups and channels it joins.

3. Run the bot

# Terminal 1 — web app
deno task dev

# Terminal 2 — Telegram bot
deno task bot

4. Docker (run both together)

docker compose up -d

The bot and event-scheduler services start automatically after the lite-task service passes its health check. All services share the ./data volume, so databases persist across restarts.

AI provider

Env var Provider Model
ANTHROPIC_API_KEY Anthropic Claude claude-sonnet-4-6
OPENAI_API_KEY OpenAI gpt-4o-mini

Set one or both. Anthropic takes priority if both are present.

Voice transcription

When you send a voice message, it is automatically transcribed using OpenAI Whisper (whisper-1) and the transcript is passed to the agent — which can create tasks, update descriptions, or answer questions based on what you said. The audio file is also available to attach to any task.

Transcription requires OPENAI_API_KEY. Without it, the bot falls back to handling the voice file as a plain attachment.

Media support

Telegram type Stored as
Photo image
Voice message voice
Audio (MP3/M4A) audio
Video (MP4) video
Document/file auto-detected by MIME type

Groups and channels

Add the bot to a Telegram group or channel. It saves all messages it sees to data/bot-messages.db. From your private chat with the bot, you can ask it to read group history:

  • "What was discussed in the team group today?"
  • "Create tasks from the last 20 messages in the project channel"

In groups, the bot only responds when @mentioned or when replying to its own message.


Voice Calling (xAI)

lite-task uses the xAI Voice Agent for voice-based task management: you call the agent's number, and it can call you back with reminders.

What you can do by voice:

  • "Create a task called Fix login bug in project Personal"
  • "What tasks are in progress?"
  • "Mark the deploy task as done"
  • "Remind me about the deadline tomorrow at 3pm"

How the pieces are split

The agent itself lives in the Voice Agent Builder (console.x.ai → Voice → Agents), not in this repo, because the /v1/agents and /v1/tools APIs answer 403 "agents endpoint is not enabled for this team" — so the prompt and the tools cannot be pushed onto the agent over the API. This repo stays the source of truth for both and prints them for pasting.

What the app drives over the API: outbound calls, phone-number configuration (/v2/phone-numbers), call control (refer, hangup), ephemeral client secrets, and the tool endpoint every tool call lands on.

Setup

1. Create the agent

Sign up at console.x.ai, create a voice agent in Voice → Agents, and attach a phone number to it. Each account includes one.

2. Configure environment variables

XAI_API_KEY=<your xAI API key>
XAI_AGENT_ID=<agent_... from the Builder>
APP_BASE_URL=<public URL for webhooks, e.g. https://your-domain.com or ngrok URL>

Check what the account exposes and how each number is routed:

deno task calls:setup

3. Load the prompt and tools into the agent

deno task calls:tools

Paste the printed prompt into the agent, set its timezone to the printed value, and add each tool with the printed schema, pointing every one at $APP_BASE_URL/api/voice/tool. Re-run this whenever APP_BASE_URL changes — the tool URLs embed it.

Optionally point the agent's webhook at $APP_BASE_URL/api/voice/webhook and put the signing secret in XAI_WEBHOOK_SECRET so call events are logged and verified.

4. Configure reminders (optional)

For outbound reminder calls, add:

REMINDER_TO_NUMBER=+1YYYYYYYYYY    # your personal number
XAI_FROM_NUMBER=+1XXXXXXXXXX       # only if the agent has more than one number

Run the reminder scheduler:

deno task calls:scheduler

Check the phone path without waiting for a reminder to come due:

deno task calls:test              # calls REMINDER_TO_NUMBER
deno task calls:test +15551234567 # or any number

Outbound calls and the gated API. Placing a call goes through the same Voice Agent Builder API that is disabled for teams outside the beta, so the scheduler logs xAI's 403 verbatim and marks the reminder failed if your team is not enabled yet. deno task calls:test reports the same 403 in one second, which is the quickest way to tell an account problem from a configuration one. XAI_OUTBOUND_PATH overrides the endpoint path (default /v1/realtime/calls) if xAI publishes a different one.


Event Notifications

Calendar events with a time set automatically get notifications:

  • Telegram message — sent ~10 minutes before the event via the bot
  • Phone call (optional) — triggered ~5 minutes before if "Call me" was checked when creating the event

Setup

1. Telegram notifications (required)

Needs TELEGRAM_BOT_TOKEN and BOT_HOST_ID in .env (same as the bot).

2. Phone call notifications (optional)

Needs the xAI voice agent configured (see Voice Calling above) plus:

XAI_AGENT_ID=<your agent ID>
REMINDER_TO_NUMBER=+1YYYYYYYYYY

3. Run the event scheduler

deno task events:scheduler

In Docker, the event-scheduler service starts automatically alongside the bot.

How it works

  • The scheduler polls every 60 seconds for events approaching their event_time
  • Events within 10 minutes get a Telegram notification (once per event)
  • Events within 5 minutes with notify_call = 1 get a phone call (once per event)
  • Notification flags (notified_telegram, notified_call) prevent duplicate sends

REST API

Projects

GET    /api/projects          → list all projects
POST   /api/projects          → create project  { name, description? }
GET    /api/projects/:id      → get project + tasks
PUT    /api/projects/:id      → update project  { name?, description? }
DELETE /api/projects/:id      → delete project

Tasks

GET    /api/tasks             → list tasks  ?project_id=&status=&priority=
POST   /api/tasks             → create task  { project_id, title, description?, priority?, status? }
GET    /api/tasks/:id         → get task + attachments
PUT    /api/tasks/:id         → update task  { title?, description?, priority?, status? }
DELETE /api/tasks/:id         → delete task

Calendar Events

GET    /api/events            → list events  ?month=YYYY-MM&project_id=
POST   /api/events            → create event  { title, event_date, description?, event_time?, type?, project_id?, notify_call? }
GET    /api/events/:id        → get event
PUT    /api/events/:id        → update event  { title?, description?, event_date?, event_time?, type?, project_id?, notify_call? }
DELETE /api/events/:id        → delete event

Event types: event, note, reminder.

notify_call (boolean) — when true and event_time is set, the event scheduler will trigger a phone call 5 minutes before the event. Timed events always get a Telegram notification 10 minutes before, regardless of this flag.

Attachments

POST   /api/tasks/:id/upload  → upload file (multipart, field: "file")
                                 image/*        → type "image"
                                 audio/webm|ogg → type "voice" (recorded memo)
                                 other audio/*  → type "audio" (MP3, M4A, etc.)
                                 video/*        → type "video" (MP4, MOV, etc.)
GET    /api/uploads/:filename → serve uploaded file

Voice

POST   /api/voice/tool       → function-calling dispatcher (called by the xAI agent)
POST   /api/voice/webhook    → xAI event webhook (realtime.call.incoming, call lifecycle)

Reminders

GET    /api/reminders         → list reminders  ?status=
POST   /api/reminders         → create reminder  { message, remind_at, phone_number?, task_id?, project_id? }
GET    /api/reminders/:id     → get reminder
PUT    /api/reminders/:id     → update reminder  { message?, remind_at?, phone_number?, status? }
DELETE /api/reminders/:id     → delete reminder

Project structure

task-light/
├── bot/
│   ├── main.ts            # Telegram bot entry point (grammY)
│   ├── agent.ts           # AI agent loop (Anthropic / OpenAI) + voice transcription
│   ├── tools.ts           # Tool definitions and REST API executor
│   ├── media.ts           # Telegram file download helpers
│   └── store.ts           # SQLite store for group/channel message history (data/bot-messages.db)
├── calls/
│   ├── xai.ts             # xAI voice API client (fetch-based)
│   ├── tools.ts           # Voice agent tool definitions
│   ├── setup.ts           # Reports account, numbers and agent routing
│   ├── manifest.ts        # Prints prompt + tools to paste into the Builder
│   ├── scheduler.ts       # Reminder scheduler — triggers outbound calls
│   └── event-scheduler.ts # Event notification scheduler (Telegram + phone calls)
├── db/
│   ├── database.ts        # DB adapter — local SQLite (node:sqlite) or Turso (@libsql/client)
│   └── queries.ts         # Async CRUD helpers (work with both adapters)
├── mcp/
│   ├── server.ts          # MCP stdio server — direct SQLite access
│   └── http-client.ts     # MCP stdio server — HTTP client (compilable)
├── routes/
│   ├── _app.tsx           # Global layout
│   ├── index.tsx          # → redirect to /projects
│   ├── calendar.tsx       # Calendar page (FullCalendar, events/notes/reminders)
│   ├── calls.tsx          # Voice agent page (reminders, call history)
│   ├── projects/
│   │   ├── index.tsx      # Project list
│   │   └── [id]/
│   │       ├── index.tsx  # Project detail (list + board view, view toggle)
│   │       └── tasks/
│   │           ├── new.tsx
│   │           └── [taskId]/
│   │               ├── index.tsx  # Task detail (attachments, status update)
│   │               └── edit.tsx
│   ├── mcp.ts             # MCP Streamable HTTP endpoint (/mcp)
│   └── api/
│       ├── projects/
│       ├── tasks/
│       │   └── [id]/upload.tsx
│       ├── uploads/
│       ├── events/         # Calendar event CRUD
│       ├── voice/          # xAI voice agent endpoints
│       │   ├── tool.ts     # Function-calling dispatcher
│       │   └── webhook.ts  # Call lifecycle events
│       └── reminders/      # Reminder CRUD
├── islands/               # Client-side Preact components (hydrated in browser)
│   ├── ProjectCreateModal.tsx
│   ├── KanbanBoard.tsx        # Drag-and-drop board view
│   ├── ImageLightbox.tsx      # Full-screen image viewer
│   ├── AttachmentUploader.tsx # Drag-drop file uploader (image / audio / video)
│   ├── Calendar.tsx            # Interactive calendar (FullCalendar)
│   ├── VoiceRecorder.tsx      # In-browser voice memo recorder
├── components/
│   └── Badge.tsx
├── data/                  # Runtime data (gitignored) — DB files + uploads
│   ├── task-light.db      # Main app SQLite database (local mode)
│   ├── bot-messages.db    # Telegram message history
│   └── uploads/           # Uploaded files
├── Dockerfile
├── docker-compose.yml
├── .env.example
└── deno.json

Stack

Layer Technology
Runtime Deno 2.2+
Framework Fresh 2.2
Bundler Vite 7 + @fresh/plugin-vite
Styling Tailwind CSS v4
Database SQLite via node:sqlite (local) or Turso (@libsql/client)
Interactivity Preact islands + @preact/signals
MCP @modelcontextprotocol/sdk
Telegram bot grammY
AI agent Anthropic SDK / OpenAI SDK
Voice calling xAI Voice Agent (Grok voice, SIP telephony)

Deno tasks

Task What it does
deno task dev Dev server with HMR on port 8011
deno task build Build for production → _fresh/
deno task start Serve production build on port 8011
deno task preview Build + serve in one command (useful in Arc browser)
deno task bot Run the Telegram bot (reads .env)
deno task mcp MCP server — direct SQLite access
deno task mcp:http MCP server — HTTP client mode
deno task compile-mcp Compile MCP HTTP client to a standalone binary
deno task calls:setup Report xAI account, phone numbers and agent routing
deno task calls:tools Print prompt + tools to paste into the Builder
deno task calls:test Place one real outbound call now, to test the path
deno task calls:scheduler Run reminder scheduler for outbound calls
deno task events:scheduler Run event notification scheduler (Telegram + calls)

Data

All runtime data lives under data/ (relative to the working directory):

Path Contents
data/task-light.db Main app database (local SQLite mode only)
data/bot-messages.db Telegram group/channel message history (always local)
data/uploads/ Uploaded files (images, audio, video)

All files are created automatically on first run. In Docker, the entire data/ directory is bind-mounted from the host (./data:/app/data), so data survives container restarts and image rebuilds.

When Turso is configured, data/task-light.db is never written — but data/uploads/ is still used for file storage.

About

very simple very light task manager + mcp to allow your favorite agent create and monitor yours or your gents tasks

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages