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 Group | Accepts |
|---|---|
| Process / Scans / Queue | JWT |
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"
}
| Field | Type | Default | Description |
|---|---|---|---|
github_url | string | required | Full GitHub repository URL |
revision | string | repo default branch | Branch, 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" }
}
}
}
| Field | Type | Default | Description |
|---|---|---|---|
url | string | — | Remote server endpoint (required for remote servers; include the full path, e.g. /sse) |
transport | string | inferred | sse or http. Falls back to type for backward compatibility. |
type | string | inferred | Same as transport. If both are set, transport wins. |
headers | object | {} | Headers to forward to the remote server (e.g. Authorization) |
command | string | — | Executable for stdio servers |
args | array | [] | Arguments for the executable |
env | object | {} | 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
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
server_name | string | Yes | Logical server name (used for caching and rug-pull detection) |
tools[].tool_name | string | Yes | Tool name |
tools[].tool_description | string | Yes | Tool description |
tools[].input_schema | object | No | JSON Schema for tool inputs |
tools[].output_schema | object | No | JSON 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:
| Param | Type | Default | Description |
|---|---|---|---|
page | int | 1 | Page number (1-indexed) |
page_size | int | 10 | Items per page |
status | string | — | Comma-separated values: pending, processing, completed, failed, needs_auth |
security | string | — | Comma-separated levels: low, medium, high |
complexity | string | — | Comma-separated levels: low, medium, high |
sensitivity | string | — | Comma-separated levels: low, medium, high |
search | string | — | Substring match on filename |
start_time | string | — | ISO datetime, inclusive lower bound on upload_time |
end_time | string | — | ISO datetime, inclusive upper bound on upload_time |
sort_field | string | upload_time | filename, upload_time, status, security, complexity, sensitivity |
sort_order | string | desc | asc 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": "..."
}
| Field | Type | Description |
|---|---|---|
scan_type | string | file, github, or mcp |
status | string | pending / processing / completed / failed / needs_auth |
processing_stage | string | Optional human-readable substage (e.g. Detecting Vulnerabilities, Summarizing) |
prompt_injection_flagged_count | int | Number of tools flagged in this scan |
prompt_injection_total_count | int | Total tools analyzed |
parent_scan_id | int | Previous version of this scan when produced via /scan/{id}/rescan |
rescannable | bool | True 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
}
| Field | Type | Description |
|---|---|---|
status | string | Same values as on ScanResultDetailResponse.status |
queue_position | int / null | 1-indexed position in the queue. 0 if currently processing, null if not queued. |
queue_size | int / null | Total 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_idlinking 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 theADMIN_API_KEYenvironment 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" }