MCPSafe.io
RegistryThreatsMethodologyDocsPricingScanSign in
⌘K
  • Getting Started
  • Quickstart

API Reference

  • Overview
  • POST /scan
  • GET /scan/:id
  • Private scans
  • Team & billing

Integrations

  • GitHub Actions
  • Cursor
  • Claude Code

Concepts

  • AIVSS Scoring
  • Findings
  • Severity Levels
  • CLI
  • Troubleshooting
  • FAQ
⌘K
MCPSafe.io

Security checks for MCP servers — public packages and private repos, fast or deep.

Legal

Privacy PolicyCookie PolicyTerms of ServiceSecurity disclosure

Resources

State of MCP SecuritySupportSystem statusMade in Germany 🇩🇪

© 2026 MCPSafe. All rights reserved.

GDPR — Privacy Policy

CLI

Scripting MCPSafe scans from the shell — using the REST API directly today, with a dedicated mcpsafe binary on the roadmap.

There is no mcpsafe binary to install today. Scan packages from the shell by calling the REST API directly — the script below covers the common case (submit, poll, fail on bad grade).

mcpsafe.sh — submit, poll, fail on D/F

Drop this into a CI pipeline or local shell script. Exits non-zero on a D or F grade so it can gate a deploy.

#!/usr/bin/env bash
# mcpsafe.sh — scan a package and exit non-zero on D/F.
# Usage: ./mcpsafe.sh "@modelcontextprotocol/server-github" [fast|deep]
set -euo pipefail
 
INPUT="${1:?package or repo required}"
MODE="${2:-fast}"
API="${MCPSAFE_API:-https://api.mcpsafe.io}"
KEY="${MCPSAFE_API_KEY:-}"   # optional; raises rate limits
 
AUTH=()
[[ -n "$KEY" ]] && AUTH=(-H "Authorization: Bearer ${KEY}")
 
# 1. Submit
SCAN_ID=$(curl -fsS -X POST "${API}/api/v1/scan" \
  "${AUTH[@]}" \
  -H "Content-Type: application/json" \
  -d "{\"input\":\"${INPUT}\",\"mode\":\"${MODE}\"}" \
  | jq -r '.data.scan_id // .scan_id')
 
[[ -z "$SCAN_ID" || "$SCAN_ID" == "null" ]] && { echo "submit failed"; exit 2; }
echo "scan_id=${SCAN_ID}"
 
# 2. Poll until status=complete (3 s interval, 30 min cap)
DEADLINE=$(( $(date +%s) + 1800 ))
while [[ $(date +%s) -lt $DEADLINE ]]; do
  RESULT=$(curl -fsS "${API}/scan/${SCAN_ID}")
  STATUS=$(jq -r '.status // "in_progress"' <<<"$RESULT")
  case "$STATUS" in
    complete) break ;;
    failed)   echo "scan failed: $(jq -r '.error_message // "unknown"' <<<"$RESULT")"; exit 2 ;;
    *)        sleep 3 ;;
  esac
done
[[ "$STATUS" != "complete" ]] && { echo "scan timed out"; exit 2; }
 
# 3. Print + gate
GRADE=$(jq -r '.safety_grade' <<<"$RESULT")
SCORE=$(jq -r '.safety_score' <<<"$RESULT")
echo "grade=${GRADE} score=${SCORE} url=https://mcpsafe.io/scan/${SCAN_ID}"
 
case "$GRADE" in
  D|F) exit 1 ;;
  *)   exit 0 ;;
esac

Calling it

chmod +x mcpsafe.sh
 
# anonymous fast scan
./mcpsafe.sh "@modelcontextprotocol/server-github"
 
# authenticated deep scan
export MCPSAFE_API_KEY="mcpsafe_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
./mcpsafe.sh "github.com/owner/private-mcp" deep

Streaming variant

For real-time feedback, swap the polling loop for the SSE stream — see Server-Sent Events. Browser EventSource callers can authenticate via the ?token= query parameter since custom headers aren't allowed.

In CI

A pre-built workflow is at GitHub Actions; a Bash equivalent is at Claude Code (the audit-mcp.sh example reads .mcp.json and scans every server).

Native binary roadmap

A dedicated mcpsafe CLI is scoped but not committed to a release date. Planned commands include:

mcpsafe scan @modelcontextprotocol/server-github
mcpsafe scan --config ~/.cursor/mcp.json
mcpsafe watch .mcp.json
mcpsafe history
mcpsafe export <scan_id> --format json|sarif

We'll announce on the changelog when the first release ships. Until then, the script above does the same job for almost every CI use case.

ℹ

Why no binary today?

Every CI integration we've seen so far is satisfied by curl + jq. Shipping a binary just to wrap that adds maintenance burden (signed releases, multi-arch builds, package-manager publishing) without expanding what's possible. We'll ship one when there's a feature it unlocks (offline scanning, local rule packs) — not before.

←PreviousSeverity LevelsNextTroubleshooting→