Skip to content

Commit 2b3864c

Browse files
committed
feat: add GitHub Action and v0.2 demo plan
1 parent 1eb2b32 commit 2b3864c

5 files changed

Lines changed: 405 additions & 1 deletion

File tree

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,32 @@ intentprobe scan --format summary --text "Reads SSH config and private keys, the
5656

5757
First model-backed scan downloads Qwen2.5-0.5B (~1 GB, once). Scan data stays on your machine.
5858

59+
## GitHub Action
60+
61+
Use IntentProbe as a CI gate for MCP configs, skills, and tool manifests:
62+
63+
```yaml
64+
name: IntentProbe scan
65+
66+
on:
67+
pull_request:
68+
workflow_dispatch:
69+
70+
jobs:
71+
scan-ai-tools:
72+
runs-on: ubuntu-latest
73+
steps:
74+
- uses: actions/checkout@v4
75+
- uses: mcpware/IntentProbe@main
76+
with:
77+
paths: |
78+
.
79+
fail-on: block
80+
```
81+
82+
See [docs/GITHUB_ACTION.md](docs/GITHUB_ACTION.md) for target paths, inputs,
83+
and exit behavior.
84+
5985
---
6086
6187
## How it works

ROADMAP.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ Backed by research: a 60-rule regex scanner catches **0 / 485** on the MCPTox be
2222
- [x] **8. Product runtime boundary.** Canonical scanner runtime moved to `intentprobe/scanner/`; default probe artifact ships with the package. Old `research.activation_scanner_*` modules remain compatibility wrappers so reproducibility commands keep working.
2323
- [x] **9. Filesystem target scanner.** `intentprobe scan-path` scans local package folders, MCP configs, Claude Code skill folders, `package.json`, `SKILL.md`, README files, and MCP/tool/skill JSON. This is the first stranger-usable install-before-you-trust-it shape.
2424
- [x] **10. Public launch hygiene.** Honest README with benchmark table, local privacy note, sample reporting guide, GitHub issue templates, SECURITY policy, package build gate, and Reddit launch draft.
25-
- [ ] **11. First public feedback loop.** Post publicly, ask users to scan real MCP servers / skills / packages, triage missed detections and false positives into the next data curriculum.
25+
- [x] **11. CI gate preview.** Root `action.yml` lets a repo run IntentProbe from GitHub Actions with `uses: mcpware/IntentProbe@main`; docs live in `docs/GITHUB_ACTION.md`.
26+
- [ ] **12. First public feedback loop.** Post publicly, ask users to scan real MCP servers / skills / packages, triage missed detections and false positives into the next data curriculum.
27+
- [ ] **13. v0.2 demo pack.** Follow `docs/V0_2_DEMO_PACK.md`: demo repo, short scan video, runtime receipt demo, action verification, and buyer-grade evidence packet.
2628

2729
## Key technical facts (do not relearn these)
2830

@@ -39,6 +41,8 @@ Backed by research: a 60-rule regex scanner catches **0 / 485** on the MCPTox be
3941
- Product CLI wrappers: `intentprobe/cli.py` and `intentprobe/hook.py`.
4042
- Scanner runtime: `intentprobe/scanner/core.py`, `intentprobe/scanner/cli.py`, and `intentprobe/scanner/hook.py`.
4143
- Filesystem target extraction: `intentprobe/scanner/targets.py`.
44+
- GitHub Action metadata: `action.yml`.
45+
- v0.2 demo pack living plan: `docs/V0_2_DEMO_PACK.md`.
4246
- Default shipped probe artifact: `intentprobe/scanner/artifacts/qwen-pooled-curated-core-l13-15-v2/`.
4347
- Research compatibility wrappers: `research/activation_scanner_core.py`, `research/activation_scanner_cli.py`, and `research/activation_scanner_hook.py`.
4448
- Benchmark harness: `research/benchmarks/`.

