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

API Overview

MCPSafe REST API — base URL, authentication, scopes, IP allowlist, rate limits, and response format.

The MCPSafe API is a REST API that returns JSON. You can use it to scan packages programmatically, retrieve results, and integrate into CI/CD pipelines.

Looking for the formal spec? Every endpoint is documented in the interactive OpenAPI reference (Redoc + try-it-out).

Want to try it now? Import the Postman collection — pre-wired endpoints, auth header, and example bodies. After import, set the api_key collection variable to a key minted in your API keys dashboard.

Base URL

https://api.mcpsafe.io

All paths in this documentation are relative to the base URL.

Authentication

Two authentication methods, depending on the route:

API keys (programmatic — CI/CD, scripts, integrations)

Mint a key from your API keys dashboard and pass it as a Bearer token. Keys start with the mcpsafe_ prefix and are only shown once at creation — store them in your secret manager immediately.

curl https://api.mcpsafe.io/api/v1/scan \
  -X POST \
  -H "Authorization: Bearer mcpsafe_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"input": "@modelcontextprotocol/server-everything", "mode": "fast"}'

API keys are a paid feature (Developer, Team, Business plans). Free-tier accounts get a 402 SUBSCRIPTION_REQUIRED if they try to mint one.

ℹ

Legacy mcp_ prefix

Keys minted before 2026-04-29 use the mcp_ prefix. They keep working until they expire (max 365 days from mint date). New keys use mcpsafe_ and the legacy prefix is fully retired on 2027-04-29.

Session JWT (web app)

Routes consumed directly by the web UI accept a NextAuth session token. You typically don't need this — the web app handles it for you.

Scopes

Each API key carries one or more scopes. The authorizer rejects requests where the route's required scope isn't held. Pick the narrowest set that covers your use case — a leaked key only does what its scopes allow.

Granular scopes (preferred for new keys)

