
Company News
Socket Joins New OpenJS Program to Fund Node.js Security Work
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.
mcp-tracking
Advanced tools
MCP server, HTTP API and Textual TUI dashboard to track long-running tasks in real time
English summary. Local-first tracking of long-running tasks: an MCP server for Claude or Lyra, a small HTTP API on 127.0.0.1:8765 and a Textual terminal dashboard. Sessions have items, progress, logs and templates (download, machine, free, lyra_task, movie); pollers feed qBittorrent and Bazarr sessions automatically. Install: pip install . then mcp-tracking, mcp-tracking-api, mcp-tracking-ui. No cloud, no telemetry.
Serveur MCP de suivi en temps reel avec dashboard terminal. Permet a Claude/Lyra de tracker n'importe quelle operation longue ET alimente automatiquement les sessions depuis le media-server (qBittorrent, Bazarr, conversion DV).

Enregistree avec docs/demo/record.sh : server.py --test alimente quatre sessions simulees dans un repertoire d'etat temporaire (TRACKING_STATE_DIR), puis server.py --ui ouvre le dashboard dessus. Les sessions reelles ne sont pas touchees.
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)
| Fichier | Emplacement | Surcharge |
|---|---|---|
tracking_state.json | ~/.local/state/tracking/ | TRACKING_STATE_DIR |
poller_state.json | ~/.local/state/tracking/ | TRACKING_STATE_DIR |
templates.json (templates utilisateur, optionnel) | ~/.config/tracking/ | TRACKING_TEMPLATES_FILE |
credentials/*.cred (qBittorrent, Bazarr) | a cote du code, gitignore | -- |
Un ancien tracking_state.json a cote du code est migre automatiquement au premier
demarrage (copie, jamais supprime).
Variables d'environnement de retention :
| Variable | Defaut | Role |
|---|---|---|
TRACKING_TTL_DAYS | 7 | Purge des sessions done / error / paused |
TRACKING_TTL_RUNNING_H | 24 | Purge des sessions running orphelines (plus mises a jour) |
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)
Le fichier d'etat est ecrit a chaque modification via ecriture atomique (os.replace) sous verrou
fichier (tracking_state.lock). Tous les processus (MCP, API, poller, dashboard) partagent cet
unique fichier ; chaque lecture verifie le mtime pour invalider son cache.
Toute mutation (HTTP ou MCP) passe par mutations.py, qui garantit le meme comportement sur les
deux chemins : started_at / finished_at poses sur les items et la session, historique de
progression (fenetre glissante de 40 points), niveaux de log info / warn / error,
auto-completion quand tous les items sont termines.
GET /sessions et tracking_get renvoient un bloc metrics calcule a la volee par metrics.py :
| Champ | Sens |
|---|---|
percent | progression (plafonnee a 100) |
rate, rate_str | vitesse sur les 120 dernieres secondes (2.0 MB/s, 30.0 u/min) |
eta_seconds, eta_str | temps restant estime (session running uniquement) |
elapsed_seconds, elapsed_str | depuis created_at jusqu'a finished_at ou maintenant |
idle_seconds, stale | stale = running sans mise a jour depuis 10 min (affiche dans le 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
Configuration 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"
Le MCP est enregistre dans Claude Code (scope user) :
claude mcp list # -> tracking: Connected
Pour reenregistrer :
claude mcp add tracking -s user -- \
<dossier du dépôt>/.venv/bin/python \
<dossier du dépôt>/server.py
Deux services tournent en permanence et se lancent au boot :
| Service | Role | Port |
|---|---|---|
tracking-api.service | API HTTP locale pour scripts externes | 127.0.0.1:8765 |
tracking-poller.service | Poll qBittorrent (10s) + Bazarr (60s) | -- |
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
Les instances MCP server.py deja ouvertes par des sessions Claude Code ne sont pas
redemarrees par deploy.sh : reconnecter tracking via /mcp dans ces sessions.
# 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
Cherche "MCP Tracking" dans wofi/launcher. Lance le dashboard dans 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
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
Simule 4 sessions en parallele : download, machine (12 noeuds), free, movie (pipeline DV complet).
[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 --
-- --
-- --
| Icone | Statut | Couleur |
|---|---|---|
[ ] | pending | gris |
[>] | running | cyan |
[ok] | done | vert |
[!] | error | rouge |
| Couleur | Statut |
|---|---|
| cyan | running |
| vert | done |
| rouge | error |
| jaune | paused |
| Touche | Action |
|---|---|
f | Filtre suivant (cycle dynamique par template) |
e | Basculer filtre erreurs uniquement |
r | Refresh manuel |
s | Stop propre d'une session (saisir l'ID) -> status paused |
k | Kill force d'une session (saisir l'ID) -> suppression |
q | Quitter |
| Fleches / Molette | Scroll |
Appuyer sur s ou k ouvre un modal avec un champ de saisie pour l'ID de session.
s marque la session en paused et ajoute un logk supprime definitivement la session du dashboardEchap annuleLe cycle de filtres est construit automatiquement depuis les sessions presentes :
all -> download -> free -> movie -> lyra_task -> errors -> all -> ...
all toujours presenterrors n'apparait que si au moins une session a une erreurfiltre: movie | 2/5 session(s)allLe poller interroge http://localhost:8080/api/v2/torrents/info toutes les 10 secondes.
[DOWNLOAD] avec nom, taille, vitesse, ETAcredentials/qbt-password.cred (chiffre systemd-creds --user, genere par media-server/scripts/secrets/rotate-secrets.sh)Le poller interroge l'API Bazarr toutes les 60 secondes.
[SUBTITLES] unique liste tous les episodes/films sans sous-titres FRSous-titres manquants (151)credentials/bazarr-api-key.cred (meme mecanisme). Sans credential, le poll concerne est simplement desactive.Declenche par dv-webhook.service quand Radarr/Sonarr importent un film DV Profile 4 ou 7.
Flux :
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
Les 6 etapes trackees avec leurs metriques :
| Etape | Outil | Metriques affichees |
|---|---|---|
| 1/6 extraction HEVC | ffmpeg | frame / speed / size / time |
| 2/6 demux BL/EL | dovi_tool | frames X/Y (%), bl: X GB, el: X GB |
| 3/6 extraction RPU + conv P8 | dovi_tool | frames X/Y (%), RPU: X KB |
| 4/6 injection RPU P8 dans BL | dovi_tool | frames X/Y (%), P8 HEVC: X GB |
| 5/6 reconstruction timestamps | ffmpeg | frame / fps / size |
| 6/6 remuxage MKV final | ffmpeg | frame / speed / size |
La barre de progression globale avance en continu pendant chaque etape (pas par sauts de 1/6 a la fin de chaque etape).
Mode manuel :
# 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
En mode manuel, la session tracking est creee automatiquement dans process_file.
Scripts externes peuvent creer/modifier des sessions directement :
# 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
Corps PUT complet (tous les champs optionnels) :
{
"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
}
}
tracking_createParametres:
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_updateParametres:
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_logAjoute un log sans modifier la progression.
Parametres:
session_id (str)
message (str)
tracking_completeMarque done a 100%.
Parametres:
session_id (str)
message (str, opt)
tracking_errorMarque en erreur (prefixe "ERREUR:" auto, remonte dans colonne Erreurs).
Parametres:
session_id (str)
message (str)
tracking_stopArrete proprement une session (status -> paused). Reste visible dans le dashboard.
Parametres:
session_id (str)
message (str, opt)
tracking_killSupprime une session en force. Disparait immediatement du dashboard.
Parametres:
session_id (str)
tracking_getRetourne l'etat complet formate d'une session.
tracking_listParametres:
template (str, opt) Filtrer par template
status (str, opt) Filtrer par statut ("running", "done", "error", "paused")
tracking_deleteSupprime une session (equivalent de tracking_kill).
tracking_templatesAffiche la liste des templates et leurs champs.
open_tracking_uiOuvre le dashboard dans un terminal Kitty.
Parametres:
filter_template (str, opt) Template a afficher au lancement
downloadTelechargement de fichiers. Alimente automatiquement par qBittorrent via le poller.
Champs extra : speed, eta
Unite par defaut : MB
machineOperations sur des machines (update, clone, snapshot, deploy). Utilise par Lyra pour les operations VM/cluster.
Champs extra : operation, target
Unite par defaut : machines
freeFormat libre. Utilise par le poller pour les sous-titres Bazarr manquants.
Aucun champ extra impose, aucune unite par defaut.
lyra_taskOperations Lyra (VM clone, backup, update, snapshot).
Champs extra : operation, target, phase, eta
Unite par defaut : %
moviePipeline complet d'un film : telechargement -> conversion Dolby Vision. Alimente automatiquement par dv_convert.py quand Radarr/Sonarr importent un fichier DV P4/P7.
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"
Deux attaquants realistes sur une machine de bureau :
127.0.0.1:8765. Sans protection, elle pourrait creer des
sessions, en supprimer, et surtout demander l'envoi d'un signal a un processus.api.py ecoute uniquement sur 127.0.0.1:8765 -- inaccessible depuis le reseau$XDG_RUNTIME_DIR/tracking/token (droits 0600, donc illisible par un autre utilisateur), exige
en Authorization: Bearer ... sur POST, PUT et DELETE. Une page web ne peut pas le lire.Origin donne un 403, meme avec le jeton.Host verifie (boucle locale uniquement) et Content-Type: application/json exige
en ecriture.pid annonce existe et appartient au meme utilisateur, puis releve son
heure de demarrage. /stop et /kill refusent d'agir si cette empreinte a change (numero de
processus recycle par un autre programme) ou si le pid n'a jamais ete enregistre. Le pid ne
peut plus etre modifie par un PUT. Chaque signal envoye est journalise.ProtectSystem=strict, ProtectHome=read-only avec le seul etat
en ecriture, PrivateTmp, SystemCallFilter=@system-service, CapabilityBoundingSet= vide,
UMask=0077. Verifiable avec systemd-analyze security tracking-api.service.lyra-daemon garde un sudo NOPASSWD pour piloter la machine (services,
VMs, audio). Ce n'est pas le tracking qui l'accorde, et le durcir releve du projet Lyra ; tant
que ce daemon existe, un attaquant qui obtiendrait l'execution de code sous cet utilisateur
disposerait de ce pouvoir, independamment des protections ci-dessus.127.0.0.1:5678 dans docker-compose.ymldv_webhook_server.py ecoute sur 0.0.0.0:8787 (necessaire pour recevoir les webhooks Docker) -- proteger ce port avec un firewall si la machine est exposeeNoNewPrivileges=truepoller.py lit $CREDENTIALS_DIRECTORY (service user) ou dechiffre credentials/*.cred via systemd-creds decrypt --user (service systeme), avec repli sur les variables QBT_PASSWORD / BAZARR_KEY pour le debugtemplates.py et ajouter une entree dans TEMPLATES :"mon_template": {
"description": "Description courte",
"extra_fields": ["champ1", "champ2"],
"default_unit": " unites",
"example_extra": {"champ1": "valeur", "champ2": "valeur"},
},
_sim_mon_template() dans sim.py.Le template est immediatement disponible sans autre modification.
Sans toucher au code, un template peut aussi etre declare dans ~/.config/tracking/templates.json
(meme structure, cle = nom du template) ; il est charge au demarrage.
make test # unitaires (storage, metrics) + integration (API HTTP reelle sur port ephemere)
La fixture autouse de conftest.py redirige la persistence vers un tmp_path : les tests ne
touchent jamais l'etat de production.
| Dépôt | Rôle |
|---|---|
| lyra | assistant DevOps vocal, local par défaut (AGPL-3.0) |
| fedora-agents | MCP : machines virtuelles KVM et sauvegardes |
| mcp-tracking | MCP + API + tableau de bord des tâches longues |
| neutroncore | hub PWA du homelab |
| hue-mcp | MCP Philips Hue (fork de ThomasRohde/hue-mcp) |
| pylips-mcp | MCP TV Philips |
| denon-mcp | MCP ampli Denon |
| catt-mcp | MCP Chromecast et DLNA |
FAQs
MCP server, HTTP API and Textual TUI dashboard to track long-running tasks in real time
We found that mcp-tracking demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Company News
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.

Research
/Security News
A malicious Firefox extension fetches its payload after installation to evade detection, steal Google session cookies, and automate account takeover.