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

Private scans

Scan private GitHub repos, npm packages, PyPI packages, Docker Hub images, and GHCR images with a read-only token.

A private scan runs the same pipeline as a public scan but the result stays in your account — no public permalink, no entry in the public registry, never readable by anyone else. Required for any scan that needs to download a non-public artifact.

Available on Developer, Team, and Business plans. A signed-in free-tier user submitting a private scan gets 402 SUBSCRIPTION_REQUIRED.

When to use

  • A GitHub repository that requires authentication to clone
  • A package published to a private npm registry scope
  • A PyPI package on a private index
  • A Docker Hub or GHCR image that requires login
  • Any scan you don't want appearing in the public registry, even for an otherwise-public package

Visibility is fixed at submission time — you can't flip a public scan to private (or vice versa) after it runs.

Supported targets and token formats

Targetcredentials.typeToken formatMinimum scope
GitHubgithubghp_… (classic) or github_pat_… (fine-grained)Read-only on Contents + Metadata for the target repo
npmnpmnpm_… (granular access token)read-only access to the target package
PyPIpypipypi-…Read-only on the project
Docker HubdockerPersonal access tokenPublic Repo Read
GHCR (GitHub Container Registry)ghcrSame ghp_… / github_pat_… as GitHubread:packages

The type field must match the resolved input — submitting a github token against pypi:requests returns 400 CREDENTIAL_TYPE_MISMATCH before a scan slot is consumed.

Custom registries (Artifactory, Nexus, GitHub Packages on a custom URL) and self-hosted Git providers are on the roadmap.

Submitting a private scan

curl -X POST https://api.mcpsafe.io/api/v1/scan \
  -H "Authorization: Bearer mcpsafe_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "github:my-org/private-mcp",
    "mode": "deep",
    "scan_visibility": "private",
    "credentials": { "type": "github", "token": "ghp_xxxxxxxxxxxxxxxxxxxx" }
  }'

The response shape is identical to a public scan — scan_id and canonical_id only. Read the result with GET /api/v1/scan/{id} (your API key) or GET /scan/{id} from a signed-in browser session.

Docker Hub / GHCR (username + token)

-d '{
  "input": "docker:my-org/private-image:1.4.2",
  "scan_visibility": "private",
  "credentials": {
    "type": "docker",
    "username": "my-bot",
    "token": "dckr_pat_xxxxxxxxxxxxxxxxxxxx"
  }
}'

What the token sees and what's logged

  • Used once. The Lambda that downloads the artifact reads the token from the Step Functions execution input, performs the download, then drops it. No downstream stage sees it.
  • Never written to DynamoDB. Not in the queue state, not in scan history, not in the result cache. The credentials object is scrubbed from every log line before it reaches CloudWatch.
  • Encrypted at rest in transit. SFN execution inputs are encrypted by AWS with per-account KMS keys.
  • Not stored after the scan. A rescan submits the credential again from your side — there's no "saved tokens" feature.

If a scan fails, the failure event includes the rule path and error code but never echoes the credential.

Reading private results

PathAuthWhat it returns
GET /api/v1/scan/{id}API key with scan:get or scan:readSame as public, plus scan_visibility: "private"
GET /scan/{id}Session JWT (web app)Same
GET /user/scansSession JWTPaginated history of your private scans
GET /api/v1/scansAPI key with scan:listPaginated history of your public scans (the API-key path doesn't surface private rows yet)

Team-shared visibility: members of a Team or Business plan see all private scans submitted by other members of the same org — see Team API.

Rate limits

Private scans have a separate, smaller monthly budget than public scans because they cost more to run (token-driven downloads bypass the public registry cache).

TierPrivate / monthPrivate deep / month
Developer7020
Team70040
Business4,000120

Hitting the cap returns 429 RATE_LIMITED with error.details.window: "month". See API Overview — Rate limits for the full picture.

Error codes specific to private scans

CodeHTTPMeaning
PRIVATE_REQUIRES_SIGNIN401scan_visibility: "private" was requested without authentication
SUBSCRIPTION_REQUIRED402Free-tier account tried to submit a private scan
CREDENTIALS_PUBLIC_SCAN400credentials was supplied on a scan_visibility: "public" scan — credentials are private-scan only
INVALID_CREDENTIALS400Wrong shape or empty token
CREDENTIAL_TYPE_MISMATCH400credentials.type doesn't match the resolved target
ORG_RATE_LIMITED429Org-shared monthly cap reached (Team / Business plans)

Token rotation

Treat MCPSafe scan tokens like any other CI credential — rotate on a schedule (90 days is a reasonable default), revoke immediately on suspected leak, and never reuse the same token across MCPSafe and other services. We log the source IP of every API request and email you on first use from a new /24 prefix; if you get an unexpected "new location" email after a private scan, rotate the token.

←PreviousGET /scan/:idNextTeam & billing→