@imenam/claude-session-tracker-mcp
v1.7.0
Published
MCP server letting a Claude Code agent publish its own status (running, done, waiting, error) to the Claude Session Watcher dashboard.
Maintainers
Readme
@imenam/claude-session-tracker-mcp
Publie l'état des sessions Claude Code — en cours, question en attente, terminé — dans le tableau de bord Claude Session Watcher.
Le cycle de vie est mesuré par des hooks, pas raconté par le modèle : un hook se déclenche toujours, un outil seulement si l'agent y pense. L'outil MCP ne lui laisse donc qu'une seule nuance, celle qu'aucun événement ne porte — « la question que je viens de poser attend une réponse ».
Features
- Aucun identifiant à fournir. La session est résolue depuis le registre officiel de
Claude Code (
~/.claude/sessions/<pid>.json) : un agent ne peut pas publier sur la mauvaise session, et n'a rien à retenir. - Correct après un
/clear. Le registre est relu à chaque appel, jamais mis en cache — un nouveausessionIdfrappé dans le même process est pris en compte immédiatement. - Cycle de vie déterministe. Le binaire de hook publie « En cours », « Question en attente » et « Terminé » sur les événements Claude Code correspondants : le statut ne reste jamais figé parce que l'agent a oublié d'appeler un outil.
- Le hook prime sur l'agent, sans règle à écrire : la dernière écriture gagne, et un hook se déclenche toujours là où un outil dépend du bon vouloir du modèle.
- Inoffensif. Le hook sort toujours en code 0 et n'écrit jamais sur stdout : il ne peut ni bloquer une action de Claude Code, ni polluer le contexte de l'agent. Un Watcher éteint est un cas normal, pas une panne.
- Mode lecture seule (
READ_ONLY) filtrant l'outil d'écriture sur deux couches.
Installation
npx -y @imenam/claude-session-tracker-mcpPrérequis : Node >= 20.6, et le Claude Session Watcher qui tourne (npm start). Son port n'a
pas à être connu : le Watcher publie son adresse dans ~/.claude-session-watcher/endpoint.json
à chaque démarrage, et le tracker l'y lit à chaque publication.
Pour vérifier que ce poste sait joindre le Watcher :
npx -y @imenam/claude-session-tracker-mcp --config-diagLa commande dit d'où vient l'adresse retenue et si quelqu'un répond au bout. Sortie 0 si tout va bien, 1 sinon. Il n'existe aucun port par défaut : sans adresse découverte ni surcharge explicite, rien n'est publié et l'erreur le dit — plutôt que d'envoyer les statuts dans le vide.
Depuis les sources
npm install
npm run buildMCP Configuration
Le suivi n'a d'intérêt que si toutes les sessions de la machine sont instrumentées — le Watcher les inventorie toutes. On enregistre donc le serveur en portée utilisateur plutôt que dans un dépôt particulier :
claude mcp add -s user claude-session-tracker -- npx -y @imenam/claude-session-tracker-mcpPour la portée projet, une commande écrit le .mcp.json du répertoire courant, en le créant
si besoin :
npx -y @imenam/claude-session-tracker-mcp --claude-mcp-setupMêmes garanties que --claude-setup : fusion (les autres serveurs déclarés sont préservés),
sauvegarde horodatée, idempotence, échec net sur un JSON illisible.
| Option | Effet |
|---|---|
| --dev | commande node <racine du package>/dist/src/index.js au lieu de npx |
| --dry-run | affiche ce qui serait fait, sans rien écrire |
| --watcher-url <url> | fige l'URL du Watcher (par défaut : découverte automatique) |
| --read-only | ajoute READ_ONLY=1 : set_session_status masqué et refusé |
| --mcp-file <fichier> | fichier cible (défaut : ./.mcp.json) |
| --help | rappel de l'usage |
Ou, à la main, dans ~/.claude.json (portée utilisateur) ou .mcp.json (portée projet) :
{
"mcpServers": {
"claude-session-tracker": {
"command": "npx",
"args": ["-y", "@imenam/claude-session-tracker-mcp"]
}
}
}Hooks
Le serveur MCP couvre ce que l'agent sait ; les hooks couvrent ce qui arrive. Sans eux, une session reste affichée « En cours » dès que l'agent oublie d'annoncer qu'il a fini.
npx -y @imenam/claude-session-tracker-mcp --claude-setupÉcrit les quatre hooks dans ~/.claude/settings.json, en préservant le reste du fichier.
Idempotent : relancer la commande met à jour l'entrée existante au lieu d'en créer une seconde.
Le fichier est sauvegardé (settings.json.backup-<horodatage>) avant réécriture, et un JSON
illisible fait échouer la commande plutôt que d'être écrasé.
| Option | Effet |
|---|---|
| --dev | installe node <racine du package>/dist/src/hook.js au lieu de la commande npx |
| --dry-run | affiche ce qui serait fait, sans rien écrire |
| --settings <fichier> | fichier cible (défaut : ~/.claude/settings.json) |
| --help | rappel de l'usage |
La commande ne touche que les hooks : l'enregistrement du serveur MCP reste claude mcp add.
Diagnostic
npx -y @imenam/claude-session-tracker-mcp --config-diagRépond à la seule question qui décide de tout le reste : ce process saurait-il joindre le Watcher, maintenant ? Elle mérite une commande parce qu'elle ne se voit nulle part ailleurs — un hook qui échoue n'a le droit ni d'écrire sur stdout (réinjecté dans le contexte de l'agent) ni de sortir en erreur (cela bloquerait l'action observée) : il note et s'efface. La panne est donc structurellement muette côté session.
La sortie donne, dans l'ordre : la surcharge MCP_TRACKER_WATCHER_URL si elle existe — et d'où
elle vient, un .env de copie de travail étant signalé comme tel puisqu'il n'existera pas sous
npx —, le contenu du fichier de découverte, l'adresse finalement retenue, et le résultat d'un
appel réel au Watcher. Sortie 0 s'il répond, 1 sinon. Lancée par npx, la commande s'exécute
dans le même environnement que le serveur MCP : ce qu'elle voit est bien ce que lui verrait.
Équivalent manuel, à ajouter dans ~/.claude/settings.json :
{
"hooks": {
"UserPromptSubmit": [
{ "hooks": [{ "type": "command", "command": "npx -y -p @imenam/claude-session-tracker-mcp claude-session-tracker-hook" }] }
],
"Notification": [
{ "hooks": [{ "type": "command", "command": "npx -y -p @imenam/claude-session-tracker-mcp claude-session-tracker-hook" }] }
],
"Stop": [
{ "hooks": [{ "type": "command", "command": "npx -y -p @imenam/claude-session-tracker-mcp claude-session-tracker-hook" }] }
],
"SessionEnd": [
{ "hooks": [{ "type": "command", "command": "npx -y -p @imenam/claude-session-tracker-mcp claude-session-tracker-hook" }] }
]
}
}En développement, remplacer la commande par node <chemin absolu>/dist/src/hook.js, ce qui
évite la latence de résolution npx à chaque événement.
SessionEnd supprime la ligne d'affichage dans le Watcher. Il ne termine aucun process :
l'événement survient après la fin de la session.
Qui publie quoi
| Origine | Statut publié |
|---|---|
| Hook UserPromptSubmit | En cours |
| Hook Notification | Question en attente |
| Hook Stop | Terminé |
| Hook SessionEnd | (entrée supprimée) |
| Outil set_session_status | Question en attente, ou retour à En cours |
Aucun arbitrage entre les deux : la dernière écriture gagne. Les hooks priment donc de fait, puisqu'ils se déclenchent toujours là où l'outil n'est appelé que si le modèle y pense.
Environment variables
| Variable | Required | Description |
|---|---|---|
| MCP_TRACKER_WATCHER_URL | Non | Fige l'URL du Watcher et court-circuite la découverte. Aucun défaut : sans elle, l'adresse vient de ~/.claude-session-watcher/endpoint.json, et si ce fichier manque, rien n'est publié et l'erreur le dit. Ne sert qu'à un Watcher que la découverte ne peut pas trouver — autre machine, tunnel. |
| MCP_DATA_DIR | Non | Répertoire de persistance des logs. Défaut : <racine du package>/.claude-session-tracker-data. |
| READ_ONLY | Non | 1, true ou yes : masque et refuse set_session_status. |
| CLAUDE_PID | Auto | Injectée par Claude Code. Sert à localiser l'entrée de registre de la session. |
| CLAUDE_CODE_SESSION_ID | Auto | Injectée par Claude Code. Repli si le registre est illisible. |
Un .env placé à la racine du package est chargé s'il existe. Les variables déjà définies
dans l'environnement priment.
Available tools
| Tool | Description |
|---|---|
| set_session_status | Signale que la session attend une réponse humaine (waiting) ou qu'elle l'a reçue (answered → « En cours »). Aucun texte libre : le Watcher n'affiche qu'un badge, la question elle-même se lit dans l'interface de l'agent. Aucun autre état n'est exposé : le reste du cycle appartient aux hooks. Écriture. |
| get_session_status | Relit le statut affiché et renvoie l'identité résolue de la session (session id, origine de la résolution, pid, cwd, URL du Watcher). Outil de diagnostic. |
Comment la session est identifiée
C'est la seule question difficile du projet, et elle a une réponse exacte plutôt qu'une heuristique.
Le registre ~/.claude/sessions/<pid>.json porte le sessionId courant de la session dont
le pid est <pid>. C'est la source d'autorité qu'utilise aussi le Watcher, donc les deux côtés
ne peuvent pas diverger. Tout le problème est de trouver le pid du process Claude Code qui
nous a lancés.
Un hook n'a rien à résoudre : Claude Code lui fournit session_id sur stdin. Pour le serveur
MCP, trois façons, essayées dans cet ordre :
CLAUDE_PID— injecté par Claude Code dans l'environnement des enfants de l'outil shell et du binaire de hook. Mesuré : absent de l'environnement d'un serveur MCP. Essayé quand même, c'est gratuit.process.ppid— un serveur MCP stdio est un enfant direct declaude.exe: son parent est sa session, par définition. Aucune chaîne à remonter, aucune requête système, aucun code spécifique à la plateforme. C'est ce chemin qui porte le cas réel.- Balayage du registre à la recherche de l'entrée dont le
sessionIdégaleCLAUDE_CODE_SESSION_ID. Nécessaire quand un intermédiaire s'insère — typiquementnpx, qui passe par uncmd.exesous Windows, si bien que le parent n'est plusclaude.exe.
Faute de quoi CLAUDE_CODE_SESSION_ID est employé tel quel, et à défaut une erreur explicite
est levée — jamais de devinette.
Le pid trouvé est mémorisé (il est stable pour la durée de vie du process), mais le registre est
relu à chaque appel. C'est ce qui rend la résolution correcte après un /clear, qui frappe
un nouveau sessionId dans le même process : la variable d'environnement, capturée au
démarrage du MCP, devient fausse, tandis que le registre est réécrit. CLAUDE_CODE_SESSION_ID
n'arrive donc qu'en dernier recours, précisément parce que c'est la seule valeur qui puisse être
périmée.
get_session_status renvoie le chemin effectivement emprunté (found via CLAUDE_PID /
parent process / registry scan) : c'est le moyen le plus rapide de vérifier qu'on ne tourne
pas sur le repli.
La clé d'échange avec le Watcher est le sessionId, jamais le pid : unique globalement,
immunisé à la réutilisation de pid, et seule information dont disposent les hooks.
Data storage
Logs uniquement, dans <MCP_DATA_DIR>/logs/ : claude-session-tracker-mcp.log et
claude-session-tracker-hook.log, avec rotation sur un unique .1 au-delà de 1 Mo. Aucune
donnée métier n'est stockée localement — les statuts vivent dans le Watcher.
Notes
- stdout est réservé au JSON-RPC. Tout log part sur stderr et dans le fichier ;
console.logest détourné dès la première ligne du process. - Le délai de garde des appels HTTP est de 2 s. Un Watcher éteint ou figé produit une erreur immédiate et actionnable, jamais un agent bloqué.
src/version.tsest généré parscripts/generate-version.jset ne doit pas être édité. Une release =npm run release(:minor/:majorpour les autres incréments) : bump de version, tag git, build viaprepublishOnly, publication, puisgit push --follow-tags. L'arbre de travail doit être propre, sinonnpm versionrefuse de démarrer.tsconfig.jsondéclare"types": ["node"]: TypeScript 7 n'inclut plus@types/nodeautomatiquement.
