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
urlstringRemote 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)
commandstringExecutable 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
statusstringComma-separated values: pending, processing, completed, failed, needs_auth
securitystringComma-separated levels: low, medium, high
complexitystringComma-separated levels: low, medium, high
sensitivitystringComma-separated levels: low, medium, high
searchstringSubstring match on filename
start_timestringISO datetime, inclusive lower bound on upload_time
end_timestringISO 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" }