Skip to content

Scan a real MCP server before release

Historical 1.2.1 walkthrough. Its keyless --allow-degraded commands are fallback evidence, not a review-disable interface. Prepared 1.3.0 adds --rules-only; see installation for the new first-use workflow.

A useful first scan should be repeatable without trusting the target enough to run it. This walkthrough uses PortunusMCP Sentinel 1.2.1 to inspect the official MCP Git server source. It keeps the checkout untouched, selects all seven static rules, and writes reports outside the target. The observed result is zero findings, with one rule skipped. That is a coverage result, not a claim that this server is vulnerability-free.

Prepare an isolated environment

Use Linux, Python 3.12, Git, and pipx. Start in a new working directory. Package installation and cloning need network access; the scans themselves can run offline. No Docker is needed for this walkthrough. The published-package verification used Python 3.12.14 and pipx 1.16.0 inside a fresh Linux container, installing 1.2.1 directly from public PyPI. The launch evidence retains both the earlier candidate-wheel rehearsal and the successful public installation, including validated reports, effective configuration, and hashes.

python3 --version
pipx install portunusmcp-sentinel==1.2.1
sentinel --version
mkdir sentinel-walkthrough
cd sentinel-walkthrough
git clone https://github.com/modelcontextprotocol/servers.git servers
git -C servers checkout --detach d73f99efbfd40c3aa1b61e88728b3d49fb52608f
git -C servers rev-parse HEAD
git -C servers status --porcelain
mkdir reports

The revision must match exactly, and the status command should print nothing. We scan only servers/src/git, not every server in the monorepo. Do not run sentinel init, create permission grants, install the Git server's dependencies, or start its entry point. Sentinel reads source without importing target modules or contacting a running MCP endpoint.

Run the selected static checks

The following POSIX-shell function removes ambient environment variables for each scan. It preserves only the executable search path, so no GPT credentials, SENTINEL_* overrides, or OpenAI routing variables reach Sentinel. This pinned target has no sentinel.toml; built-in defaults apply, including the high failure threshold. The explicit rule list prevents accidental reliance on a narrower selection. Keep these commands in the same shell and directory.

scan_report() {
  format=$1
  code=0
  env -i PATH="$PATH" sentinel scan servers/src/git \
    --static-only --allow-degraded \
    --rules SENT-001,SENT-002,SENT-003,SENT-004,SENT-005,SENT-006,SENT-007 \
    --format "$format" --output "reports/git.$format" || code=$?
  printf '%s\n' "$code" > "reports/git.$format.exit"
  case "$code" in
    0|1) ;;
    *) printf 'Walkthrough verification failed: exit %s\n' "$code" >&2; return "$code" ;;
  esac
}
scan_report json && scan_report sarif

Exit 0 and 1 both mean a completed scan. Exit 1 means a finding reached the failure threshold; it does not mean the scanner malfunctioned. Exit 2 indicates invalid input or configuration, and exit 3 indicates incomplete or failed analysis. Either makes this walkthrough verification unsuccessful: retain the diagnostics and resolve the cause before interpreting any report.

Read the actual result

Both retained scans exited 0, inspected five files, and produced no findings or warnings. Native JSON reports schema 1.6.0; SARIF remains 2.1.0. The JSON and SARIF came from separate invocations, so timestamps and scan IDs differ. Their substantive results agree.

Rules Observed outcome
SENT-001 Skipped: sentinel.permissions.yaml is absent
SENT-002–SENT-007 Evaluated; zero matches
SENT-008–SENT-011 Dynamic probes excluded by --static-only

Do not add a permissions file merely to make the skipped row disappear: that would change the experiment. “Evaluated” also does not mean every handler or possible vulnerability was understood. Rules recognize bounded source patterns; indirection, imported implementations, and behavior outside the scan root can escape their coverage. This scan does not audit the target's installed dependencies or exercise its runtime authorization.

--allow-degraded permits deterministic candidates to remain needs_review when GPT review is unavailable. Here there were no candidates to review and none were confirmed. The report's GPT summary labels its configured mode live, but contains zero batches, zero tokens, and zero current cost; no model call occurred. A successful empty review stage is not GPT endorsement.

Sentinel validates reports before writing them. For an independent offline check, use the Python interpreter in pipx's isolated Sentinel environment:

tool_python="$(pipx environment --value PIPX_LOCAL_VENVS)/portunusmcp-sentinel/bin/python"
"$tool_python" -c 'import json; from sentinel.report.validate_json import validate_report_data; validate_report_data(json.load(open("reports/git.json")))'
"$tool_python" -m sentinel.report.validate_sarif reports/git.sarif
git -C servers status --porcelain
sha256sum reports/git.json reports/git.sarif

Try isolated dynamic proof separately

With Docker running, sentinel demo --replay-review --output-dir demo-results uses the bundled deliberately vulnerable Python fixture. It reuses recorded GPT responses while executing new Docker probes. It does not dynamically test the public Git server above. See the installation requirements and rule boundaries before applying full scans to other targets.

The 2:45 demo by Bashaar Javaid shows older MCP Sentinel v0.1.0 branding and historical GitHub alerts. Its fixture/replay demonstration remains relevant; use this page's 1.2.1 install command and current reports for launch verification. Share reproducible false positives or missed patterns through issues, and sensitive vulnerabilities through the private reporting form.