Skip to main content

API Reference

This page covers the public MCP-Guard HTTP API. Where a deployment exposes interactive docs, you can also browse them at:

  • OpenAPI JSON: GET /openapi.json
  • Swagger UI: GET /docs
  • ReDoc: GET /redoc

Authentication​

MCP-Guard uses JWT-based authentication for all user-facing endpoints.

  • JWT (Bearer): Authorization: Bearer <jwt>
Endpoint GroupAccepts
Process / Scans / QueueJWT
Leaderboard (/leaderboard/*)No auth (public)
Admin (/admin/*)X-Admin-Key: <admin_api_key>

Process​

Endpoints that create a new scan. All scans run asynchronously: the endpoint returns a scan_id immediately, and clients poll GET /scan/{scan_id}/status or GET /scan/{scan_id} to retrieve results.

POST /process​

Scan an uploaded file (zip of source code).

Request: multipart/form-data with a file field. File scans are cached per user for 90 days based on content hash.

Response: ScanCreateResponse.

{
"scan_id": 123,
"status": "pending",
"message": "File scan queued for processing"
}

POST /process/github​

Scan a public GitHub repository.

Request:

{
"github_url": "https://github.com/owner/repo",
"revision": "main"
}
FieldTypeDefaultDescription
github_urlstringrequiredFull GitHub repository URL
revisionstringrepo default branchBranch, tag, or commit SHA. Empty string resolves to the repo's default branch.

Repository size is limited to 100 MB. Results are globally cached for 30 days keyed by resolved commit hash.

Response: ScanCreateResponse.

POST /process/batch_github​

Scan multiple GitHub repositories in one call.

Request:

{
"repositories": [
{ "github_url": "https://github.com/owner/repo-1", "revision": "main" },
{ "github_url": "https://github.com/owner/repo-2", "revision": "" }
]
}

Response: BatchScanResponse.

{
"total": 2,
"successful": 2,
"failed": 0,
"scans": [
{
"scan_id": 123,
"status": "pending",
"message": "GitHub repository ... scan queued for processing",
"github_url": "https://github.com/owner/repo-1",
"revision": "main"
}
],
"failed_repos": []
}

POST /process/mcp_config​

Scan one or more MCP servers from a Claude- or VS Code-style configuration.

Request — Claude format:

{
"mcpServers": {
"my-server": {
"url": "https://mcp.example.com/sse",
"transport": "sse",
"headers": { "Authorization": "Bearer ..." }
},
"local-server": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://..."]
}
}
}

Request — VS Code format:

{
"mcp": {
"servers": {
"my-server": { "url": "https://mcp.example.com/sse", "transport": "sse" }
}
}
}
FieldTypeDefaultDescription
urlstring—Remote server endpoint (required for remote servers; include the full path, e.g. /sse)
transportstringinferredsse or http. Falls back to type for backward compatibility.
typestringinferredSame as transport. If both are set, transport wins.
headersobject{}Headers to forward to the remote server (e.g. Authorization)
commandstring—Executable for stdio servers
argsarray[]Arguments for the executable
envobject{}Environment variables for the stdio server

Each server is queued as a separate scan. If a server requires OAuth, its scan moves to status needs_auth and OAuth metadata is returned via GET /scan/{scan_id}.

Response: BatchScanResponse with one entry per server.

{
"total": 1,
"successful": 1,
"failed": 0,
"scans": [
{
"scan_id": 234,
"status": "pending",
"message": "MCP server 'my-server' scan queued for processing",
"server_name": "my-server"
}
],
"failed_repos": []
}

POST /process/mcp_config_with_auth​

Resume scanning an MCP server after the client has obtained an OAuth access token. Used together with POST /scan/{scan_id}/authenticate (see below).

Headers:

  • X-MCP-Auth-Token: <access_token> (required)

Request:

{
"server_name": "my-server",
"server_config": {
"url": "https://mcp.example.com/sse",
"transport": "sse"
}
}

Response: ScanCreateResponse.

POST /process/tools​

Scan a list of MCP tools without starting an MCP server. Useful when tool definitions are already known.

Request:

{
"server_name": "my-server",
"tools": [
{
"tool_name": "create_payout",
"tool_description": "Create a payout to a receiver email.",
"input_schema": { "type": "object", "properties": { "amount": { "type": "number" } } },
"output_schema": null
}
]
}
FieldTypeRequiredDescription
server_namestringYesLogical server name (used for caching and rug-pull detection)
tools[].tool_namestringYesTool name
tools[].tool_descriptionstringYesTool description
tools[].input_schemaobjectNoJSON Schema for tool inputs
tools[].output_schemaobjectNoJSON Schema for tool outputs

Response:

{
"issuccess": true,
"failed_reason": null,
"server_name": "my-server"
}

Scans​

Endpoints for listing, retrieving, and acting on existing scans. All endpoints require JWT.

GET /scans​

List scans owned by the current user, with pagination, filtering, and sorting.

Query params:

ParamTypeDefaultDescription
pageint1Page number (1-indexed)
page_sizeint10Items per page
statusstring—Comma-separated values: pending, processing, completed, failed, needs_auth
securitystring—Comma-separated levels: low, medium, high
complexitystring—Comma-separated levels: low, medium, high
sensitivitystring—Comma-separated levels: low, medium, high
searchstring—Substring match on filename
start_timestring—ISO datetime, inclusive lower bound on upload_time
end_timestring—ISO datetime, inclusive upper bound on upload_time
sort_fieldstringupload_timefilename, upload_time, status, security, complexity, sensitivity
sort_orderstringdescasc or desc

Response:

{
"scans": [ /* ScanResultResponse[] */ ],
"total": 42,
"page": 1,
"page_size": 10,
"total_pages": 5
}