ScopeAllows
scan:submitPOST /api/v1/scan — submit a scan
scan:listGET /api/v1/scans — list your scan history
scan:getGET /api/v1/scan/{id} — read a specific scan result
registry:readGET /api/v1/registry/* — browse the public registry
watchlist:readreserved — GET /api/v1/watchlist/* will land in a later release

Umbrella scopes (legacy, still accepted)

ScopeEquivalent to
scan:readscan:list + scan:get + registry:read + watchlist:read
scan:writescan:submit + everything in scan:read

A key minted only with scan:list cannot submit scans even if the bearer is otherwise authenticated. The dashboard defaults new keys to scan:submit + scan:list + scan:get — the typical CI/CD shape (submit, then poll). Add scan:write (or pick scan:submit + scan:read) for full read+write.

IP allowlist

When creating a key, you can optionally pin it to a list of source IP CIDRs. Requests from any IP outside the list are rejected with the same shape as missing scope (no information leak about which check failed).

  • Up to 20 CIDR entries per key
  • Validated server-side with ipaddress.ip_network
  • Empty list = no restriction (the default; current behavior)
  • IPv6 source IPs aren't currently honored — keys with allowlists fall back closed for IPv6 traffic, so don't enable allowlists if your client is IPv6-only

This is high-leverage for CI runners, fixed bastions, and office VPNs. Less useful for laptops on dynamic IPs.

Anomaly detection

The first time an API key is used from a /24 prefix it hasn't been seen from before, the user gets a "new location" email with the source IP and key label. Subsequent requests from the same /24 don't re-trigger. This catches leaked keys quickly without spamming users on legitimate IP changes.

If you ever get a "new location" email you didn't expect, revoke the key from the dashboard immediately.

Response envelope

Most responses are wrapped in a standard envelope:

{
  "success": true,
  "data": { ... }
}

Errors follow the same shape with success: false:

{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit reached. Please try again in 15 seconds.",
    "details": {
      "window": "minute",
      "tier": "dev",
      "current": 11,
      "limit": 10,
      "retry_after_seconds": 15
    }
  }
}

Stable v1 endpoints under /api/v1/* (registry, stats, recent scans) return data directly per the OpenAPI spec — see the interactive reference for exact shapes.

Rate limits

API-key requests are bucketed at two layers:

  1. Per key — each key has its own minute / day / week / month / deep_month buckets. A leaked key consumes its own quota; your other keys are isolated.
  2. Per user (shared with web UI) — scans across all of your keys plus the web UI count against a single per-tier user budget. Minting more keys doesn't multiply your effective quota — splitting one workload across 10 keys hits the user budget before any single-key cap.

Caps depend on your subscription tier. Monthly is the binding cost ceiling — daily and weekly allow bursts within it.

TierPublic scans / monthDeep scans / monthBurst (req / min)
Anonymous (no auth)— (10 / day cap)03
Free (signed in)— (20 / day cap)6 / week5
Developer2002010
Team2,0006020
Business20,00018030

Anonymous and Free tiers have no monthly cap — they're bucketed at the day/week level so casual scanning stays free, but sustained throughput requires a paid plan. Anonymous deep scans are blocked at the API layer (sign in to enable).

The per-key cap and per-user budget use the same numbers; the per-key bucket exists for leak isolation, not extra quota.

Private scans have separate, smaller budgets because they cost more to run:

TierPrivate scans / monthPrivate deep / month
Developer7020
Team70040
Business4,000120

An edge abuse-protection rule also blocks any single Authorization header value making more than 8,000 requests per 5 minutes on /api/v1/*. This is well above any legitimate paid usage and only fires on abuse patterns.

⚠

429 Too Many Requests

When rate-limited, retry after the Retry-After header (in seconds). The response body's error.details includes window, current, limit, and retry_after_seconds so you can pace yourself precisely.

Headers X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset are emitted on every API-key response (including successful ones) so CI clients can pace before hitting 429.

Key expiry & rotation

Every API key has an expiry date. Default 90 days, maximum 365 days. There is no "never expires" option — rotation is mandatory hygiene. Revoke from the dashboard the moment you suspect a key has leaked, then mint a new one with a fresh prefix.

last_used is recorded on every authenticated request (throttled to one write per minute per key) so you can spot stale keys before purging them.

A maximum of 10 active keys per account is enforced at create time. Revoke unused keys to free slots.

Endpoints

Scan submission & results

MethodPathAuthDescription
POST/api/v1/scanAPI key (scan:submit or scan:write)Submit a scan
GET/api/v1/scan/{id}API key (scan:get or scan:read)Read scan result by ID
GET/api/v1/scansAPI key (scan:list or scan:read)List your public scan history
GET/scan/{id}NoneRead scan result by ID (unauthenticated, public-only)
GET/scan/{id}/streamNoneSSE progress stream
GET/scan/{id}/consensusNoneLLM judge panel verdicts (deep scans only)
GET/scansSession JWTList your public scan history (web UI)
GET/user/scansSession JWTList your private scan history
GET/versionsNoneVersion history for a package

Stable v1 endpoints

MethodPathAuthDescription
GET/api/v1/registryNoneBrowse the public registry
GET/api/v1/registry/{source}/{package}NoneSingle registry record
GET/api/v1/scans/recentNoneRecently completed public scans
GET/api/v1/rules/catalogNoneLive snapshot of the rule catalog
GET/api/v1/statsNoneEcosystem stats (packages scanned, rules live)
GET/api/v1/stats/state-of-mcpNoneState of MCP report data
GET/api/v1/compareNoneSide-by-side package comparison

Public endpoints (Auth = None) don't require a key, but if you supply one, your usage still counts toward the per-user aggregate cap.

Team & billing

Team management, invites, billing, and Stripe-portal sessions are exposed under /team/*, /billing/*, /checkout/session, /subscription, and /me. These endpoints authenticate with a NextAuth session JWT, not an API key — they're intended for the web app. See Team & billing for the full surface.

See POST /scan, GET /scan/:id, Private scans, and Team & billing for full details.

←PreviousQuickstartNextPOST /scan→