ClickDown
Enables AI agents to manage Kanban tasks, boards, teams, and checklists via natural language, with full CRUD operations and live updates.
README
ClickDown
<p align="center"> <img src="screenshots/board.png" alt="ClickDown board — Kanban task manager" width="1280" /> </p>
A fast, lightweight, multi-team Kanban task manager built on Bun and SQLite. ClickDown uses a hypermedia-driven architecture — server-side rendering with HTMX and Alpine.js — to deliver a responsive, app-like experience without a heavy client-side framework or build step.
It is local-first: a single Bun process provides the database (embedded SQLite), file storage (data/uploads/), and real-time updates (in-process WebSockets). No CDN, cloud service, or external broker is required — the app runs fully offline or on a LAN.
✨ Features
Tasks
- Title, description (with Markdown rendering + Write/Preview toggle), priority (low / medium / high / urgent), assignee, and due date
- Inline editing of every field from a task detail modal
- Checklists, comments, labels, and file attachments (stored under
data/uploads/) - Duplicate a task, or bulk-select and delete many at once
- Archive tasks (soft-hide) with a recycle-bin style archive view; permanent purge (single task or empty archive)
- Activity log per task tracking who changed what
- Shareable deep links — every task has a
/tasks/:idURL that opens its board with the task modal expanded - Priority color-coding and overdue / due-soon badges
- Dashboard summary of assigned tasks with team/board context
Boards
- Boards created with a default Backlog → To Do → In Progress → Done flow
- Customizable columns (add, edit, delete, reorder, color, WIP limits) — admin/owner only
- Drag-and-drop tasks within a column, across columns; column reorder for admins
- Live task search across the board, "My tasks only" filter, and "Show archived"
- Keyboard shortcuts (
/focuses search,nadds a task,Esccloses modals)
Teams & collaboration
- Create teams, or join via a tokenized invite link (slug alone is not enough; owners/admins can regenerate the link)
- Owner / admin / member roles, with a member-management UI to change roles and remove members
- Isolated boards and tasks per team with membership-based access control
- Board/column structure mutations require admin or owner; task work is open to all members
Real-time (WebSocket)
- Live presence — avatars appear/disappear as team members open or close the board; new viewers receive a roster of who's already there
- Live task updates — create, edit, move, delete, and archive operations broadcast to all viewers instantly
- Refresh safety — remote changes reload the board with your search/filters preserved, and are deferred (with a notice) while you have a dialog open so unsaved edits are never lost
- Built on Bun's native
server.publish()+ HTMX WS Extension + Alpine.js; no external message broker required
Accounts & UX
- Registration / login / logout with bcrypt-hashed passwords (cost 12) and strength validation (8+ chars with upper, lower, digit, and special character)
- Profile settings to update display name, email, and password (email or password change requires your current password; other sessions are revoked)
- API tokens for AI agents — create/revoke personal tokens from the profile page (see MCP below)
- Auto-generated initials avatars
- Light / dark theme toggle (persisted to
localStorage) - Toast notifications for all actions — visible even above open modals, and on network failures
- Responsive layout and a custom design system over a modernized Pico CSS base
- Structured logging via pino; health probes at
/healthand/ready
AI agent API (MCP)
- Model Context Protocol endpoint at
POST /api/mcp(Streamable HTTP, JSON-RPC 2.0) - Personal bearer tokens (SHA-256 hashed at rest) created from the profile page, with a one-step
claude mcp addcommand shown on creation - 16 tools covering teams, boards, tasks, checklists, comments, and search — mutations broadcast live to open boards
🛠 Tech Stack
| Layer | Technology |
|---|---|
| Runtime & server | Bun (Bun.serve) |
| Database | SQLite via bun:sqlite |
| Templating | @kitajs/html (server-side JSX → HTML) |
| Interactivity | HTMX + Alpine.js |
| Styling | Pico CSS + a custom design system |
| Auth | bcrypt password hashing, cookie sessions, SHA-256-hashed API tokens |
| Agent API | MCP Streamable HTTP (JSON-RPC 2.0, no SDK dependency) |
| IDs | nanoid |
| Logging | pino |
| Lint / Format | Biome |
| Testing | bun test (unit + integration) + Playwright (E2E, Chromium + Firefox) |
🚀 Getting Started
Prerequisites
Bun 1.3.14 is the only requirement. Install or upgrade it if needed:
curl -fsSL https://bun.sh/install | bash
bun upgrade
If you use mise, the pinned mise.toml provides the same version automatically (mise install).
Installation
git clone <repository-url>
cd ClickDown
bun install
Database Setup
bun run dev and bun run start apply pending SQLite migrations automatically. You can also run them explicitly:
bun run db:migrate
By default the database lives at data/clickdown.db (override with DB_PATH).
Running the App
bun run dev # development, with hot reload (recommended)
bun run start # production
The app is then available at http://localhost:3000.
Health checks: GET /health (liveness) and GET /ready (schema and writable local upload storage).
All browser runtime assets are served locally from installed packages. Running ClickDown does not require CDN or cloud-service access.
Docker
A production-ready Dockerfile is included:
docker build -t clickdown .
docker run -p 3000:3000 -v clickdown-data:/app/data -e NODE_ENV=production clickdown
The container:
- Applies pending migrations before accepting traffic, and ships a
HEALTHCHECKon/ready - Installs production dependencies only in the runtime stage (no Playwright/TypeScript/Biome)
- Runs as the non-root
bunuser with the base image pinned by digest - Persists SQLite data (and uploads) in a named volume (
clickdown-data) - Sets
NODE_ENV=production(strict CSRF Origin checks) - Declares
STOPSIGNAL SIGTERM— the app shuts down gracefully (stops accepting requests, closes WebSocket connections, closes the database) - Exposes port 3000
The SQLite, in-memory rate limit, and WebSocket design supports one application process. Do not run multiple replicas against the same database volume.
.dockerignore excludes test files, local DBs, and dev tooling for a minimal image.
Optional smoke: bun run smoke:docker — builds the image, polls /ready, checks /health, and always cleans up (requires Docker).
⚙️ Configuration
ClickDown is configured through environment variables (Bun auto-loads .env):
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
Port the HTTP server listens on |
DB_PATH |
data/clickdown.db |
SQLite database file path |
NODE_ENV |
— | Set to production for CSRF Origin requirements and quieter logs |
APP_URL |
request origin | Browser-facing base URL used for invite links and Origin checks; set this behind a reverse proxy |
TRUST_PROXY |
false |
Trust X-Forwarded-For for rate-limit keys; enable only behind a controlled proxy |
COOKIE_SECURE |
inferred from APP_URL |
Force (true) or disable (false) the Secure cookie flag |
E2E_TEST |
— | Test-only flag that disables rate limiting; set automatically by Playwright, never in production |
UPLOAD_DIR |
data/uploads |
Local directory for task attachments |
LOG_LEVEL |
debug / info |
Pino log level (debug in dev, info in production by default) |
🤖 MCP (AI agent) endpoint
ClickDown exposes a Model Context Protocol endpoint so AI agents (Claude Code, Cursor, custom scripts, etc.) can find and work on tasks directly.
Endpoint: POST /api/mcp (Streamable HTTP transport, JSON-RPC 2.0)
Setup
-
Open Profile → API Tokens (MCP) and create a token. It is shown once — copy it.
-
Register ClickDown with Claude Code (the profile page shows this command pre-filled with your token and URL):
claude mcp add --transport http clickdown http://localhost:3000/api/mcp \ --header "Authorization: Bearer cd_your_token_here" -
In Claude Code, run
/mcpto verify the connection.
For config-file based clients (Cursor, etc.):
{
"mcpServers": {
"clickdown": {
"type": "http",
"url": "http://localhost:3000/api/mcp",
"headers": { "Authorization": "Bearer cd_your_token_here" }
}
}
}
Tokens act as your user (full team access) and can be revoked at any time from the profile page. Only the SHA-256 hash is stored; session cookies are not accepted on the endpoint.
Available tools
| Tool | Purpose |
|---|---|
list_teams / list_boards / get_board |
Navigate teams → boards → columns |
list_tasks |
Tasks on a board (filter by column, assignee, archived) |
get_task |
Full detail: description, checklist, comments, labels, attachments |
create_task / update_task / move_task / archive_task / delete_task |
Task lifecycle |
add_comment |
Leave progress notes on a task |
add_checklist_item / toggle_checklist_item / delete_checklist_item |
Checklist management |
search_tasks |
Text search across all accessible boards |
get_my_tasks |
Everything assigned to the token's user |
All mutations broadcast over the existing WebSocket layer, so open boards update live when an agent makes a change. A typical agent flow: list_teams → list_boards → list_tasks → get_task → move_task + add_comment.
📁 Project Structure
src/
├── index.tsx # Entry point: handleRequest() + Bun.serve()
├── config/ # Database + migrations runner
├── middleware/ # Session auth
├── mcp/ # MCP protocol (JSON-RPC) + agent tool definitions
├── models/ # TypeScript types
├── realtime/ # WebSocket pub/sub + presence
├── routes/ # HTTP route handlers
├── services/ # Business logic
├── schemas/ # Zod validation
├── utils/ # access helpers, logger, password, HTML safety
└── components/ # Kita JSX pages, fragments, modals
public/ # Static assets (custom.css, app.js, favicon)
migrations/ # SQL migrations (001–018, checksummed + auto-applied)
scripts/ # migrate.ts, e2e-reset.ts
tests/ # unit, integration, e2e
.github/workflows/ # CI: static gates, E2E, Docker smoke
mise.toml # Bun 1.3.14 toolchain pin
data/ # SQLite DB + uploads (gitignored)
Request flow: Browser → HTMX → handleRequest → auth/access → service → SQLite → Kita JSX → HTML fragment.
🔒 Security notes
- XSS prevention is enforced in CI —
@kitajs/ts-html-plugin(bun run xss:scan) fails the build on unescaped JSX; user content is escaped at render, and Markdown is sanitized withhttp(s)-only links - Local-only CSP — all scripts/styles/fonts load from the same origin (vendored, no CDN), with
default-src 'self' - CSRF — Origin/Referer must match on every mutating request (required in production;
APP_URLdefines the external origin behind a proxy). The MCP endpoint is exempt: bearer tokens are never sent ambiently by browsers, and session cookies are rejected there - Sessions — HttpOnly + SameSite=Lax cookies, 7-day expiry; password and email changes require the current password and revoke other sessions (including live WebSockets)
- API tokens — shown once at creation, stored as SHA-256 hashes, revocable instantly, deleted with the owning user; a periodic sweep closes WebSockets whose sessions or team access have been revoked
- Invites — secret, regenerable tokens; team slugs alone never grant access
- Rate limits — in-memory, scoped per endpoint and per user for auth, join, profile, token creation, markdown preview, uploads (10 MB per file), and task/board/team mutations
- Attachments — served only to team members, forced
Content-Disposition: attachment
💾 SQLite backups
The database is a single file (default data/clickdown.db, plus -wal/-shm in WAL mode).
# Safe online backup while the app is running (SQLite CLI)
sqlite3 data/clickdown.db ".backup 'data/clickdown-backup.db'"
# Or stop the app and copy the files
cp data/clickdown.db data/clickdown.db-wal data/clickdown.db-shm /path/to/backup/
Also back up data/uploads/ if you use attachments. Prefer volume snapshots when running under Docker.
🧪 Testing
- Unit (
tests/unit) — services/utils against in-memory SQLite - Integration (
tests/integration) — realhandleRequestin-process, including the MCP endpoint - E2E (
tests/e2e) — Playwright against a server on port 3001, including security/hardening regressions (XSS, offline, realtime) and the MCP token flow. Runs on both Chromium and Firefox (two suites skip on Firefox: JS coverage and clipboard permission, which are Chromium-only Playwright APIs).
Install the browser binaries once before running E2E tests:
bunx playwright install chromium firefox
bun run test # unit + integration
bun run test:coverage
bun run test:e2e # full Playwright suite
bun run test:e2e:smoke # smaller auth + security subset
bun run xss:scan # JSX escaping audit (@kitajs/ts-html-plugin)
bun run ci # typecheck + XSS scan + lint + unit/integration
bun run ci:full # ci + full e2e
GitHub Actions (.github/workflows/ci.yml) runs the full gate set on push/PR: static checks + unit/integration, the Playwright suite, and a Docker build with a readiness-probed smoke test.
📋 Available Commands
| Command | Description |
|---|---|
bun run dev |
Start with hot reload |
bun run start |
Start server (applies migrations first) |
bun run build |
Bundle entry to dist/ (Bun target) |
bun run db:migrate |
Run database migrations |
bun run test |
Unit + integration tests |
bun run test:coverage |
Tests with coverage report |
bun run test:e2e |
Playwright E2E |
bun run test:e2e:smoke |
Fast E2E subset |
bun run lint / lint:fix |
Biome |
bun run typecheck |
tsc --noEmit |
bun run xss:scan |
JSX escaping audit |
bun run ci |
typecheck + XSS scan + lint + unit/integration |
bun run ci:full |
ci + full E2E |
bun run smoke:docker |
Build image, poll /ready, check /health |
📄 License
MIT © 2026 amscotti — free to use, modify, and self-host, with attribution.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。