task-queue-mcp
task-queue-mcp
エージェントオーケストレーションのタスクキューをMCPツールインターフェースとして公開するFastMCPサーバーです。エージェントは生のYAMLファイル書き込みではなく、型付けされ検証されたツールを通じてタスクの送信、ステータス確認、完了記録を行います。
Dockerコンテナとしてポート8485で実行されます。~/.claude.jsonにグローバルに配線され、すべてのClaude Codeエージェントセッションからアクセスできます。
ツール
ツール | 説明 |
|
|
| オプションのフィルタ付きでタスクを一覧表示する。TTL期限切れのタスクは除外される |
| UUIDで単一のタスクを取得する(アーカイブ済みタスクも解決する) |
| エージェント向けのステータス遷移(厳格)。履歴エントリを追記する |
| オペレーター向けのステータス変更 — 承認、キャンセル、パーク、または遅延タスクの進行(監査付きオーバーライド) |
| 古いタスクに対する正常な終端状態 |
| タスクを非表示にせず一時停止する — 一覧には残り、TTLの対象外となり、誰も取得しない |
| パークされたタスクを、パーク前のステータスに戻す |
| キュー内のタスクに修正を追記する。元の説明は決して書き換えられない |
エージェントは厳格な update_task パスを使用します。オペレーター(HTTP制御API経由)は
set_task_status / cancel_task / park_task / unpark_task を使用します。エージェントはキャンセルや
パークができません — どちらもオペレーター専用です。amend_task は例外です。タスクの 送信元 エージェントは
修正できますが、ターゲットエージェントはできません。
submit_task
submit_task(
source_agent="research",
target_agent="deploy-agent", # agent name or "auto" for dispatcher routing
# build | deploy | fix | research | review | audit | notify | docs |
# ticket_audit | ticket_audit_complete
task_type="build",
summary="Deploy qmd update",
description="Apply the qmd stack update from build plan...",
risk_level="low", # low | medium | high (default: low)
requires_approval=False, # explicit override of approval gate
priority="normal", # normal | high | urgent (default: normal)
context_refs=["/srv/agents/build-plans/qmd/plan.md"], # absolute paths only
ttl_days=30,
workflow_mode="semi-auto", # semi-auto | auto (default: semi-auto)
originating_task_id=None, # UUID of the parent task, if this is a return task
)
# → {"ok": true, "task_id": "<uuid>", "filename": "<timestamp>-<slug>.yml"}context_refs は絶対パスである必要があります。risk_level と priority は許可リストに対して検証されます。workflow_mode はディスパッチャーの動作を制御します。semi-auto(デフォルト)はオペレーターのピックアップ用にMatrix通知付きでタスクをキューに入れ、auto はディスパッチャーがターゲットエージェントをヘッドレスで起動します。サーバーはUUIDを生成し、created を設定し、retry_policy スタブを初期化します。
発信元タスクの自動クローズ(v0.6.0以降)
originating_task_id を渡すと、親タスクは completed としてクローズされます — 戻りタスクの送信がリクエストをクローズするのです。 応答には、発火時に auto_closed_task_id が含まれます。
これは以下のすべてが成立する場合にのみ発火します:
条件 | 理由 |
親タスクが解決され、アーカイブされていない | それ以外はクローズするものがない |
| 機能全体の制約 — エージェントAがエージェントBのタスクを親として指定してクローズできてはならない。ここで明示的にチェックされる。 |
| 戻り形状 のもう半分 — 尋ねた相手に答えなければならない。これがないと、転送 リクエストが戻りと同一に見える(下記参照) |
親が |
|
両方の半分が必要な理由(v0.6.1以降)。 originating_task_id は多重定義されています。戻りタスクでは「このリクエストへの回答」を意味しますが、転送 リクエストでは「この親から workflow_mode を継承する」ことを意味します — ビルドエージェントが自身の進行中のビルドに対して監査リクエストを提出するときに渡すものです。最初の条件だけをチェックするとこれらを区別できません。ビルドタスクはビルドエージェントをターゲットとし、ビルドエージェントが送信者だからです。v0.6.0は最初のチェックのみで出荷され、1時間以内に進行中のビルドタスクをクローズしてしまいました。
真の戻りは対称ですが、転送リクエストは対称ではありません:
親 | 新しいタスク | 発火するか? | |
戻り | 監査 |
| はい — 両方の半分が成立 |
転送リクエスト | ビルド | 監査リクエスト | いいえ — |
approved の親は最初に in-progress を経由して遷移するため、履歴はテレポートではなく「要求済み→クローズ済み」として読み取れます。
これはフェイルセーフであり、主要経路ではありません。エージェントは依然として自身のタスクを明示的にクローズすることが期待されます — これによりエージェント自身のメモが履歴に残ります。ここでは auto-closed: return task <id> submitted のみが書き込まれます。自動クローズ内の失敗は警告レベルでログに記録され、送信は正常に戻ります。副作用である送信を失敗させることは決してありません。
list_tasks
list_tasks(
target_agent="deploy-agent", # optional
source_agent="research", # optional
status="approved,in-progress", # comma-separated, optional
task_type="build", # optional
include_archived=False, # include archive/ subdirectory
limit=20, # max 200
)
# → list of task dicts, sorted by created descending認識されない status はエラーであり、空の結果ではありません(v0.6.0以降)。 以前は静かにフィルタリングされていました。そのため、ここでは決して存在しない status="pending" のスイープが数か月間 [] を返し、「あなたに仕事はありません」と区別がつきませんでした。空のリストは適切に形成された質問に対する正当な回答です。したがって、タイプミスと空のキューを区別する唯一の方法は、タイプミスを拒否することです。空白と末尾のカンマは依然として許容されます。空文字列はフィルタなしを意味します。
終端 状態のタスクで ttl_days を超えたものは除外されます。TTLアーカイブの権限はディスパッチャーにありますが、list_tasks は完了したレコードを積極的にフィルタリングして、エージェントが古い項目に基づいて行動しないようにします。
非終端タスクは決してTTLフィルタリングされません(v0.8.1、vikunja#395以降)。未完了の作業は、ttl_days 後に一覧から消えても、誰かを待ってディスク上に残っていました — ガードではなく盲点であり、すでにキュー掃引で、このツールが13と報告したところ17の取り残されたタスクが見つかる原因となっていました。まだ誰かの責任であるものを時計で隠すべきではありません。古い未完了タスクを渡されたエージェントはそれを判断できますが、見えないタスクに対して誰も行動できません。
パークされたタスクはTTLフィルタの対象外です。 パークは「これは一時停止して、後で戻る」という意図的なものです。パークされたタスクが静かに一覧から消えると、ステータスの意味が失われます。
get_task
get_task(task_id="a7f3d2c1-1234-5678-abcd-000000000000")
# → full task dict, or {"ok": false, "error": "not found"}最初にメインキューを検索し、次に archive/ を検索します。完全なUUIDが必要です。プレフィックス一致はありません。
update_task
update_task(
task_id="a7f3d2c1-1234-5678-abcd-000000000000",
status="in-progress", # see transition table below
actor="deploy-agent",
note="Claimed task, starting build.",
output=None, # written to result.output on completed/failed
)
# → {"ok": true, "task_id": "<uuid>"} or {"ok": false, "error": "..."}所有権チェック(v0.5.0以降): actor はタスクの target_agent と等しいか、"operator" である必要があります。それ以外のアクターは拒否されます。これにより、タスクが割り当てられたエージェント以外のエージェントがタスクを要求または完了できるというギャップが埋められます。
有効な遷移:
遷移元 | 遷移先 |
|
|
|
|
任意の非終端 |
|
非終端:submitted、pending-approval、approved、in-progress、parked、routing-failed。
終端:completed、failed、cancelled。
routing-failed はディスパッチャーが書き込むものであり、上記の「任意の非終端 → failed」行から意図的に除外されています。エージェントは、ディスパッチャーがまだ再試行しているタスクを終端状態に失敗させてはなりません。これは以下のオペレーター遷移(cancelled、parked、オーバーライド)の通常のソースです。
retry_policy はディスパッチャーが所有します — update_task は決して触れません。
オペレーター遷移(set_task_status)
update_task よりも広いですが、依然として監査され制限されています:
遷移元 | 遷移先 | 注記 |
|
| 標準 |
任意の非終端 |
| 標準( |
任意の非終端 |
| 標準( |
任意の非終端 | 任意の非終端 |
|
任意の 認識されない ステータス | 任意の有効なステータス |
|
終端タスクはオペレーターにとっても不変です。すべてのオペレーター変更は、actor + note を含む履歴エントリを追記します。
修復パス は、キューディレクトリに複数の書き込み者がいるために存在します。このサーバーの語彙に完全に含まれないステータスを持つレコード — 過去の complete タイプミスや、ここでまだ許可されていない将来のディスパッチャーステータス — は、他のすべての分岐から到達不能であり、永久にスタックしてしまいます。修復は常にタスクを無効なステータス から 移動させるだけです。ターゲットは依然として有効である必要があり、履歴エントリには repaired_from が記録されます。routing-failed はこのパスを必要としなくなりました — 現在は第一級の非終端ステータスであり(上記参照)、標準の cancelled/parked 行または通常のオーバーライド行から到達可能です。
park_task / unpark_task
park_task(task_id="...", actor="operator", note="waiting on upstream fix")
# → {"ok": true, "task_id": "<uuid>"}
unpark_task(task_id="...", actor="operator", status=None)
# → returns the task to the status it was parked fromパークはステータスのみを変更します — YAMLは移動しません。タスクは list_tasks に表示され続け、TTL期限切れの対象外であり、誰も取得しません。ディスパッチャーのピックアップループは submitted と routing-failed のみを一致させるためです。以前のステータスは parked_from に記録され、解除時にクリアされるため、古くなることはありません。unpark_task に status を渡すと、タスクを元の場所以外に送信できます — 直接YAML書き込み者によってパークされたタスク(parked_from を持たない)に必要です。
パークは「今はやらないが、失いたくない」ためのものです。長期間アイドル状態のタスクは必ずしも放置を意味しません。parked は、意図的なブックマークと本当に放棄されたものを区別する語彙です。
amend_task
amend_task(
task_id="...",
amendment="Preflight answered the open question — FastMCP mount() is live-linked.",
actor="research", # the task's source_agent, or "operator"
reason="preflight ran after queuing",
)
# → {"ok": true, "task_id": "...", "amendment_count": 1, "agent_may_have_started": false}タスクがキューに入ると、その説明は不変です。キューイングから開始までの間に何かが変わった場合 — 事前チェックが未解決の質問に答えたり、依存関係が到着したり、レビュー担当者がエラーを見つけたり、スコープが縮小したり — 修正の行き場がなく、タスクの説明を信頼するエージェントは間違ったことを行います。
amend_task はそのギャップを追記専用で埋めます。payload.description は決して変更されません。修正は payload.amendments の下に {timestamp, actor, reason, text} として蓄積され、リーダーは説明の後にそれらをレンダリングします。タスクが元々要求したものは記録に残ります。
ルール | 動作 |
修正できる者 | タスクの |
時期 |
|
in-progress | 許可される — 最も重要なケースである — ただし、エージェントがすでに元の指示を読んでいる可能性があるため、レスポンスは |
上限 | タスクあたり10件の修正、各4096文字。 |
スコープ拡大のガイドライン: タスクに対する修正が1〜2件を超える場合は、積み上げるのではなくキャンセルして再キューするシグナルである。上限はバックストップであって、予算ではない。
Related MCP server: MCP Task Assistant
ステータスライフサイクル
submitted → [pending-approval] → approved → in-progress → completed
↓
failed
routing-failed # dispatcher-written on a failed dispatch attempt; non-terminal
Any non-terminal ──(operator)──> cancelled # graceful dismissal, record kept
Any non-terminal <──(operator)──> parked # pause; stays listed, TTL-exemptディスパッチャーはsubmitted → approved/pending-approval遷移を担い、ディスパッチ試行が失敗したときはrouting-failedも書き込む(独自のスケジュールで再試行する。オペレーターはset_task_statusを介してキャンセル、パーク、または別の場所で強制することもできる)。エージェントはapproved → in-progress → completed(またはfailed)を担う — routing-failedはupdate_taskからは到達できない。オペレーターはcancelled、parked、および監査対象のステータスオーバーライドを担う。承認ゲーティングはエージェントマニフェストとrequires_approvalフィールドによって制御される。
すべてのタスクは、そのタスク自身の対象エージェントによってクローズされる。 これはupdate_taskの所有権チェックから導かれ、新しいクロスエージェントワークフローを配線するときに覚えておくべき唯一のルールである: リクエストを送信したエージェントはそれをクローズできない。なぜなら、そのリクエストは他の誰かを対象としているからである。したがって、リクエスト/リターンのペアは、受信側エージェントが自身のエントリをクレームしてクローズする必要がある — completedはin-progressからのみ到達可能なので、2回の呼び出しが必要である。自動クローズは、それが行われない場合のフェイルセーフであり、その代替ではない。
HTTP制御API
MCP以外のクライアント(CloudCLIプラグインとMatrixボット)はPythonコアをインポートできないため、それらのすべての変更は、同じポート8485にFastMCPカスタムルートとしてマウントされた薄いHTTP制御APIを経由する。各エンドポイントは上記のツールハンドラに委譲し、遷移検証、fcntlロック、アトミック書き込みを継承する — つまり、システム全体で検証済みの書き込みパスは正確に1つだけである。
メソッド | パス | 委譲先 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| アクティブなキュー全体のステータス別カウント |
ボディフィールド: note、さらにステータスルートではstatus / allow_override、amendではamendment / reason、updateではstatus / output / on_behalf_of。レスポンスは正規の結果にマッピングされる: 200 OK、404 未検出、400 検証/遷移エラー。
actorはこれらのすべてのルートでoperatorに固定され、ボディからは読み取られない(v0.8.0以降)。以前はbody.get("actor", "operator")だった — 実際には正しかったが、オペレーターのアイデンティティを、誰かが選択するものではなく、呼び出し元が省略によって継承するものにしていた。固定により、将来ここに現れる非オペレーターのクライアントが、すべての所有権チェックが免除するアイデンティティを静かに取得することはできない。
オペレータースイープ — POST /tasks/{id}/update
別のエージェントのタスクに対する終端遷移への唯一のパス。これはv0.8.0が不正なバージョンを閉じたために存在する: エージェントは、そのエージェントの名前をactorとして渡すことで、取り残されたタスクを整理していたが、actorをベアラートークンにバインドすることでそれが排除される。それ以外のものはそこに到達できない — set_task_statusは終端遷移を作成できず、update_taskツールは解決されたアイデンティティを要求する — したがって、これがなければ、すべての迷子タスクはオペレーターが手動で介入する必要がある。
そのタスクの持ち主であるエージェントを指定するon_behalf_ofを渡す。ハンドラはそれをタスクの実際のtarget_agentと照合する(不一致は400になる。誤って識別したタスクをクローズするオペレーターには、その間違いを意図的なものとして記録するのではなく、伝えるべきだからである)そして両方の名前を履歴に書き込む:
history:
- timestamp: ...
status: completed
actor: operator
on_behalf_of: developer
note: "stranded; swept during queue cleanup"スイープは何年後にもスイープとして読めるべきであり、エージェントが静かに自身の作業をクローズしたようには読めない。on_behalf_ofは任意である — 省略するとオペレーターが自身の名前で行動することになる — そしてoperator以外のactorには完全に拒否される。
GET /queue/summaryは{"ok": true, "counts": {...}, "active": N, "total": N}を返す。ここでactiveは非終端の合計(現在はrouting-failedも名前でカウントに含まれる)である。サーバーの語彙に完全に含まれないステータスは破棄されるのではなく"unknown"のバケットに分類されるため、他の直接YAML書き込みによって記録されたレコードもカウントに表示され続ける。
認証: カスタムルートはトランスポートのベアラー認証をバイパスするため、共有シークレットヘッダーがゲートとなる — そしてこれらのルートは意図的にその外側にある。なぜなら、それらはオペレーター向けのサーフェスだからである:
すべての変更で
X-Task-Queue-Secret: $TASK_QUEUE_API_SECRETを送信する。サーバーはそれを定数時間(
hmac.compare_digest)で比較し、シークレットが欠落、誤り、または未設定の場合はフェイルクローズ(401)する。シークレットはリポジトリ外のオペレーター管理のenvファイルにあり、
env_file経由でコンテナと各クライアントの環境に注入される — ソースにコミットされることは決してない。
デプロイメント
Docker(本番)
services:
task-queue-mcp:
image: task-queue-mcp:latest
container_name: task-queue-mcp
ports:
# The loopback bind is load-bearing, not cosmetic. The MCP transport on this port
# is unauthenticated (see Trust model below), so publishing it as "8485:8485"
# would expose an unauthenticated queue-mutation endpoint to your whole LAN.
- "127.0.0.1:8485:8485"
volumes:
- ~/.claude/task-queue:/task-queue # host queue directory
environment:
- TASK_QUEUE_DIR=/task-queue
# 0.0.0.0 here is the *container-internal* bind and must stay wide, or the port
# mapping above has nothing to forward to. The host-side bind is what limits reach.
- MCP_HOST=0.0.0.0
- MCP_PORT=8485
cap_drop: [ALL]
security_opt: [no-new-privileges:true]
read_only: true
tmpfs: [/tmp]
user: "1000:1000"
restart: unless-stopped
networks:
- agent-netコンテナはタスクキューディレクトリのみを読み書き可能でマウントする。ファイルシステムの残りは読み取り専用である。/tmpは一時的なスクラッチ領域用のtmpfsである。
Claude Code settings.json
{
"mcpServers": {
"task-queue-mcp": {
"type": "url",
"url": "http://localhost:8485/mcp"
}
}
}環境変数
変数 | デフォルト | 説明 |
|
| コンテナ内のタスクキューディレクトリへのパス |
|
| HTTPサーバーのバインドホスト |
|
| HTTPサーバーのポート |
| — | HTTP制御APIの共有シークレット。制御APIの変更には必須 — 未設定の場合はフェイルクローズ(401)する。MCPツール自体はこれを使用しない。 |
| — | 1つの呼び出し元エージェントのベアラートークン(例: |
各エージェントには独自のトークンが必要である — トークンが呼び出し元を識別するものであり、2つのエージェント間で共有すると帰属が無意味になる。サーバーは、共有トークン、空の値、16文字未満のトークン、または予約済みのoperatorアイデンティティ用に発行されたトークンでは起動を拒否する。以下で生成する:
python -c "import secrets; print(secrets.token_urlsafe(32))"呼び出し元は標準のベアラーヘッダーとして提示する:
headers:
Authorization: "Bearer ${TASK_QUEUE_TOKEN}"ビルド
docker build -t task-queue-mcp:latest .開発
Python 3.11+が必要。
pip install -e ".[dev]"
# Lint + format (Baseline gate)
ruff check .
ruff format --check .
# Tests with coverage (gate: >=80%)
python -m pytest --cov=src --cov-report=term-missing
# Run server locally against a local task-queue directory
TASK_QUEUE_DIR=~/.claude/task-queue python -m src.serverテストスイートはすべてのツールとHTTP制御APIをカバーする — 検証のエッジケース、敵対的なYAML文字列、不正な遷移、パーク/アンパークのラウンドトリップ、amend_taskの認可(拒否される対象エージェントを含む)、オペレーターオーバーライドの監査、語彙外ステータスの修復、共有シークレットゲート(欠落/誤ったシークレット→401)。すべての書き込みはyaml.dumpを使用する — 文字列補間は決して使用しない — YAMLインジェクションを防ぐためである。
セキュリティ
ポート8485の両方のサーフェスは資格情報を必要とする:
MCPツールパス(
/mcp)— エージェントごとのベアラートークン。FastMCPのStaticTokenVerifierによって検証される。欠落または不明なトークン→401。トランスポートはトークンが設定されていないと起動を拒否するため、静かにフェイルオープンすることはできない。HTTP制御ルート(
/tasks/...、/queue/summary)— 共有シークレットヘッダー(X-Task-Queue-Secret、定数時間比較、フェイルクローズ)。HTTP制御APIを参照。
コンテナはUID 1000でcap_drop: ALL、no-new-privileges、読み取り専用のrootfs(書き込み可能なのは/task-queueのみ)で実行される。
信頼モデル
v0.7.0までMCPツールパスは未認証であり、READMEはループバックが十分な信頼境界であると主張していた。それは正しくなかった: ポートは公開されており*、かつ*コンテナは共有Dockerネットワークに参加しているため、そのネットワーク上のすべてのコンテナがツールパスにも到達できた。それらのいずれも、任意のactor — 所有権チェックが明示的に免除するoperatorを含む — を主張しながらset_task_status、cancel_task、park_task、unpark_task、またはamend_taskを呼び出すことができた。これにより、completed_byとhistory[].actorは証拠ではなく主張になった。(vikunja#387)
v0.7.0はその経路を閉じる。各エージェントは個別のトークンを保持するため、トークンは呼び出し元を認証すると同時に識別する。意図的に別のアイデンティティヘッダーはない: エージェントがトークンを保持すると、直接リクエストで任意のヘッダーを設定できるため、ヘッダー由来のアイデンティティはトークン由来のものと競合する厳密に弱い第二のチャネルになる。アイデンティティのソースは1つであり、2つではない。
これが得られるものと得られないもの。 これは、誤った、またはプロンプトインジェクションされたエージェントが自身のツールサーフェスを介して動作することを含み、監査証跡がその意味するところを正確に示すようにします。これは、資格情報を探し回るエージェントに対する境界では意図的にありません。エージェントがシェルツールを持ち、シークレットファイルを所有する同じOSユーザーとして実行される場合、ホスト上の任意のトークンはそれらのいずれでも読み取ることができます。それを閉じるには、エージェントごとのOSユーザーまたは資格情報ブローカーが必要であり、このサーバーの範囲外です。
operator アイデンティティは、HTTPコントロールルートからのみ到達可能です。TASK_QUEUE_TOKEN_OPERATOR は起動時に拒否されます。なぜなら、operator はすべての所有権チェックから免除されており、エージェント向けトランスポートでそれを発行するトークンは、保持者にキュー全体を渡すことになるからです。
アイデンティティバインディング (v0.8.0以降)
actor は、呼び出し元から取得されるのではなく、ベアラートークンから導出されます。認証されたアイデンティティと一致しない名前を渡すと、黙って修正されるのではなく拒否されます。呼び出しの間違った名前は、表面化させる価値のあるバグです。省略しても問題ありません。トークンから自動的に埋められます。
これは submit_task の source_agent にも適用されます。これは単なるラベルではなくアイデンティティの主張です。送信時の自動クローズは source_agent/target_agent から発火するかどうかを決定するため、それを偽装すると、update_task を呼び出すことなく別のエージェントのタスクを終了させてしまいます。
Tool | 呼び出し可能な主体 |
| 認証された任意のエージェント( |
| タスクの |
| タスクの |
| タスクの |
| operator のみ — エージェントアイデンティティは拒否 |
set_task_status は operator のみが使用できます。その allow_override パスはタスクを任意の2つの非終端ステータス間で移動させるため、タスクが遷移ルールを満たす代わりに迂回する方法となります。cancel_task は他人の作業に対する終端的で取り消し不可能な判断です。エージェントが自身のタスクを放棄する場合は、update_task を介して理由を付けて failed とマークします。
タスクファイルスキーマ
タスクは ~/.claude/task-queue/ 内のYAMLファイルで、YYYYMMDD-HHMMSS-<uuid-prefix>.yml という名前です。すべての書き込みはアトミックです(.tmp に書き込んでから os.rename() します)。fcntl.flock によるタスクごとのファイルロックは、並行するMCP呼び出しとディスパッチャー間の競合を防ぎます。
完全なスキーマとライフサイクルのドキュメントについては、homelab-agent コンポーネントドキュメント を参照してください。
関連
homelab-agent — エージェントオーケストレーションのドキュメント
task-dispatcher — タスクをルーティングおよびゲートするディスパッチャー
This server cannot be deployed
Maintenance
Related MCP Connectors
Project management MCP for AI agents with safe task reads and writes.
Local-first task manager: create, edit, and complete tasks, projects, and checklists via MCP.
- DazbenchOAuthapp.dazbench
Task management your AI agents can actually run. One line becomes a context-ready task over MCP.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- AlicenseAqualityFmaintenanceModel Context Protocol server for Task Management. This allows Claude Desktop (or any MCP client) to manage and execute tasks in a queue-based system.10864 npm216MIT
- FlicenseNot gradedqualityDmaintenanceExposes task management (add, list, complete tasks) and document search (RAG) as MCP tools for AI agents.-
- FlicenseNot gradedqualityCmaintenanceExposes a FastAPI task management REST API as MCP tools, enabling an LLM client to list, create, get, complete, and delete tasks via natural language.-
- AlicenseAqualityAmaintenanceEnables AI agents to manage tasks on a local-first board via MCP, exposing task creation, updates, and queries through a thin adapter over the REST service.7MIT