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.
- bi_mcp-0.2.0/.claude/hooks/run_tests_advisory.sh +56 -0
- bi_mcp-0.2.0/.claude/settings.json +16 -0
- bi_mcp-0.2.0/.claude/skills/bi-troubleshoot/SKILL.md +109 -0
- bi_mcp-0.2.0/.env.example +38 -0
- bi_mcp-0.2.0/.gitignore +12 -0
- bi_mcp-0.2.0/AGENTS.md +596 -0
- bi_mcp-0.2.0/LICENSE +21 -0
- bi_mcp-0.2.0/PKG-INFO +433 -0
- bi_mcp-0.2.0/README.md +408 -0
- bi_mcp-0.2.0/pyproject.toml +48 -0
- bi_mcp-0.2.0/scripts/reg_decoder_coverage.md +741 -0
- bi_mcp-0.2.0/scripts/reg_decoder_coverage.py +322 -0
- bi_mcp-0.2.0/scripts/smoke_concurrent_set_camera.py +101 -0
- bi_mcp-0.2.0/scripts/smoke_motion_config.py +189 -0
- bi_mcp-0.2.0/scripts/smoke_verify_inconclusive.py +203 -0
- bi_mcp-0.2.0/src/bi_mcp/__init__.py +1 -0
- bi_mcp-0.2.0/src/bi_mcp/__main__.py +22 -0
- bi_mcp-0.2.0/src/bi_mcp/cli.py +157 -0
- bi_mcp-0.2.0/src/bi_mcp/client.py +469 -0
- bi_mcp-0.2.0/src/bi_mcp/errors.py +314 -0
- bi_mcp-0.2.0/src/bi_mcp/logging_setup.py +92 -0
- bi_mcp-0.2.0/src/bi_mcp/reg.py +208 -0
- bi_mcp-0.2.0/src/bi_mcp/server.py +121 -0
- bi_mcp-0.2.0/src/bi_mcp/shapers.py +1441 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/__init__.py +41 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/registry.py +93 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_actionset.py +93 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_alerts.py +168 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_audit_actions.py +641 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_cameras.py +237 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_clips.py +104 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_explain_alert.py +926 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_log.py +199 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_mutations.py +2055 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_ptz.py +56 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_reg.py +95 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_status.py +142 -0
- bi_mcp-0.2.0/src/bi_mcp/tools/tools_timeline.py +53 -0
- bi_mcp-0.2.0/src/bi_mcp/utils/__init__.py +0 -0
- bi_mcp-0.2.0/src/bi_mcp/utils/logging.py +72 -0
- bi_mcp-0.2.0/src/bi_mcp/utils/time.py +46 -0
- bi_mcp-0.2.0/tests/__init__.py +0 -0
- bi_mcp-0.2.0/tests/conftest.py +32 -0
- bi_mcp-0.2.0/tests/unit/__init__.py +0 -0
- bi_mcp-0.2.0/tests/unit/test_mutation_gate.py +87 -0
- bi_mcp-0.2.0/tests/unit/test_reg_parser.py +178 -0
- bi_mcp-0.2.0/tests/unit/test_shape_camlist_stale_fencing.py +109 -0
- bi_mcp-0.2.0/tests/unit/test_shape_ptz_inconsistencies.py +98 -0
- bi_mcp-0.2.0/tests/unit/test_shape_reg_mask_omission.py +89 -0
- bi_mcp-0.2.0/tests/unit/test_shape_reg_profile_sync.py +299 -0
- bi_mcp-0.2.0/tests/unit/test_shape_reg_retriggers_label.py +108 -0
- bi_mcp-0.2.0/tests/unit/test_shape_status_profile_name.py +77 -0
- bi_mcp-0.2.0/tests/unit/test_shapers_actionset.py +247 -0
- 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
|