关于 Task tracking (sessions, ETA, dashboard)
面向 Claude、Lyra 等 MCP 客户端的本地长任务跟踪服务,提供会话进度、速度、预计剩余时间和终端仪表盘。还可通过本地 HTTP API 及轮询器共享任务状态,并自动跟踪 qBittorrent 和 Bazarr。
主要功能
- 01跟踪任务会话、步骤与日志
- 02计算速度、预计剩余时间和停滞状态
- 03提供本地 HTTP API 与终端仪表盘
- 04通过轮询器跟踪 qBittorrent 和 Bazarr
- 05使用本地 JSON 文件保存状态
适用场景
- 01查看下载或转换任务的实时进度
- 02让 Agent 启动任务后稍后检查状态
- 03发现长时间没有更新的运行会话
- 04在仪表盘中筛选和管理任务会话
MCP 相关
作为 MCP 服务,为客户端提供长任务会话的跟踪和管理工具。会话状态也能通过本地 HTTP API 与脚本及轮询器共享。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
12收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| pid | any | 否 | |
| name | string | 是 | |
| unit | string | 否 | |
| extra | any | 否 | |
| items | any | 否 | |
| total | number | 是 | |
| template | string | 是 |
详细介绍
MCP Tracking
英文摘要。 以本地为先的长时任务跟踪:一个供 Claude 或 Lyra 使用的 MCP 服务、一个运行于 127.0.0.1:8765 的小型 HTTP API,以及一个 Textual 终端仪表板。会话包含项目、进度、日志和模板(download、machine、free、lyra_task、movie);轮询器会自动为 qBittorrent 和 Bazarr 会话提供数据。安装:pip install .,然后运行 mcp-tracking、mcp-tracking-api、mcp-tracking-ui。无需云服务,也无遥测。
实时跟踪 MCP 服务,附带终端仪表板。 允许 Claude/Lyra 跟踪任意长时操作,并从 media-server(qBittorrent、Bazarr、DV 转换)自动为会话提供数据。
为何专为大模型 Agent 设计
启动长时任务(转换、克隆 VM、下载)的大模型 Agent 无法一直等待任务完成,也看不到两轮交互之间发生的事情。此服务为其提供共享的工作记忆:
- 共享的进度总线。 Agent、Shell 脚本和轮询器都写入相同的会话(MCP 工具或 127.0.0.1:8765 上的本地 HTTP API):Agent 启动任务、交出控制权,之后再通过
tracking_get读取状态。 - 计算得出的指标,而非猜测。 2 分钟平滑速度、预计剩余时间、已用时间(
metrics.py):Agent 获取的是具体数据,无需自行推算。 - 无响应的任务一目了然。
running会话若 10 分钟没有更新,就会标记为stale:Agent 可以区分“仍在运行”和“进程已终止”。 - 停止时针对正确的进程。 会话可以记录其进程的
pid;从/proc读取的启动时间可避免误报被其他程序复用的 pid。tracking_stop发送 SIGTERM 并保留会话,tracking_kill发送 SIGKILL 并删除会话。 - 不会丢失更新。 状态存储在本地 JSON 文件中,由
flock锁保护并以原子方式重写(storage.py):多个 Agent 和脚本可以同时写入。 - 按任务类型提供模板(
download、machine、movie、lyra_task、free),让每个 Agent 都能以相同方式描述任务。
以本地为先且可自托管:无需云服务、无遥测,所有数据都保留在本机(on-prem)。Lyra(链接)这个法语优先的语音助手,以及任意 MCP 客户端(Claude Code、Claude Desktop 等)都在使用。
演示
![]()
通过 docs/demo/record.sh 录制:server.py --test 在临时状态目录(TRACKING_STATE_DIR)中填充四个模拟会话,然后 server.py --ui 在该目录上打开仪表板。不会影响真实会话。
目录
架构
MCP/tracking/
server.py -- Serveur MCP (outils Claude/Lyra) + point d'entree --ui / --test
api.py -- API HTTP locale (127.0.0.1:8765) pour les scripts externes
mutations.py -- Mutations d'une session, partagees par api.py ET server.py
(horodatage items, historique, niveaux de log, auto-completion)
metrics.py -- Metriques derivees (vitesse, ETA, ecoule, stale) -- logique pure,
calculees a la lecture, jamais stockees
storage.py -- Persistence JSON atomique + verrou fichier + cache mtime + purge TTL
models.py -- Modeles pydantic (TrackingSession, TrackingItem, LogEntry, ProgressPoint)
templates.py -- Templates builtin + templates utilisateur (JSON)
ui.py -- Dashboard Textual (TUI temps reel) + modales stop/kill
sim.py -- Simulations de demo (server.py --test)
poller.py -- Daemon polling qBittorrent (10s) + Bazarr (60s)
tracking-api.service -- Unite systemd (systeme) pour api.py
tracking-poller.service -- Unite systemd (systeme) pour poller.py
install.sh / deploy.sh -- Installation initiale / redeploiement des services
Makefile -- make test | smoke | deploy | ui
tests/ -- unitaires (storage, metrics) + integration/ (API HTTP reelle)
状态和配置文件
| 文件 | 位置 | 覆盖项 |
|---|---|---|
tracking_state.json | ~/.local/state/tracking/ | TRACKING_STATE_DIR |
poller_state.json | ~/.local/state/tracking/ | TRACKING_STATE_DIR |
templates.json(用户模板,可选) | ~/.config/tracking/ | TRACKING_TEMPLATES_FILE |
credentials/*.cred(qBittorrent、Bazarr) | 与代码位于同一目录,已加入 gitignore | -- |
与代码位于同一目录的旧版 tracking_state.json 会在首次启动时自动迁移(复制,不会删除)。
保留期限环境变量:
| 变量 | 默认值 | 作用 |
|---|---|---|
TRACKING_TTL_DAYS | 7 | 清理已完成 / 出错 / 已暂停的会话 |
TRACKING_TTL_RUNNING_H | 24 | 清理已失联的运行中会话(不再更新) |
完整数据流
Claude/Lyra (outils MCP)
|
v
server.py ─────────────────────────────────────────┐
|
qBittorrent API (poll 10s) |
| |
Bazarr API (poll 60s) ──> poller.py ──> api.py ──> mutations.py ──> storage.py ──> ~/.local/state/tracking/tracking_state.json
| | |
dv_webhook_server.py | v
| | ui.py
v | (rafraichit chaque seconde)
dv_convert.py ──────────────────────────────────────>
(metriques temps reel ffmpeg/dovi_tool)
状态文件在每次修改时都会通过原子写入(os.replace)并在文件锁(tracking_state.lock)保护下写入。所有进程(MCP、API、轮询器、仪表盘)共用这一个文件;每次读取时都会检查 mtime,以使缓存失效。
所有变更操作(HTTP 或 MCP)都会经过 mutations.py,确保两种路径的行为一致:为条目和会话设置 started_at / finished_at,记录进度历史(40 个点的滑动窗口),设置 info / warn / error 日志级别,并在所有条目都完成时自动完成会话。
派生指标
GET /sessions 和 tracking_get 会返回一个由 metrics.py 即时计算的 metrics 块:
| 字段 | 含义 |
|---|---|
percent | 进度(最高为 100) |
rate、rate_str | 最近 120 秒的速度(2.0 MB/s、30.0 u/min) |
eta_seconds、eta_str | 预计剩余时间(仅适用于运行中的会话) |
elapsed_seconds、elapsed_str | 从 created_at 到 finished_at 或当前时间的时长 |
idle_seconds、stale | stale = 运行中但 10 分钟没有更新(显示在 TUI 中) |
一行安装
uvx mcp-tracking # serveur MCP (stdio) ; avant publication : uvx --from git+https://github.com/amineutron/mcp-tracking mcp-tracking
uvx --from mcp-tracking mcp-tracking-api # API HTTP 127.0.0.1:8765
uvx --from mcp-tracking mcp-tracking-ui # tableau de bord terminal
Claude Desktop / Claude Code 配置(mcpServers):
{ "tracking": { "command": "uvx", "args": ["mcp-tracking"] } }
安装
cd <dossier du dépôt>
# Creer le venv et installer les dependances
uv venv .venv
uv pip install "mcp[cli]>=1.0.0" "pydantic>=2.0" "textual>=0.80.0" "fastapi"
MCP 已在 Claude Code 中注册(用户范围):
claude mcp list # -> tracking: Connected
重新注册:
claude mcp add tracking -s user -- \
<dossier du dépôt>/.venv/bin/python \
<dossier du dépôt>/server.py
systemd 服务
两个服务持续运行,并在系统启动时启动:
| 服务 | 作用 | 端口 |
|---|---|---|
tracking-api.service | 为外部脚本提供本地 HTTP API | 127.0.0.1:8765 |
tracking-poller.service | 轮询 qBittorrent(10 秒)+ Bazarr(60 秒) | -- |
初次安装和重新部署
cd <dossier du dépôt>
./install.sh # premiere fois : venv + services (demande sudo)
sudo ./deploy.sh # apres chaque mise a jour du code : stop, unites, restart, verif
make smoke # sante rapide
已由 Claude Code 会话打开的 MCP server.py 实例不会被 deploy.sh 重启:在这些会话中通过 /mcp 重新连接 tracking。
常用命令
# Etat
systemctl status tracking-api.service tracking-poller.service
# Logs en direct
journalctl -fu tracking-poller.service
journalctl -fu tracking-api.service
# Redemarrage
sudo systemctl restart tracking-api.service tracking-poller.service
# Test API
curl http://127.0.0.1:8765/health
curl http://127.0.0.1:8765/sessions
启动
仪表盘(wofi 快捷方式)
在 wofi/launcher 中搜索“MCP Tracking”。仪表盘将在 Kitty 中启动。
仪表盘(终端)
# Toutes les sessions
<dossier du dépôt>/.venv/bin/python \
<dossier du dépôt>/server.py --ui
# Filtre direct au lancement
.venv/bin/python server.py --ui --filter download
.venv/bin/python server.py --ui --filter movie
.venv/bin/python server.py --ui --filter errors
通过 MCP 工具(从 Claude/Lyra)
open_tracking_ui() # toutes les sessions
open_tracking_ui(filter_template="lyra_task") # vue Lyra uniquement
open_tracking_ui(filter_template="errors") # erreurs uniquement
测试模式(演示)
.venv/bin/python server.py --test
并行模拟 4 个会话:下载、机器(12 个节点)、free、电影(完整 DV 流程)。
仪表盘
会话布局
[TEMPLATE] Nom de la session id:xxxxxxxx (status)
[=============> ] 54.2% 27100 MB / 50000 MB
champ_extra1: valeur | champ_extra2: valeur
[ok] item-1 100.0 GB -- termine
[>] item-2 frame: 94231 / 172800 (54.5%) speed: 3.2x
[ ] item-3 --
[!] item-4 erreur detail
Logs Erreurs
14:32:01 Message log 1 [!] item-4
14:32:04 Message log 2 14:32:08 ECHEC: details
14:32:07 Message log 3 --
-- --
-- --
条目图标
| 图标 | 状态 | 颜色 |
|---|---|---|
[ ] | 待处理 | 灰色 |
[>] | 运行中 | 青色 |
[ok] | 已完成 | 绿色 |
[!] | 出错 | 红色 |
会话颜色
| 颜色 | 状态 |
|---|---|
| 青色 | 运行中 |
| 绿色 | 已完成 |
| 红色 | 出错 |
| 黄色 | 已暂停 |
键盘快捷键
| 按键 | 操作 |
|---|---|
f | 下一个筛选条件(按模板动态循环) |
e | 切换为仅显示错误的筛选条件 |
r | 手动刷新 |
s | 正常停止会话(输入 ID)-> 状态为 paused |
k | 强制终止会话(输入 ID)-> 删除 |
q | 退出 |
| 方向键 / 滚轮 | 滚动 |
停止/终止模态框
按 s 或 k 会打开一个模态框,其中有一个会话 ID 输入框。
s将会话标记为paused并添加一条日志k从仪表板中永久删除会话Echap取消
动态筛选
筛选条件循环会根据当前存在的会话自动构建:
all -> download -> free -> movie -> lyra_task -> errors -> all -> ...
all始终存在- JSON 中存在的每个模板都会自动添加
- 只有至少有一个会话出错时才会显示
errors - 活动筛选条件显示在副标题中:
filtre: movie | 2/5 session(s) - 如果筛选的模板从 JSON 中消失,则自动返回
all
与 media-server 集成
qBittorrent(自动)
轮询器每 10 秒查询一次 http://localhost:8080/api/v2/torrents/info。
- 一个活动种子 = 一个
[DOWNLOAD]会话,包含名称、大小、速度和预计剩余时间 - 种子完成或消失后,会话会自动删除
- 凭据:
credentials/qbt-password.cred(使用systemd-creds --user加密,由media-server/scripts/secrets/rotate-secrets.sh生成)
Bazarr 缺失字幕(自动)
轮询器每 60 秒查询一次 Bazarr API。
- 一个唯一的
[SUBTITLES]会话列出所有缺少法语字幕的剧集/电影 - 会话标题显示总数:
Sous-titres manquants (151) - 前 50 个缺失文件会列为条目
- Bazarr API 密钥:
credentials/bazarr-api-key.cred(相同机制)。没有凭据时,相关轮询会直接禁用。
Dolby Vision 转换(自动)
Radarr/Sonarr 导入 Dolby Vision Profile 4 或 7 的电影时,由 dv-webhook.service 触发。
流程:
Radarr/Sonarr import
|
v
dv_webhook_server.py (port 8787)
|-- cree session tracking via api.py
|-- passe DV_TRACKING_SESSION_ID en env
v
dv_convert.py
|-- 6 etapes avec metriques temps reel
|-- ffmpeg : frame / speed / size / time (parse stderr)
|-- dovi_tool: frames X/Y ou X% (parse stderr indicatif)
v
session tracking completee ou en erreur
跟踪的 6 个步骤及其指标:
| 步骤 | 工具 | 显示的指标 |
|---|---|---|
| 1/6 提取 HEVC | ffmpeg | 帧 / 速度 / 大小 / 时间 |
| 2/6 解复用 BL/EL | dovi_tool | 帧 X/Y (%),bl:X GB,el:X GB |
| 3/6 提取 RPU + 转换为 P8 | dovi_tool | 帧 X/Y (%),RPU:X KB |
| 4/6 将 RPU P8 注入 BL | dovi_tool | 帧 X/Y (%),P8 HEVC:X GB |
| 5/6 重建时间戳 | ffmpeg | 帧 / fps / 大小 |
| 6/6 最终 MKV 重新封装 | ffmpeg | 帧 / 速度 / 大小 |
每个步骤执行期间,全局进度条会持续前进 (不会在每个步骤结束时才按 1/6 跳跃)。
手动模式:
# Fichier unique
python <media-server>/scripts/dv_convert.py /chemin/film.mkv
# Scan dossier
python <media-server>/scripts/dv_convert.py --scan /mnt/media/media/movies
在手动模式下,跟踪会话会在 process_file 中自动创建。
本地 HTTP API(端口 8765)
外部脚本可以直接创建/修改会话:
# Creer une session
curl -X POST http://127.0.0.1:8765/sessions \
-H "Content-Type: application/json" \
-d '{"name":"Mon operation","template":"free","total":100,"unit":"%"}'
# -> {"id": "a1b2c3d4"}
# Mettre a jour
curl -X PUT http://127.0.0.1:8765/sessions/a1b2c3d4 \
-H "Content-Type: application/json" \
-d '{"processed":45,"log":"Etape 2/5 en cours","extra":{"phase":"etape 2"}}'
# Mettre a jour un item
curl -X PUT http://127.0.0.1:8765/sessions/a1b2c3d4 \
-H "Content-Type: application/json" \
-d '{"item":{"name":"mon-item","status":"done","note":"100 frames speed: 2x"}}'
# Supprimer
curl -X DELETE http://127.0.0.1:8765/sessions/a1b2c3d4
# Lister
curl http://127.0.0.1:8765/sessions
完整 PUT 请求体(所有字段均为可选):
{
"processed": 45.0,
"total": 100.0,
"status": "running",
"extra": {"phase": "etape 2"},
"log": "message de log",
"item": {
"name": "nom-de-l-item",
"status": "running",
"note": "metriques ici",
"processed": 50.0,
"total": 100.0
}
}
MCP 工具
tracking_create
Parametres:
name (str) Nom de la session
template (str) "download" | "machine" | "free" | "movie" | "lyra_task" |
"subtitles" | "series_episode" | "series_season" | template utilisateur
total (float) Valeur totale
unit (str, opt) Unite affichee (ex: " MB", " machines", "%")
items (list, opt) Liste d'elements a suivre
extra (dict, opt) Champs specifiques au template
Format items:
[{"name": "fichier.iso", "total": 5100, "unit": " MB", "note": "info"}]
Retourne: ID de session + etat initial formate
tracking_update
Parametres:
session_id (str) ID de la session
processed (float, opt) Nouvelle valeur de progression
message (str, opt) Message de log
item_updates (list, opt) Mises a jour des items
extra (dict, opt) Champs extra a merger
Format item_updates:
[{"name": "item-1", "status": "done", "processed": 1200, "note": "detail"}]
Status: "pending" | "running" | "done" | "error"
tracking_log
添加日志,但不修改进度。
Parametres:
session_id (str)
message (str)
tracking_complete
将状态标记为 done,进度设为 100%。
Parametres:
session_id (str)
message (str, opt)
tracking_error
标记为出错(自动添加前缀 “ERREUR:”,并显示在“错误”列中)。
Parametres:
session_id (str)
message (str)
tracking_stop
正常停止会话(状态 -> paused)。会话仍会显示在仪表板中。
Parametres:
session_id (str)
message (str, opt)
tracking_kill
强制删除会话。会话会立即从仪表板中消失。
Parametres:
session_id (str)
tracking_get
返回格式化的会话完整状态。
tracking_list
Parametres:
template (str, opt) Filtrer par template
status (str, opt) Filtrer par statut ("running", "done", "error", "paused")
tracking_delete
删除会话(等同于 tracking_kill)。
tracking_templates
显示模板列表及其字段。
open_tracking_ui
在 Kitty 终端中打开仪表板。
Parametres:
filter_template (str, opt) Template a afficher au lancement
模板
download
文件下载。由 qBittorrent 通过轮询器自动填充。
Champs extra : speed, eta
Unite par defaut : MB
machine
机器操作(更新、克隆、快照、部署)。 Lyra 用于 VM/集群操作。
Champs extra : operation, target
Unite par defaut : machines
free
自由格式。轮询器用于查找缺失的 Bazarr 字幕。
Aucun champ extra impose, aucune unite par defaut.
lyra_task
Lyra 操作(VM 克隆、备份、更新、快照)。
Champs extra : operation, target, phase, eta
Unite par defaut : %
movie
完整的电影流水线:下载 -> Dolby Vision 转换。 当 Radarr/Sonarr 导入 DV P4/P7 文件时,由 dv_convert.py 自动填充。
Champs extra : phase, quality, codec, audio, source, dv, speed, eta
Unite par defaut : %
Les 6 etapes DV trackees avec metriques temps reel :
"1/6 extraction HEVC"
"2/6 demux BL/EL"
"3/6 extraction RPU + conv P8"
"4/6 injection RPU P8 dans BL"
"5/6 reconstruction timestamps"
"6/6 remuxage MKV final"
安全
威胁模型
桌面机器上两种现实的攻击者:
- 在浏览器中打开的网页。“localhost 不是安全边界”:网页可以向
127.0.0.1:8765发送请求。没有保护的话,它可能创建或删除会话,尤其是请求向某个进程发送信号。 - 另一个本地用户(或遭到入侵的服务),试图读取状态或控制 API。
措施
api.py仅监听127.0.0.1:8765——无法从网络访问- 写入时必须提供本地令牌:首次启动时在
$XDG_RUNTIME_DIR/tracking/token中生成(权限为 0600,因此其他用户无法读取),对 POST、PUT 和 DELETE 请求要求通过Authorization: Bearer ...提供。网页无法读取该令牌。 - 拒绝浏览器请求:任何
Origin标头都会导致 403,即使提供了令牌也一样。 - 验证
Host标头(仅限回环地址),写入时必须提供Content-Type: application/json。 - 信号仅可发送给由服务注册的进程:创建会话时,服务会验证所声明的
pid是否存在且属于同一用户,然后记录其启动时间。若进程指纹已更改(进程编号被其他程序重用),或者该 pid 从未注册过,/stop和/kill都会拒绝操作。无法再通过PUT修改pid。每次发送的信号都会记入日志。 - 强化 systemd 服务:
ProtectSystem=strict、ProtectHome=read-only(仅状态目录可写)、PrivateTmp、SystemCallFilter=@system-service、空的CapabilityBoundingSet=、UMask=0077。可通过systemd-analyze security tracking-api.service检查。 - 已知限制:
lyra-daemon保留了用于控制机器(服务、VM、音频)的sudo NOPASSWD。这并非 tracking 授予的权限,强化该权限属于 Lyra 项目;只要此守护进程存在,攻击者若能以该用户身份执行代码,就能获得此权限,不受上述保护措施的影响。 - 在
docker-compose.yml中将 n8n 限制为127.0.0.1:5678 dv_webhook_server.py监听0.0.0.0:8787(接收 Docker webhook 所必需)——如果机器暴露在外,请使用防火墙保护此端口- systemd 服务以
NoNewPrivileges=true运行 - 代码中不含明文密钥:
poller.py读取$CREDENTIALS_DIRECTORY(用户服务),或通过systemd-creds decrypt --user解密credentials/*.cred(系统服务);调试时则回退到QBT_PASSWORD/BAZARR_KEY环境变量
添加模板
- 打开
templates.py,在TEMPLATES中添加一项:
"mon_template": {
"description": "Description courte",
"extra_fields": ["champ1", "champ2"],
"default_unit": " unites",
"example_extra": {"champ1": "valeur", "champ2": "valeur"},
},
- 可选:在
sim.py中添加_sim_mon_template()模拟函数。
无需其他修改,模板即可立即使用。
也可以不修改代码,直接在 ~/.config/tracking/templates.json 中声明模板(结构相同,键为模板名称);启动时会加载该文件。
测试
make test # unitaires (storage, metrics) + integration (API HTTP reelle sur port ephemere)
conftest.py 中的 autouse fixture 会将持久化路径重定向到 tmp_path:测试绝不会触及生产状态。
Lyra 生态系统的一部分
| 仓库 | 作用 |
|---|---|
| lyra | 法语优先的语音助手:本地优先的语音 DevOps 助手(AGPL-3.0);它通过规则理解日常法语,甚至无需调用模型,因此只用 0.5B 模型即可满足需求 |
| fedora-agents | MCP:KVM 虚拟机和备份 |
| mcp-tracking | MCP + API + 长时间运行任务的仪表板 |
| neutroncore | 家庭实验室的 PWA 中心 |
| hue-mcp | Philips Hue MCP(fork 自 ThomasRohde/hue-mcp) |
| pylips-mcp | Philips TV MCP |
| denon-mcp | Denon 功放 MCP |
| catt-mcp | Chromecast 和 DLNA MCP |
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。