GET /scans/recent​

Return the most recent scans for the current user (no pagination).

GET /scan/{scan_id}​

Get a single scan with full result data.

Response (ScanResultDetailResponse):

{
"id": 234,
"user_id": "usr_...",
"filename": "MCP:my-server",
"upload_time": "2026-05-19T07:34:37Z",
"status": "completed",
"processing_stage": null,
"success": true,
"error_message": null,
"security": "low",
"complexity": "medium",
"sensitivity": "high",
"vulnerability_count": 0,
"scan_type": "mcp",
"github_url": null,
"mcp_server_count": 1,
"authentication_required": false,
"oauth_metadata": null,
"mcp_config": { "my-server": { "url": "...", "transport": "sse" } },
"prompt_injection_flagged_count": 1,
"prompt_injection_total_count": 4,
"parent_scan_id": null,
"rescannable": true,
"result_data": {
"vulnerabilities": [],
"summary": null,
"textguard_results": [
{
"tool_name": "create_payout",
"result": {
"flagged": true,
"categories": ["Prompt Injection", "Destructive Toxic Flow"],
"category_scores": { "Prompt Injection": 0.85, "Destructive Toxic Flow": 0.92 },
"explanation": { "Prompt Injection": "...", "Destructive Toxic Flow": "..." }
},
"model": "TextGuard-Lite",
"description": "Create a payout to a receiver email. ..."
}
]
},
"security_reasoning": "...",
"complexity_reasoning": "...",
"sensitivity_reasoning": "...",
"content_hash": "..."
}
FieldTypeDescription
scan_typestringfile, github, or mcp
statusstringpending / processing / completed / failed / needs_auth
processing_stagestringOptional human-readable substage (e.g. Detecting Vulnerabilities, Summarizing)
prompt_injection_flagged_countintNumber of tools flagged in this scan
prompt_injection_total_countintTotal tools analyzed
parent_scan_idintPrevious version of this scan when produced via /scan/{id}/rescan
rescannableboolTrue if the scan has stored MCP config and supports rescan

GET /scan/{scan_id}/status​

Lightweight status check.

Response (ScanStatusResponse):

{
"status": "processing",
"queue_position": 0,
"queue_size": 0
}
FieldTypeDescription
statusstringSame values as on ScanResultDetailResponse.status
queue_positionint / null1-indexed position in the queue. 0 if currently processing, null if not queued.
queue_sizeint / nullTotal tasks waiting in the queue

POST /scan/{scan_id}/rescan​

Re-run an MCP scan to detect rug-pull (W003) changes.

Reconnects to the MCP server using the stored config and compares tool descriptions with the previous scan:

  • If descriptions are unchanged, no new scan is created.
  • If anything changed, a new scan is queued with parent_scan_id linking back to the previous version. W003 (Entity Changed) is evaluated on every tool whose description differs.

Only works on scans where rescannable: true (i.e. created via /process/mcp_config with a stored config).

Response when changes are detected: ScanCreateResponse. Response when nothing changed:

{ "status": "unchanged" }

Queue​

GET /queue/status​

Get the current state of the scan worker queue.

Response (QueueStatusResponse):

{
"queue_size": 2,
"processing_count": 1,
"pending_scan_ids": [342, 343],
"processing_scan_ids": [341],
"max_workers": 3
}

Admin​

Admin endpoints are gated by a shared admin key. They are not authenticated with JWT and are excluded from /openapi.json.

Headers:

  • X-Admin-Key: <admin_api_key> (required; set on the server via the ADMIN_API_KEY environment variable)

GET /admin/usage/scan/{scan_id}​

Get LLM token usage and cost for a single scan.

Response:

{
"scan_id": 234,
"user_id": "usr_...",
"filename": "MCP:my-server",
"status": "completed",
"prompt_tokens": 2475,
"completion_tokens": 121,
"total_tokens": 2596,
"llm_cost": 0.00430375,
"upload_time": "2026-05-19T07:34:37.957970+00:00"
}

GET /admin/usage/user/{user_id}​

Aggregate usage for a single user across all completed scans.

Response:

{
"user_id": "usr_...",
"prompt_tokens": 5071670,
"completion_tokens": 81051,
"total_tokens": 5152721,
"llm_cost": 18.265861,
"scan_count": 30
}

GET /admin/usage/summary​

Global usage summary across all users.

Response:

{
"user_count": 25,
"scan_count": 313,
"prompt_tokens": 5071670,
"completion_tokens": 81051,
"total_tokens": 5152721,
"llm_cost": 18.265861
}

Health​

GET /​

API root. No authentication required.

{ "message": "MCPScan API", "version": "1.0.0" }

GET /health​

Health check. No authentication required.

{ "status": "healthy", "service": "MCPScan API" }