Skip to content

Commit a1164c8

Browse files
Shreyanandclaude
andauthored
Add MLflow Stop and SessionStart hooks to setup and docs (#17)
Replace old session-start.sh hook references with inline MLflow hooks (Stop for trace flushing, SessionStart for session ID capture). Update interactive setup in SKILL.md to auto-add hooks when MLflow is configured. Rename check_session_hook to check_mlflow_hooks in setup.py to validate both hooks are present. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 387b89f commit a1164c8

File tree

5 files changed

+72
-37
lines changed

5 files changed

+72
-37
lines changed

.claude/settings.example.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,22 @@
1818
"MLFLOW_TAG_USER": "<your-username>"
1919
},
2020
"hooks": {
21-
"SessionStart": [
21+
"Stop": [
2222
{
2323
"hooks": [
2424
{
2525
"type": "command",
26-
"command": "bash .claude/hooks/session-start.sh"
26+
"command": "python -c \"from mlflow.claude_code.hooks import stop_hook_handler; stop_hook_handler()\""
2727
}
2828
]
2929
}
3030
],
31-
"Stop": [
31+
"SessionStart": [
3232
{
3333
"hooks": [
3434
{
3535
"type": "command",
36-
"command": "python -c \"from mlflow.claude_code.hooks import stop_hook_handler; stop_hook_handler()\""
36+
"command": "INPUT=$(cat); SESSION_ID=$(echo \"$INPUT\" | jq -r '.session_id'); echo \"export CLAUDE_SESSION_ID='$SESSION_ID'\" >> \"$CLAUDE_ENV_FILE\""
3737
}
3838
]
3939
}

README.md

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -176,22 +176,30 @@ Step 5 [Claude] Analyze and summarize root cause
176176
```json
177177
{
178178
"hooks": {
179+
"Stop": [
180+
{
181+
"hooks": [
182+
{
183+
"type": "command",
184+
"command": "python -c \"from mlflow.claude_code.hooks import stop_hook_handler; stop_hook_handler()\""
185+
}
186+
]
187+
}
188+
],
179189
"SessionStart": [
180190
{
181-
"matcher": "*",
182-
"hooks": [
191+
"hooks": [
183192
{
184193
"type": "command",
185-
"command": "./.claude/hooks/session-start.sh"
194+
"command": "INPUT=$(cat); SESSION_ID=$(echo \"$INPUT\" | jq -r '.session_id'); echo \"export CLAUDE_SESSION_ID='$SESSION_ID'\" >> \"$CLAUDE_ENV_FILE\""
186195
}
187196
]
188197
}
189198
]
190199
}
191200
}
192201
```
193-
---
194-
**Note:** Run the following to make the script executable: `chmod +x ./.claude/hooks/session-start.sh`
202+
**Note:** These hooks are required for MLflow tracing. The `Stop` hook flushes traces and the `SessionStart` hook captures the session ID.
195203

196204
### context-fetcher
197205

@@ -366,17 +374,29 @@ Add the following environment variables to your Claude settings file at `~/.clau
366374
**Note**: Replace `<your-username>@<your-jumpbox> -p <port>` with your actual jumpbox connection details.
367375
Replace `<experiment-name>` to a experiment name to the desired experiment name. This will automatically create the experiment for you if it does not exist.
368376

369-
### Step 3: Add hooks SessionStart to hooks in .claude/settings.json
377+
### Step 3: Add MLflow hooks to `.claude/settings.json`
378+
379+
Add both `Stop` and `SessionStart` hooks to your settings:
380+
370381
```json
371382
{
372383
"hooks": {
384+
"Stop": [
385+
{
386+
"hooks": [
387+
{
388+
"type": "command",
389+
"command": "python -c \"from mlflow.claude_code.hooks import stop_hook_handler; stop_hook_handler()\""
390+
}
391+
]
392+
}
393+
],
373394
"SessionStart": [
374395
{
375-
"matcher": "*",
376396
"hooks": [
377397
{
378398
"type": "command",
379-
"command": "~/.claude/hooks/session-start.sh"
399+
"command": "INPUT=$(cat); SESSION_ID=$(echo \"$INPUT\" | jq -r '.session_id'); echo \"export CLAUDE_SESSION_ID='$SESSION_ID'\" >> \"$CLAUDE_ENV_FILE\""
380400
}
381401
]
382402
}
@@ -385,10 +405,8 @@ Replace `<experiment-name>` to a experiment name to the desired experiment name.
385405
}
386406
```
387407

388-
Make the script executable:
389-
```bash
390-
chmod +x ./.claude/hooks/session-start.sh
391-
```
408+
- **Stop hook**: Flushes MLflow traces when Claude stops
409+
- **SessionStart hook**: Captures the session ID for trace correlation
392410

393411
### Step 4: cd into AIOPS-SKILLS/ dir (where claude will be running)
394412

skills/root-cause-analysis/README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,14 @@ MLflow tracing is supported for debugging and performance analysis but is **not
177177
To enable tracing:
178178
1. Install MLflow: `pip install "mlflow[genai]>=3.4"` (or `pip install -e ".[mlflow]"` from the repo root)
179179
2. Configure `MLFLOW_PORT`, `MLFLOW_EXPERIMENT_NAME`, and optionally `JUMPBOX_URI` in `.claude/settings.json`
180-
3. The `session-start.sh` hook will auto-start the SSH tunnel and configure tracing when `MLFLOW_PORT` is set
180+
3. Add the required MLflow hooks to `.claude/settings.json`:
181+
```json
182+
"hooks": {
183+
"Stop": [{ "hooks": [{ "type": "command", "command": "python -c \"from mlflow.claude_code.hooks import stop_hook_handler; stop_hook_handler()\"" }] }],
184+
"SessionStart": [{ "hooks": [{ "type": "command", "command": "INPUT=$(cat); SESSION_ID=$(echo \"$INPUT\" | jq -r '.session_id'); echo \"export CLAUDE_SESSION_ID='$SESSION_ID'\" >> \"$CLAUDE_ENV_FILE\"" }] }]
185+
}
186+
```
187+
The `Stop` hook flushes traces and the `SessionStart` hook captures the session ID for correlation.
181188

182189
When enabled, pipeline-level traces (analysis steps 1-4) appear in the MLflow UI. When not installed, tracing is silently skipped.
183190

skills/root-cause-analysis/SKILL.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,16 @@ If any checks have `"status": "missing"` and `"configurable": true`, offer to he
6161
- Then set `REMOTE_HOST` to the alias name
6262
4. After collecting all values, read the project's `.claude/settings.json` file
6363
5. Merge the new values into the `"env"` block (create it if it doesn't exist)
64-
6. Write the updated settings file
65-
7. Tell the user to **restart the Claude Code session** for env vars to take effect
66-
8. **Important**: Write secrets (tokens, passwords) to `.claude/settings.json` -- ensure this file is in `.gitignore`
64+
6. If MLflow env vars were configured (MLFLOW_PORT, MLFLOW_EXPERIMENT_NAME), also add the required MLflow hooks to the `"hooks"` block (create it if it doesn't exist):
65+
```json
66+
"hooks": {
67+
"Stop": [{ "hooks": [{ "type": "command", "command": "python -c \"from mlflow.claude_code.hooks import stop_hook_handler; stop_hook_handler()\"" }] }],
68+
"SessionStart": [{ "hooks": [{ "type": "command", "command": "INPUT=$(cat); SESSION_ID=$(echo \"$INPUT\" | jq -r '.session_id'); echo \"export CLAUDE_SESSION_ID='$SESSION_ID'\" >> \"$CLAUDE_ENV_FILE\"" }] }]
69+
}
70+
```
71+
7. Write the updated settings file
72+
8. Tell the user to **restart the Claude Code session** for env vars and hooks to take effect
73+
9. **Important**: Write secrets (tokens, passwords) to `.claude/settings.json` -- ensure this file is in `.gitignore`
6774

6875
If checks show non-configurable errors (e.g., venv issues, rsync not found), provide the fix command instead.
6976

skills/root-cause-analysis/scripts/setup.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -507,37 +507,40 @@ def check_mlflow_server() -> dict:
507507
}
508508

509509

510-
def check_session_hook(repo_root: Path) -> dict:
511-
"""Check if session-start hook is configured."""
512-
hook_path = repo_root / ".claude" / "hooks" / "session-start.sh"
513-
if not hook_path.exists():
514-
return {
515-
"name": "Session hook",
516-
"status": "missing",
517-
"message": ".claude/hooks/session-start.sh not found",
518-
}
519-
520-
# Check if settings.json has the SessionStart hook registered
510+
def check_mlflow_hooks(repo_root: Path) -> dict:
511+
"""Check if MLflow Stop and SessionStart hooks are configured in settings.json."""
521512
for settings_name in ["settings.json", "settings.local.json"]:
522513
settings_path = repo_root / ".claude" / settings_name
523514
if settings_path.exists():
524515
try:
525516
with open(settings_path) as f:
526517
settings = json.load(f)
527518
hooks = settings.get("hooks", {})
528-
if "SessionStart" in hooks:
519+
has_stop = "Stop" in hooks
520+
has_session = "SessionStart" in hooks
521+
if has_stop and has_session:
529522
return {
530-
"name": "Session hook",
523+
"name": "MLflow hooks",
531524
"status": "ok",
532-
"message": f"registered in {settings_name}",
525+
"message": f"Stop + SessionStart registered in {settings_name}",
533526
}
527+
missing = []
528+
if not has_stop:
529+
missing.append("Stop")
530+
if not has_session:
531+
missing.append("SessionStart")
532+
return {
533+
"name": "MLflow hooks",
534+
"status": "missing",
535+
"message": f"Missing hooks: {', '.join(missing)}. See settings.example.json",
536+
}
534537
except (json.JSONDecodeError, OSError):
535538
pass
536539

537540
return {
538-
"name": "Session hook",
541+
"name": "MLflow hooks",
539542
"status": "missing",
540-
"message": "SessionStart hook not registered in settings. See settings.example.json",
543+
"message": "Stop and SessionStart hooks not found in settings. See settings.example.json",
541544
}
542545

543546

@@ -556,7 +559,7 @@ def run_checks(base_dir: Path, repo_root: Path | None = None) -> list[dict]:
556559
check_github_mcp(repo_root),
557560
check_mlflow(),
558561
check_mlflow_server(),
559-
check_session_hook(repo_root),
562+
check_mlflow_hooks(repo_root),
560563
]
561564

562565

0 commit comments

Comments
 (0)