Task.md Utilities MCP Server
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Task.md Utilities (HENDRICKSON KANBAN)
A REST API, MCP server, and interactive CLI for managing tasks organized in lanes, where each task is a markdown file. The CLI includes a full-screen Textual TUI (kanban board with keyboard navigation) as well as non-interactive subcommands for scripting.
About Tasks.md
This project builds on and extends the Tasks.md project. For comprehensive information about the Tasks.md system, including task visualization, board views, file format specifications, and VSCode integration, visit the Tasks.md repository.
This package adds a REST API, a network-capable interactive CLI, and an MCP server for programmatic task management.
Related MCP server: task-manager-mcp
Overview
Tasks are stored as markdown files organized in lane directories:
Task title = filename (without
.md)Tags:
[tag:tagname]— one per line at the top of the fileDue dates:
[due:YYYY-MM-DD]Tasks can be split into subtasks using the
[[split]]marker
Architecture
┌──────────────────┐ HTTP ┌───────────────────┐
│ tasks CLI / TUI │ ────────▶│ Flask REST API │ :2999 (container) / :3101 (host)
│ (bin/tasks) │ │ (task_api/) │──┐
└──────────────────┘ └───────────────────┘ │ ┌──────────────┐ ┌───────────────────┐
├──▶│ task_lib/ │──▶│ Markdown files │
┌──────────────────┐ MCP ┌───────────────────┐ │ │ (in-process) │ │ /data/tasks/ │
│ AI assistant │ ────────▶│ MCP server │──┘ └──────────────┘ └───────────────────┘
│ (Claude etc.) │ │ (mcp_task_service)│ :3003 ▲
└──────────────────┘ └───────────────────┘ │ same folder
Tasks.md web app ──────────┘The REST API and the MCP server each import task_lib and read and write the
card files directly; neither calls the other. The CLI and TUI only talk to the
REST API. The Tasks.md web app reads the same folder.
Project Structure
task.md-utilities/
├── bin/
│ ├── tasks # CLI — TUI (no args) or subcommands (scripting)
│ └── tag-utility.py # One-time tag format migration utility
├── docs/
│ └── screenshot.svg # TUI kanban board screenshot
├── task_lib/
│ ├── api_client.py # Shared HTTP helpers (used by CLI and TUI)
│ ├── cli_config.py # CLI settings (~/.config/tasks/config.yaml)
│ ├── config.py # Configuration (YAML)
│ ├── lane_order.py # Per-machine lane (column) order for CLI and TUI
│ ├── errors.py # Typed errors: not_found, exists, invalid, conflict
│ ├── sections.py # Partial edits: append, upsert section, check items
│ ├── task.py # Task model and file I/O
│ └── task_manager.py # Lane, task and trash operations
├── task_tui/ # Textual TUI package
│ ├── app.py # KanbanApp — main board, keybindings, workers
│ ├── api.py # Sync API wrappers for use in workers
│ ├── screens.py # Detail, form, confirm, filter, move screens
│ └── widgets.py # LaneColumn, TaskItem, FunctionKeyBar
├── task_api/ # Flask REST API service
│ ├── app.py
│ ├── config.py
│ ├── models.py # Pydantic request/response schemas
│ ├── routes/ # tasks, trash, lanes, operations, health
│ ├── gunicorn.conf.py
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── README.md
├── mcp_task_service/ # FastMCP server for AI assistants
│ ├── server.py
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── README.md
├── tests/ # pytest suite (conftest.py isolates $HOME)
├── config.yaml # Task data location (baked into Docker images)
├── tasks.config.example.yaml # Every CLI setting with its default
├── docker-compose.yml # Deploys both services together
├── Makefile # build / push / deploy / test / lint
└── .mcp.json # Connects Claude Code in this repo to the MCP serverDeployment
Both services are deployed as Docker containers. Build from the repo root (build context must include both task_lib/ and the service directory).
Build images
make push # build and push both images (make build to only build)
make release # run the tests first, then build and push
make deploy # recreate the containers in /opt/stacks/mcp-task
# Single service, or another registry/tag
make push-mcp
make push-api REGISTRY=registry.example:5000 TAG=0.3.0make help lists every target.
Deploy
The running services are the Dockge stack in /opt/stacks/mcp-task, whose
compose file matches the root docker-compose.yml. After make push:
make deploy # docker compose pull && up -d --force-recreate in /opt/stacks/mcp-taskRestarting the MCP server drops connected MCP clients; reconnect them (in
Claude Code: /mcp reconnect). To run the stack from the repo instead, use
docker compose up -d in the repo root.
The compose file starts both services on a shared network:
Service | Internal port | Host port |
REST API | 2999 | 3101 |
MCP server | 3003 | 3003 |
Both containers mount the task data directory:
volumes:
- /mnt/raid1/lib/tasks.md/tasks:/data/tasksConfiguration
config.yaml (committed to the repo) sets the task data location inside the container. It is copied into the image at build time — no volume mount needed:
base_dir: /data/tasksTo change the data path, edit config.yaml and rebuild the images.
Running services individually
Each service has its own docker-compose.yml for standalone use:
cd task_api && docker-compose up -d # REST API only
cd mcp_task_service && docker-compose up -d # MCP onlyCLI (bin/tasks)
The tasks binary has two modes:
No arguments — launches the full-screen interactive TUI (kanban board)
With a subcommand — runs non-interactively for scripting
Installation
poetry install
poetry run bin/tasks --helppoetry install does not put tasks on your PATH; the examples below write
tasks for poetry run bin/tasks. To type tasks directly, add an alias:
alias tasks='poetry -C ~/Working/task.md-utilities run ~/Working/task.md-utilities/bin/tasks'Configuration
The CLI reads its settings from a YAML file, the first of:
--config PATHflagTASKS_CONFIGenvironment variable~/.config/tasks/config.yaml
Every key is optional and a missing default file is fine. Copy
tasks.config.example.yaml, which lists every key
with its default: api_url, request timeout, the board's app_title,
default_sort for tasks show, the empty_cell placeholder, and the table
styles. Unknown keys and badly typed values are reported as errors.
The board's column order is per machine: Shift+←/→ in the board or
tasks lanes order saves it to ~/.local/state/tasks/lane_order.yaml
($XDG_STATE_HOME, or the lane_order_file config key). tasks lanes list
and tasks show follow it too. The server, the MCP tools and other machines
still list lanes alphabetically.
The API URL comes from the first of:
--api-url URLflagTASKS_API_URLenvironment variableapi_urlin the config fileDefault:
http://localhost:3101
api_url: http://your-server:3101Interactive TUI
tasksLaunches a full-screen kanban board. Lanes are displayed as side-by-side columns; tasks are listed under each lane.
Keyboard navigation
Key | Action |
| Move between lanes |
| Move the focused lane (column) left / right; the order is saved |
| Move between tasks within a lane |
| Open task detail view |
| Go back / close dialog |
| Show keyboard shortcut help |
| New task (in the focused lane) |
| Edit selected task |
| Delete selected task (confirm prompt) |
| Filter tasks (by lane, tag, or title substring) |
| Move selected task to another lane |
| Open / close task detail view |
| Refresh board from the API |
| Quit |
Active filters are shown in a status bar at the top; press F5 again to change or clear them.
Subcommands
tasks [--api-url URL] [--config PATH] COMMAND
Commands:
show List tasks (--lane, --tag, --string, --query, --due-before,
--due-after, --sort lane|title|due|modified)
find Search titles and bodies, showing the matching text
get Show a single task in full
add Create a new task (lane must exist unless --create-lane)
update Update task fields
append Append text to the end of a task
section Replace (or add) one "## Heading" section of a task
check Tick "- [ ]" checklist items (--uncheck to untick)
delete Move a task to Trash
move Change a task's lane (--create-lane for a new lane)
trash Manage the Trash
list List trashed cards, newest first
restore Restore a card into a lane
empty Permanently delete Trash (--dry-run, --yes)
empty-trash Same as `trash empty`
split Split tasks containing [[split]] marker
lanes Manage lanes
list List lanes with task counts
order Show or set the column order (--reset)
add Create a new lane
stats Show statistics summaryAdding, renaming, moving and restoring never overwrite a card: reusing an
existing title fails, and so does naming a lane that doesn't exist (unless you
pass --create-lane). split also checks first: a card whose 1-<title>,
2-<title>, … names are already taken is left unsplit and reported.
Examples
# List all tasks
tasks show
# Filter by lane, tag, or title substring (all case-insensitive)
tasks show --lane "In Progress"
tasks show --tag urgent
tasks show --string "login"
# Show full task detail
tasks get "Implement login"
# Create a task (prompts for missing fields)
tasks add --title "Fix bug" --content "Reproduce and fix" --lane Backlog --tags "bug,urgent"
# Update fields (only provided fields change)
tasks update "Fix bug" --tags "bug,urgent,p1" --due-date 2026-06-01
# Search titles and bodies; sort by due date
tasks find "books db"
tasks show --sort due --due-before 2026-06-30
# Edit part of a card without resending the whole body
tasks section "Fix bug" Progress "Reproduced on staging"
tasks append --no-blank-line "Fix bug" -- "- [ ] write regression test"
tasks check "Fix bug" "regression test"
echo "Patch the null check" | tasks section "Fix bug" "Next action" -
# Move between lanes
tasks move "Fix bug" "In Progress"
# Delete (moves to Trash, prompts for confirmation)
tasks delete "Fix bug"
# Trash: list, restore, preview, then permanently delete (prompts for confirmation)
tasks trash list
tasks trash restore "Fix bug" Backlog
tasks trash empty --dry-run
tasks trash empty
# Lane management
tasks lanes list
tasks lanes add "Sprint 3"
# Column order: named lanes first, the rest keep their order after them
tasks lanes order # show it
tasks lanes order Doing Backlog # set it
tasks lanes order --reset # back to alphabetical
# Split a task (divides on [[split]] marker)
tasks split
# Statistics
tasks statsMCP Server
The MCP server exposes kanban operations to AI assistants (Claude, Cursor, etc.) via the Model Context Protocol.
Connecting a client
Add to your Claude Desktop / Claude Code config (~/.claude/claude_desktop_config.json or .mcp.json):
{
"mcpServers": {
"task-manager": {
"type": "http",
"url": "http://localhost:3003/mcp"
}
}
}Available tools
Every tool returns {"ok": true, ...} or {"ok": false, "error": {"code", "message"}}.
Tool | Description |
| Server version, data folder, counts |
| Lanes with task counts, plus |
| Task summaries; lane/tag/due filters, sort, limit, optional bodies |
| Search titles and bodies |
| One task in full |
| Lane, tag, due-date, overdue and due-this-week counts |
| Create a task (refuses existing titles and unknown lanes) |
| Update task fields |
| Edit part of a card |
| Move a task to another lane |
| Move a task to Trash |
| See and undo deletions |
| Permanently delete Trash contents ( |
| Create a new lane |
| Split tasks with |
Title, lane and tag matching is case-insensitive across all tools. Writes
accept expected_version (from any task result) to guard against concurrent edits,
and return the card's summary, or the full card with include_content=true.
See mcp_task_service/README.md for full details.
REST API
The REST API is documented in task_api/README.md.
Quick reference:
# Health check
curl http://localhost:3101/health
# List tasks
curl "http://localhost:3101/tasks"
curl "http://localhost:3101/tasks?lane=Backlog&tag=urgent"
curl "http://localhost:3101/tasks?search=login" # title substring
curl "http://localhost:3101/tasks?query=books%20db&sort=due" # title or body
# Create a task
curl -X POST http://localhost:3101/tasks \
-H 'Content-Type: application/json' \
-d '{"title":"My Task","content":"Details","lane":"Backlog","tags":["urgent"]}'
# Move a task
curl -X POST "http://localhost:3101/tasks/My%20Task/move" \
-H 'Content-Type: application/json' \
-d '{"lane":"In Progress"}'
# Trash: list, preview emptying
curl http://localhost:3101/trash
curl -X DELETE "http://localhost:3101/operations/trash?dry_run=true"
# Statistics
curl http://localhost:3101/operations/statisticsTask File Format
[tag:frontend]
[tag:urgent]
[due:2026-06-01]
Task body text goes here.Only lines that contain nothing but [tag:…] tokens (or a single [due:…]) are
metadata; a tag mentioned inside a sentence is ordinary text.
Data Directory Structure
/data/tasks/
├── Backlog/
│ └── Implement login.md
├── In Progress/
│ └── Fix auth bug.md
├── Done/
│ └── Setup CI.md
└── Trash/
└── old-task~20261014-120102.md # timestamp suffix keeps same-named cards apartDevelopment
Set up
poetry install --extras dev # adds black, mypy and type stubsRun tests and checks
make test # pytest with coverage for task_lib, task_api, mcp_task_service
make lint # black --check and mypy on the modules kept clean so farmake test doesn't measure task_tui; for that, run
poetry run pytest --cov=task_tui --cov-report=term-missing tests/. Much of the
older code doesn't pass black or mypy yet, so make lint covers only the
files that do.
tests/conftest.py points Path.home() at an empty temporary directory and
clears TASKS_CONFIG, TASKS_API_URL and XDG_STATE_HOME, so your own
~/.config/tasks/config.yaml and saved lane order never affect a test run.
Run the services locally
The committed config.yaml points at /data/tasks, the path inside the
containers. Locally, point TASK_CONFIG_PATH at a config for a scratch folder:
mkdir -p /tmp/tasks-dev && echo "base_dir: /tmp/tasks-dev" > /tmp/tasks-dev.yaml
TASK_CONFIG_PATH=/tmp/tasks-dev.yaml poetry run gunicorn --config task_api/gunicorn.conf.py "task_api.app:create_app()"
TASK_CONFIG_PATH=/tmp/tasks-dev.yaml poetry run python mcp_task_service/server.pyUtilities
Tag Format Migration
bin/tag-utility.py converts old-format tags (tags: tag1, tag2) to the current format ([tag:tagname]). Edit the hardcoded directory path in the script before running. Supports dry-run mode.
This server cannot be deployed
Maintenance
Related MCP Connectors
A todo and task board in kanban form, driven from chat with full CRUD.
Read and manage your EasyKanban kanban boards: to-do summaries, create, update, and move cards.
Kanban boards your AI agent can use: add, complete, move and recall tasks with your team.
Remote MCP for Kanban AI boards—manage projects, tasks, and comments from AI tools.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables users to manage Obsidian Kanban boards within a vault by listing, reading, and creating boards or tasks. It supports moving tasks between columns and adding detailed task metadata via the Obsidian Kanban plugin.51ISC
- FlicenseAqualityDmaintenanceEnables AI assistants to manage tasks across multiple projects with structured Markdown files, supporting creation, updates, completion, and organization with metadata and dependencies.7-
- AlicenseBqualityDmaintenanceEnables AI agents to manage kanban boards with tasks, including creating boards, items, moving between columns, and searching.81MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to manage Kanban tasks, boards, teams, and checklists via natural language, with full CRUD operations and live updates.231 npmMIT