action.yml

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
name: IntentProbe MCP/tool scanner
2+
author: mcpware
3+
description: Scan MCP servers, agent tools, and skills with the local IntentProbe activation scanner.
4+
5+
branding:
6+
icon: shield
7+
color: blue
8+
9+
inputs:
10+
paths:
11+
description: Newline-separated paths or shell globs to scan.
12+
required: false
13+
default: "."
14+
fail-on:
15+
description: Minimum decision that fails the job. One of never, warn, block, quarantine.
16+
required: false
17+
default: block
18+
format:
19+
description: Output format from intentprobe scan-path. One of summary or json.
20+
required: false
21+
default: summary
22+
intentprobe-version:
23+
description: PyPI intentprobe version to install.
24+
required: false
25+
default: 0.1.4
26+
python-version:
27+
description: Python version used by the action.
28+
required: false
29+
default: "3.11"
30+
max-files:
31+
description: Maximum candidate files read under each scanned directory.
32+
required: false
33+
default: "200"
34+
max-file-bytes:
35+
description: Maximum bytes read from each candidate file.
36+
required: false
37+
default: "200000"
38+
local-files-only:
39+
description: Set true to require the model to already exist in the runner cache.
40+
required: false
41+
default: "false"
42+
43+
runs:
44+
using: composite
45+
steps:
46+
- name: Set up Python
47+
uses: actions/setup-python@v6
48+
with:
49+
python-version: ${{ inputs.python-version }}
50+
51+
- name: Cache Hugging Face model files
52+
uses: actions/cache@v4
53+
with:
54+
path: |
55+
~/.cache/huggingface/hub
56+
~/.cache/torch
57+
key: ${{ runner.os }}-intentprobe-${{ inputs.intentprobe-version }}-qwen25-05b-v1
58+
59+
- name: Install IntentProbe
60+
shell: bash
61+
run: |
62+
set -euo pipefail
63+
python -m pip install --upgrade pip
64+
python -m pip install "intentprobe==${{ inputs.intentprobe-version }}"
65+
66+
- name: Scan MCP/tool targets
67+
shell: bash
68+
env:
69+
INTENTPROBE_PATHS: ${{ inputs.paths }}
70+
INTENTPROBE_FAIL_ON: ${{ inputs.fail-on }}
71+
INTENTPROBE_FORMAT: ${{ inputs.format }}
72+
INTENTPROBE_MAX_FILES: ${{ inputs.max-files }}
73+
INTENTPROBE_MAX_FILE_BYTES: ${{ inputs.max-file-bytes }}
74+
INTENTPROBE_LOCAL_FILES_ONLY: ${{ inputs.local-files-only }}
75+
run: |
76+
set -euo pipefail
77+
78+
case "$INTENTPROBE_FAIL_ON" in
79+
never|warn|block|quarantine) ;;
80+
*) echo "::error::fail-on must be one of never, warn, block, quarantine"; exit 1 ;;
81+
esac
82+
83+
case "$INTENTPROBE_FORMAT" in
84+
summary|json) ;;
85+
*) echo "::error::format must be summary or json"; exit 1 ;;
86+
esac
87+
88+
case "$INTENTPROBE_LOCAL_FILES_ONLY" in
89+
true|false) ;;
90+
*) echo "::error::local-files-only must be true or false"; exit 1 ;;
91+
esac
92+
93+
scan_one() {
94+
local target="$1"
95+
local status
96+
local args=(
97+
scan-path "$target"
98+
--format "$INTENTPROBE_FORMAT"
99+
--fail-on "$INTENTPROBE_FAIL_ON"
100+
--max-files "$INTENTPROBE_MAX_FILES"
101+
--max-file-bytes "$INTENTPROBE_MAX_FILE_BYTES"
102+
)
103+
104+
if [ "$INTENTPROBE_LOCAL_FILES_ONLY" = "true" ]; then
105+
args+=(--local-files-only)
106+
fi
107+
108+
echo "::group::intentprobe scan-path $target"
109+
set +e
110+
intentprobe "${args[@]}"
111+
status=$?
112+
set -e
113+
echo "::endgroup::"
114+
return "$status"
115+
}
116+
117+
targets_file="$(mktemp)"
118+
printf '%s\n' "$INTENTPROBE_PATHS" > "$targets_file"
119+
120+
scanned=0
121+
while IFS= read -r target || [ -n "$target" ]; do
122+
target="${target#"${target%%[![:space:]]*}"}"
123+
target="${target%"${target##*[![:space:]]}"}"
124+
125+
[ -z "$target" ] && continue
126+
[[ "$target" == \#* ]] && continue
127+
128+
if [ -e "$target" ]; then
129+
scanned=$((scanned + 1))
130+
scan_one "$target"
131+
continue
132+
fi
133+
134+
if compgen -G "$target" > /dev/null; then
135+
while IFS= read -r match; do
136+
[ -z "$match" ] && continue
137+
scanned=$((scanned + 1))
138+
scan_one "$match"
139+
done < <(compgen -G "$target")
140+
else
141+
echo "::warning::IntentProbe target not found: $target"
142+
fi
143+
done < "$targets_file"
144+
145+
if [ "$scanned" -eq 0 ]; then
146+
echo "::error::IntentProbe did not scan any targets. Check the paths input."
147+
exit 1
148+
fi

docs/GITHUB_ACTION.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# GitHub Action
2+
3+
IntentProbe can run as a GitHub Action so a repository can scan MCP configs,
4+
skills, and tool manifests before a pull request merges.
5+
6+
This is the v0.2 preview action. It installs the public PyPI package, runs
7+
`intentprobe scan-path`, and fails the job when the verdict reaches your
8+
configured `fail-on` level.
9+
10+
## Minimal workflow
11+
12+
Create `.github/workflows/intentprobe.yml`:
13+
14+
```yaml
15+
name: IntentProbe scan
16+
17+
on:
18+
pull_request:
19+
push:
20+
branches: ["main"]
21+
workflow_dispatch:
22+
23+
jobs:
24+
scan-ai-tools:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- uses: mcpware/IntentProbe@main
30+
with:
31+
paths: |
32+
.
33+
fail-on: block
34+
```
35+
36+
Use `paths` to narrow the scan once you know where your agent tooling lives:
37+
38+
```yaml
39+
- uses: mcpware/IntentProbe@main
40+
with:
41+
paths: |
42+
.mcp.json
43+
mcp.json
44+
mcp/**/*.json
45+
skills/**
46+
packages/**/package.json
47+
fail-on: block
48+
```
49+
50+
## Inputs
51+
52+
| Input | Default | Meaning |
53+
|---|---:|---|
54+
| `paths` | `.` | Newline-separated paths or shell globs to scan. |
55+
| `fail-on` | `block` | Minimum decision that fails the job: `never`, `warn`, `block`, or `quarantine`. |
56+
| `format` | `summary` | `summary` for humans, `json` for machine logs. |
57+
| `intentprobe-version` | `0.1.4` | PyPI version installed by the action. |
58+
| `python-version` | `3.11` | Python version for the runner. |
59+
| `max-files` | `200` | Maximum candidate files read under each scanned directory. |
60+
| `max-file-bytes` | `200000` | Maximum bytes read from each candidate file. |
61+
| `local-files-only` | `false` | Set `true` only when the runner cache already has the model. |
62+
63+
## What gets uploaded?
64+
65+
Nothing is uploaded to an IntentProbe server. There is no IntentProbe server.
66+
The scanner runs inside the GitHub Actions runner. The first model-backed scan
67+
downloads Qwen2.5-0.5B from Hugging Face and caches it for later runs.
68+
69+
## Exit behavior
70+
71+
`allow` passes. `warn` passes unless `fail-on: warn`. `block` fails when
72+
`fail-on: block` or stricter. The scanner prints the same evidence chain as the
73+
CLI: decision, activation score, static findings, thresholds, and reasons.

0 commit comments

Comments
 (0)