bi-mcp 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. bi_mcp-0.2.0/.claude/hooks/run_tests_advisory.sh +56 -0
  2. bi_mcp-0.2.0/.claude/settings.json +16 -0
  3. bi_mcp-0.2.0/.claude/skills/bi-troubleshoot/SKILL.md +109 -0
  4. bi_mcp-0.2.0/.env.example +38 -0
  5. bi_mcp-0.2.0/.gitignore +12 -0
  6. bi_mcp-0.2.0/AGENTS.md +596 -0
  7. bi_mcp-0.2.0/LICENSE +21 -0
  8. bi_mcp-0.2.0/PKG-INFO +433 -0
  9. bi_mcp-0.2.0/README.md +408 -0
  10. bi_mcp-0.2.0/pyproject.toml +48 -0
  11. bi_mcp-0.2.0/scripts/reg_decoder_coverage.md +741 -0
  12. bi_mcp-0.2.0/scripts/reg_decoder_coverage.py +322 -0
  13. bi_mcp-0.2.0/scripts/smoke_concurrent_set_camera.py +101 -0
  14. bi_mcp-0.2.0/scripts/smoke_motion_config.py +189 -0
  15. bi_mcp-0.2.0/scripts/smoke_verify_inconclusive.py +203 -0
  16. bi_mcp-0.2.0/src/bi_mcp/__init__.py +1 -0
  17. bi_mcp-0.2.0/src/bi_mcp/__main__.py +22 -0
  18. bi_mcp-0.2.0/src/bi_mcp/cli.py +157 -0
  19. bi_mcp-0.2.0/src/bi_mcp/client.py +469 -0
  20. bi_mcp-0.2.0/src/bi_mcp/errors.py +314 -0
  21. bi_mcp-0.2.0/src/bi_mcp/logging_setup.py +92 -0
  22. bi_mcp-0.2.0/src/bi_mcp/reg.py +208 -0
  23. bi_mcp-0.2.0/src/bi_mcp/server.py +121 -0
  24. bi_mcp-0.2.0/src/bi_mcp/shapers.py +1441 -0
  25. bi_mcp-0.2.0/src/bi_mcp/tools/__init__.py +41 -0
  26. bi_mcp-0.2.0/src/bi_mcp/tools/registry.py +93 -0
  27. bi_mcp-0.2.0/src/bi_mcp/tools/tools_actionset.py +93 -0
  28. bi_mcp-0.2.0/src/bi_mcp/tools/tools_alerts.py +168 -0
  29. bi_mcp-0.2.0/src/bi_mcp/tools/tools_audit_actions.py +641 -0
  30. bi_mcp-0.2.0/src/bi_mcp/tools/tools_cameras.py +237 -0
  31. bi_mcp-0.2.0/src/bi_mcp/tools/tools_clips.py +104 -0
  32. bi_mcp-0.2.0/src/bi_mcp/tools/tools_explain_alert.py +926 -0
  33. bi_mcp-0.2.0/src/bi_mcp/tools/tools_log.py +199 -0
  34. bi_mcp-0.2.0/src/bi_mcp/tools/tools_mutations.py +2055 -0
  35. bi_mcp-0.2.0/src/bi_mcp/tools/tools_ptz.py +56 -0
  36. bi_mcp-0.2.0/src/bi_mcp/tools/tools_reg.py +95 -0
  37. bi_mcp-0.2.0/src/bi_mcp/tools/tools_status.py +142 -0
  38. bi_mcp-0.2.0/src/bi_mcp/tools/tools_timeline.py +53 -0
  39. bi_mcp-0.2.0/src/bi_mcp/utils/__init__.py +0 -0
  40. bi_mcp-0.2.0/src/bi_mcp/utils/logging.py +72 -0
  41. bi_mcp-0.2.0/src/bi_mcp/utils/time.py +46 -0
  42. bi_mcp-0.2.0/tests/__init__.py +0 -0
  43. bi_mcp-0.2.0/tests/conftest.py +32 -0
  44. bi_mcp-0.2.0/tests/unit/__init__.py +0 -0
  45. bi_mcp-0.2.0/tests/unit/test_mutation_gate.py +87 -0
  46. bi_mcp-0.2.0/tests/unit/test_reg_parser.py +178 -0
  47. bi_mcp-0.2.0/tests/unit/test_shape_camlist_stale_fencing.py +109 -0
  48. bi_mcp-0.2.0/tests/unit/test_shape_ptz_inconsistencies.py +98 -0
  49. bi_mcp-0.2.0/tests/unit/test_shape_reg_mask_omission.py +89 -0
  50. bi_mcp-0.2.0/tests/unit/test_shape_reg_profile_sync.py +299 -0
  51. bi_mcp-0.2.0/tests/unit/test_shape_reg_retriggers_label.py +108 -0
  52. bi_mcp-0.2.0/tests/unit/test_shape_status_profile_name.py +77 -0
  53. bi_mcp-0.2.0/tests/unit/test_shapers_actionset.py +247 -0
  54. bi_mcp-0.2.0/tests/unit/test_tool_status_enrichment_resilience.py +93 -0
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env bash
2
+ # Advisory test runner — fires on PostToolUse for Edit/Write under bi-mcp.
3
+ #
4
+ # Behavior:
5
+ # • Silent on pass (no output → no extra tokens, no noise).
6
+ # • Loud on fail (clear header + pytest short summary).
7
+ # • Exit 0 always — this is advisory, not blocking. Claude sees the
8
+ # output and decides whether to fix immediately or defer.
9
+ #
10
+ # Hook payload (PostToolUse) arrives on stdin as JSON. We only care about
11
+ # the edited file path; if it isn't under src/ or tests/, we skip silently.
12
+
13
+ set -u
14
+
15
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
16
+ PYTEST="$REPO_ROOT/.venv/bin/pytest"
17
+
18
+ # Read hook payload from stdin (best-effort — tolerate missing/odd shape).
19
+ payload="$(cat || true)"
20
+
21
+ # Extract tool_input.file_path with a tiny inline python (jq isn't a hard
22
+ # dep on every host). Falls through to empty string on any parse failure.
23
+ file_path="$(printf '%s' "$payload" | python3 -c '
24
+ import json, sys
25
+ try:
26
+ d = json.loads(sys.stdin.read() or "{}")
27
+ print((d.get("tool_input") or {}).get("file_path", ""))
28
+ except Exception:
29
+ print("")
30
+ ' 2>/dev/null)"
31
+
32
+ # Only run when an edit landed under src/ or tests/. Anything else
33
+ # (README tweaks, AGENTS.md, settings) doesn't affect test behaviour.
34
+ case "$file_path" in
35
+ "$REPO_ROOT"/src/bi_mcp/*|"$REPO_ROOT"/tests/*) ;;
36
+ *) exit 0 ;;
37
+ esac
38
+
39
+ # Pytest must exist — if the venv is gone, surface that once and bail.
40
+ if [[ ! -x "$PYTEST" ]]; then
41
+ echo "⚠️ bi-mcp tests skipped: $PYTEST not found. Run: .venv/bin/pip install 'pytest>=8'" >&2
42
+ exit 0
43
+ fi
44
+
45
+ # Run quietly; capture output so we can decide whether to emit anything.
46
+ output="$("$PYTEST" "$REPO_ROOT/tests/unit/" -q --no-header --tb=line 2>&1)"
47
+ rc=$?
48
+
49
+ if [[ $rc -ne 0 ]]; then
50
+ # Loud header so the failure stands out in the conversation transcript.
51
+ echo "❌ bi-mcp tests FAILED after edit to ${file_path#$REPO_ROOT/}" >&2
52
+ echo "$output" >&2
53
+ fi
54
+
55
+ # Always exit 0 — advisory, never blocks the edit.
56
+ exit 0
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
3
+ "hooks": {
4
+ "PostToolUse": [
5
+ {
6
+ "matcher": "Edit|Write",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/run_tests_advisory.sh"
11
+ }
12
+ ]
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: bi-troubleshoot
3
+ description: Structured walk for "camera X is missing alerts" or "alerts are firing wrong on camera X" — read alert pattern, drill into config (BI then .reg), form one hypothesis, test it (optional bi_trigger_camera), verify.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # bi-troubleshoot — alert pipeline diagnosis
8
+
9
+ Use this when the user reports an alert problem on a specific camera —
10
+ missing alerts, spurious alerts, wrong classification, alerts at the wrong
11
+ times of day. Do NOT use for connectivity / PTZ / recording issues; those
12
+ need different surfaces.
13
+
14
+ ## Step 1 — Confirm the symptom in data
15
+
16
+ ```
17
+ bi_list_alerts(camera="<short>", limit=50)
18
+ ```
19
+
20
+ Look at the time distribution, AI memo, and zone hits. Don't trust the
21
+ user's framing alone — they may have miscounted, or the issue may be
22
+ different from how they described it.
23
+
24
+ If they gave you a time range:
25
+
26
+ ```
27
+ bi_list_alerts(camera="<short>", startdate=<unix>, enddate=<unix>, limit=200)
28
+ ```
29
+
30
+ If you need clip context (was it recording? what resolution?):
31
+
32
+ ```
33
+ bi_list_clips(camera="<short>", view="alerts", limit=20)
34
+ ```
35
+
36
+ ## Step 2 — Read top-level config
37
+
38
+ ```
39
+ bi_get_camera_config(short="<short>")
40
+ ```
41
+
42
+ Note `sense`, `contrast`, `recmode`, `aizones`, profile/schedule flags.
43
+ The `_note` field tells you whether you got the admin (deep) or fallback
44
+ (shallow) view.
45
+
46
+ ## Step 3 — Drill into what camconfig doesn't expose
47
+
48
+ Pick the area that matches the symptom:
49
+
50
+ | Symptom | `bi_get_reg` key_path |
51
+ | -------------------------------------- | ---------------------------------------------------- |
52
+ | Wrong AI classification / thresholds | `AI\\<profile>` (smartconf, smartlabels, smartzones) |
53
+ | Trigger zones look wrong | `Motion\\<profile>` (maskbits_*, objmaxpercent10) |
54
+ | Alerts not firing during PTZ preset | `PTZ\\Presets` (noalerts flag per preset) |
55
+ | Dahua IVS not reaching BI | `camevents` (ONVIF event handlers) |
56
+ | Wrong action (no email/webhook) | `Alerts\\OnTrigger` |
57
+
58
+ If `bi_get_reg` returns `meta.stale: true`, stop and ask the user to
59
+ re-export the camera before continuing — stale data will lead you astray.
60
+
61
+ ## Step 4 — Form ONE hypothesis
62
+
63
+ Write it down in user-visible text. Examples:
64
+
65
+ - "AI confidence is 70 in profile 3; person events at night are scoring
66
+ 60-65 because of low contrast — they're below threshold."
67
+ - "Preset 5 has noalerts=1 set, so alerts during that preset are
68
+ deliberately suppressed. Either the preset is wrong or the noalerts
69
+ flag is."
70
+
71
+ Only one hypothesis. If you have multiple, pick the one you can falsify
72
+ fastest.
73
+
74
+ ## Step 5 — Test the hypothesis (mutations path)
75
+
76
+ If `BI_MCP_ALLOW_MUTATIONS` is enabled, you can close the loop:
77
+
78
+ ```
79
+ bi_trigger_camera(camera="<short>", memo="diagnose-<symptom>")
80
+ bi_list_alerts(camera="<short>", limit=1)
81
+ ```
82
+
83
+ The memo should appear on the new alert. If the alert didn't fire, or
84
+ fired with a different classification than you expected, your hypothesis
85
+ is wrong — go back to step 2.
86
+
87
+ If mutations are disabled, ask the user to wave at the camera (or
88
+ otherwise generate motion) and re-run step 1.
89
+
90
+ ## Step 6 — Report
91
+
92
+ User-visible answer should include:
93
+
94
+ - One-line root cause
95
+ - The specific setting (registry key, BI menu path) that's responsible
96
+ - A concrete change to make (don't apply it yourself — the user owns
97
+ the BI UI)
98
+ - Citation: which AGENTS.md / BlueIris_Manual.md section confirms the
99
+ diagnosis
100
+
101
+ ## Anti-patterns
102
+
103
+ - ❌ Walking all the .reg subkeys "in case something looks wrong" —
104
+ pick the one matching the symptom
105
+ - ❌ Forming multiple hypotheses and asking the user which to pursue —
106
+ pick one, test it, iterate
107
+ - ❌ Looping `bi_trigger_camera` — one call, observe, move on
108
+ - ❌ Re-fetching `bi_list_cameras` for identity facts already in
109
+ `project_camera_roster.md`
@@ -0,0 +1,38 @@
1
+ # Blue Iris web server endpoint
2
+ BI_HOST=192.168.1.10
3
+ BI_PORT=81
4
+
5
+ # Dedicated low-privilege Blue Iris user (Settings > Users in BI).
6
+ # Do NOT use your admin account. Give this user LAN access only.
7
+ BI_USER=
8
+ BI_PASS=
9
+
10
+ # Optional admin user — for tools that hit BI cmds gated behind admin rights:
11
+ # bi_get_camera_config (deep camconfig path), bi_list_log, bi_get_sysconfig.
12
+ # Recommended setup: create a SECOND user in Blue Iris with admin enabled.
13
+ # If you leave these blank and your BI_USER above already has admin rights,
14
+ # that user will automatically be used for admin-gated cmds. If neither path
15
+ # yields an admin user, those tools degrade or raise admin_required.
16
+ BI_ADMIN_USER=
17
+ BI_ADMIN_PASS=
18
+
19
+ # Set to 1 to register the mutating tools: bi_trigger_camera, bi_set_ptz_preset,
20
+ # bi_set_profile. With the flag off (default), they're not registered at all.
21
+ # READ AGENTS.md § "Mutation patterns" before enabling.
22
+ BI_MCP_ALLOW_MUTATIONS=0
23
+
24
+ # Optional overrides for bi_get_reg (the .reg parser tool).
25
+ # Defaults are resolved relative to the *current working directory* at the
26
+ # time the tool is called — Claude Code launches the server from the
27
+ # project directory, so by default that's <project>/.reg-venv/ and
28
+ # <project>/cam settings/.
29
+ #
30
+ # Set these to absolute paths if your layout differs:
31
+ # BI_MCP_REG_VENV_PYTHON: absolute path to a Python with python-registry
32
+ # BI_MCP_REG_DIR: absolute path to the directory holding <camera>.reg exports
33
+ #BI_MCP_REG_VENV_PYTHON=
34
+ #BI_MCP_REG_DIR=
35
+
36
+ # Optional: enable verbose logging to stderr + a rotating file.
37
+ # Default is silent. Set to 1 when debugging.
38
+ BI_MCP_DEBUG=0
@@ -0,0 +1,12 @@
1
+ .env
2
+ .venv/
3
+ __pycache__/
4
+ *.pyc
5
+ *.pyo
6
+ dist/
7
+ build/
8
+ *.egg-info/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .mypy_cache/
12
+ .audit/