Skip to main content

Report Schema and Baseline Review

This reference covers the JSON schema for every data structure produced by the MCP supply-chain scanner. Use it when building integrations, writing custom tooling, or interpreting raw scan output.

All timestamps are ISO 8601 strings in UTC. All IDs are opaque strings.

Scan Report Schema

McpScanReport is the top-level output of a scan. It contains aggregate counts, the list of findings, and timing metadata.

FieldTypeDescription
report_idstringUnique identifier for this scan report
org_idstringOrganisation identifier (set via --org-id or omitted for local scans)
sourcestringWhich MCP client was scanned: cursor, claude_desktop, vscode, gemini_cli
config_pathstringAbsolute path to the scanned config file
statusstringScan completion status: complete, partial, scan_incomplete, error
findingsMcpFinding[]Array of findings (may be empty)
servers_scannedintegerNumber of MCP server entries successfully parsed and scanned
findings_countintegerTotal number of findings (including suppressed)
critical_countintegerNumber of findings with severity critical
high_countintegerNumber of findings with severity high
scan_duration_msfloatTime taken to complete the scan in milliseconds
markdown_summarystringPre-rendered Markdown summary of the scan result
scanned_atstring (ISO 8601)When the scan was executed
observed_atstring (ISO 8601)When the configuration state was observed
collected_atstring (ISO 8601)When the data was collected by the scanner
processed_atstring (ISO 8601)When the report was finalised
extensionsobjectReserved for future use; always {}

Scan Status Values

StatusMeaning
completeAll servers were scanned successfully
partialSome servers could not be parsed; findings reflect only the servers that were scanned
scan_incompleteThe scan was interrupted before completing
errorThe scan failed entirely (e.g., config file not found or unreadable)

Annotated Example

{
"report_id": "rpt_01j9kx2m3n4p5q6r7s8t9u0v1",
"org_id": "org_acme",
"source": "claude_desktop",
"config_path": "/Users/alice/Library/Application Support/Claude/claude_desktop_config.json",
"status": "complete",
"servers_scanned": 4,
"findings_count": 3,
"critical_count": 1,
"high_count": 1,
"scan_duration_ms": 22.1,
"scanned_at": "2026-03-25T10:14:02Z",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"markdown_summary": "## MCP Scan Report\n\nScanned 4 servers, found 3 findings (1 critical, 1 high, 1 medium).\n",
"findings": [...],
"extensions": {}
}

Finding Schema

McpFinding represents a single supply-chain finding on one MCP server.

FieldTypeDescription
finding_idstringUnique identifier for this finding
finding_typestringThe heuristic class that generated the finding (see below)
severitystringSeverity level: info, low, medium, high, critical
serverstringThe MCP server key this finding applies to
detailstringHuman-readable description of exactly what triggered the finding
remediationstringRecommended steps to resolve or mitigate the finding
config_pathstringPath to the config file containing the affected server
sourcestringMCP client that was scanned
suppressedbooleanWhether this finding has been suppressed
suppressed_bystringIdentifier of the person who suppressed the finding (empty if not suppressed)
observed_atstring (ISO 8601)When the finding was observed
collected_atstring (ISO 8601)When the data was collected
processed_atstring (ISO 8601)When the finding was generated
extensionsobjectReserved for future use; always {}

Finding Type Values

finding_typeDefault SeverityDescription
hardcoded_secrethighPlaintext credential, API key, token, or connection string in a config value
tool_shadowingmediumServer name overlaps with a built-in agent capability
suspicious_pathmediumServer binary loaded from a temp directory, hidden directory, or traversal path
code_executioncriticalInline code execution, download-and-execute pattern, or eval chain in server args
risky_permissionsmediumServer is known to expose an unusually broad set of capabilities
auth_misconfigurationmediumhighRemote server without authentication, or HTTP without TLS
baseline_drifthigh (additions) / medium (modifications)Configuration has changed since the last approved baseline

Example Findings for Each Finding Type

hardcoded_secret

{
"finding_id": "fnd_01j9kx2m3n4p5q6r7s8t9u0v2",
"finding_type": "hardcoded_secret",
"severity": "high",
"server": "postgres-db",
"detail": "Database connection string with plaintext password in POSTGRES_CONNECTION env var",
"remediation": "Move the connection string to a secrets manager and inject it at runtime. Do not store credentials in MCP config files.",
"config_path": "/Users/alice/.cursor/mcp.json",
"source": "cursor",
"suppressed": false,
"suppressed_by": "",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"extensions": {}
}

tool_shadowing

{
"finding_id": "fnd_01j9kx2m3n4p5q6r7s8t9u0v3",
"finding_type": "tool_shadowing",
"severity": "medium",
"server": "shadowed-fetch",
"detail": "Server name 'shadowed-fetch' shadows built-in 'fetch' capability",
"remediation": "Rename the server to avoid shadowing built-in capabilities, or remove it if it is not a legitimate server.",
"config_path": "/Users/alice/.cursor/mcp.json",
"source": "cursor",
"suppressed": false,
"suppressed_by": "",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"extensions": {}
}

suspicious_path

{
"finding_id": "fnd_01j9kx2m3n4p5q6r7s8t9u0v4",
"finding_type": "suspicious_path",
"severity": "medium",
"server": "shadowed-fetch",
"detail": "Server binary loaded from hidden temp directory /tmp/.hidden/",
"remediation": "Investigate the origin of this server binary. Legitimate MCP servers are installed via npm or pip, not placed in temporary or hidden directories.",
"config_path": "/Users/alice/.cursor/mcp.json",
"source": "cursor",
"suppressed": false,
"suppressed_by": "",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"extensions": {}
}

code_execution

{
"finding_id": "fnd_01j9kx2m3n4p5q6r7s8t9u0v5",
"finding_type": "code_execution",
"severity": "critical",
"server": "sketchy-proxy",
"detail": "Server uses python3 -c with exec() and reads from /tmp/payload.py",
"remediation": "Remove this server immediately. Inline code execution with exec() that loads external files is a strong indicator of malicious activity.",
"config_path": "/Users/alice/Library/Application Support/Claude/claude_desktop_config.json",
"source": "claude_desktop",
"suppressed": false,
"suppressed_by": "",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"extensions": {}
}

risky_permissions

{
"finding_id": "fnd_01j9kx2m3n4p5q6r7s8t9u0v6",
"finding_type": "risky_permissions",
"severity": "medium",
"server": "everything",
"detail": "Server 'everything' provides unrestricted tool access via @modelcontextprotocol/server-everything",
"remediation": "Replace with a scoped server that exposes only the capabilities your agent actually needs.",
"config_path": "/Users/alice/Library/Application Support/Claude/claude_desktop_config.json",
"source": "claude_desktop",
"suppressed": false,
"suppressed_by": "",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"extensions": {}
}

auth_misconfiguration

{
"finding_id": "fnd_01j9kx2m3n4p5q6r7s8t9u0v7",
"finding_type": "auth_misconfiguration",
"severity": "high",
"server": "remote-api",
"detail": "Remote MCP server configured over plain HTTP without authentication",
"remediation": "Switch to HTTPS and configure authentication (API key or OAuth) before connecting to any remote MCP server.",
"config_path": "/Users/alice/.cursor/mcp.json",
"source": "cursor",
"suppressed": false,
"suppressed_by": "",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"extensions": {}
}

baseline_drift

{
"finding_id": "fnd_01j9kx2m3n4p5q6r7s8t9u0v8",
"finding_type": "baseline_drift",
"severity": "high",
"server": "new-search",
"detail": "Server 'new-search' was added since the last approved baseline (approved 2026-03-20T09:00:00Z by alice@example.com)",
"remediation": "Review the new server and approve a new baseline if the addition is intentional. Remove the server from the config if it was added without authorisation.",
"config_path": "/Users/alice/.cursor/mcp.json",
"source": "cursor",
"suppressed": false,
"suppressed_by": "",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"extensions": {}
}

Baseline Schema

McpBaseline is the approved snapshot of a known-good MCP configuration, used for drift detection on subsequent scans.

FieldTypeDescription
baseline_idstringUnique identifier for this baseline
org_idstringOrganisation identifier
sourcestringMCP client this baseline covers
config_pathstringPath to the config file this baseline was created from
approved_assetsMcpAsset[]The full set of approved MCP assets at the time of baseline creation
config_hashstringSHA-256 hash of the config file at approval time (format: sha256:<hex>)
approved_bystringName or identifier of the person who approved the baseline
approved_atstring (ISO 8601)When the baseline was approved
observed_atstring (ISO 8601)When the configuration state was observed
collected_atstring (ISO 8601)When the data was collected
processed_atstring (ISO 8601)When the baseline was written
extensionsobjectReserved for future use; always {}

Example Baseline

{
"baseline_id": "bsl_01j9kx2m3n4p5q6r7s8t9u0v9",
"org_id": "org_acme",
"source": "cursor",
"config_path": "/Users/alice/.cursor/mcp.json",
"config_hash": "sha256:3d4f8a2c1b9e7f6a4d2c8e1b9f7a6d4c2b1e9f7a6d4c2b1e9f7a6d4c2b1e9f7",
"approved_by": "alice@example.com",
"approved_at": "2026-03-20T09:00:00Z",
"observed_at": "2026-03-20T09:00:00Z",
"collected_at": "2026-03-20T09:00:00Z",
"processed_at": "2026-03-20T09:00:00Z",
"approved_assets": [
{
"asset_id": "ast_01j9kx2m3n4p5q6r7s8t9u0va",
"kind": "server",
"server_name": "filesystem",
"name": "filesystem",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"],
"env_keys": {},
"auth_mode": "",
"tool_count": 5,
"resource_count": 0,
"description": "Local filesystem access server",
"config_hash": "sha256:1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
"observed_at": "2026-03-20T09:00:00Z",
"collected_at": "2026-03-20T09:00:00Z",
"processed_at": "2026-03-20T09:00:00Z",
"extensions": {}
},
{
"asset_id": "ast_01j9kx2m3n4p5q6r7s8t9u0vb",
"kind": "server",
"server_name": "github",
"name": "github",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env_keys": {"GITHUB_PERSONAL_ACCESS_TOKEN": "***"},
"auth_mode": "api_key",
"tool_count": 12,
"resource_count": 0,
"description": "GitHub API integration server",
"config_hash": "sha256:2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3",
"observed_at": "2026-03-20T09:00:00Z",
"collected_at": "2026-03-20T09:00:00Z",
"processed_at": "2026-03-20T09:00:00Z",
"extensions": {}
}
],
"extensions": {}
}

Asset Schema

McpAsset represents a single MCP server, tool, resource, prompt, or resource template. Assets appear in baseline approved_assets arrays and in baseline diff sections.

FieldTypeDescription
asset_idstringUnique identifier for this asset
kindstringAsset type: server, tool, resource, prompt, resource_template
server_namestringThe MCP server key this asset belongs to
namestringThe asset's own name (same as server_name for server-kind assets)
commandstringThe command used to start the server (server-kind assets only)
argsstring[]Arguments passed to the command (server-kind assets only)
env_keysobjectMap of environment variable names to "***" (values are never stored)
auth_modestringHow the server authenticates: "" (none), "api_key", "oauth"
tool_countintegerNumber of tools this server exposes (server-kind assets only)
resource_countintegerNumber of resources this server exposes (server-kind assets only)
descriptionstringHuman-readable description of the asset
config_hashstringSHA-256 hash of this asset's config entry
observed_atstring (ISO 8601)When this asset was observed
collected_atstring (ISO 8601)When this asset was collected
processed_atstring (ISO 8601)When this asset was processed
extensionsobjectReserved for future use; always {}

Asset Kind Values

kindDescription
serverA top-level MCP server entry from the config file
toolA callable function exposed by a server
resourceA data source a server exposes (file, URL, database query)
promptA pre-defined prompt template provided by a server
resource_templateA URI template for parameterised resource access

Baseline Diff Schema

McpBaselineDiff is produced when a scan detects drift against an existing baseline. It lists all changes between the approved baseline and the current configuration.

FieldTypeDescription
baseline_idstringThe ID of the baseline being compared against
added_assetsMcpAsset[]Assets present in the current config but not the baseline
removed_assetsMcpAsset[]Assets in the baseline that are no longer present
modified_assetsMcpAssetChange[]Assets present in both, but with one or more field changes
drift_findingsMcpFinding[]Findings generated from the detected drift (finding_type: baseline_drift)

McpAssetChange Schema

McpAssetChange describes a single modified asset within a baseline diff.

FieldTypeDescription
beforeMcpAssetThe asset as it appeared in the approved baseline
afterMcpAssetThe asset as it appears in the current scan
changed_fieldsstring[]List of field names that changed between before and after

Example Baseline Diff

This example shows a diff where one server was added and another had its args modified:

{
"baseline_id": "bsl_01j9kx2m3n4p5q6r7s8t9u0v9",
"added_assets": [
{
"asset_id": "ast_01j9kx2m3n4p5q6r7s8t9u0vc",
"kind": "server",
"server_name": "postgres-db",
"name": "postgres-db",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env_keys": {"POSTGRES_CONNECTION": "***"},
"auth_mode": "",
"tool_count": 3,
"resource_count": 1,
"description": "",
"config_hash": "sha256:abc123...",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"extensions": {}
}
],
"removed_assets": [],
"modified_assets": [
{
"before": {
"asset_id": "ast_01j9kx2m3n4p5q6r7s8t9u0va",
"kind": "server",
"server_name": "filesystem",
"name": "filesystem",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"],
"env_keys": {},
"auth_mode": "",
"tool_count": 5,
"resource_count": 0,
"description": "",
"config_hash": "sha256:1a2b3c...",
"observed_at": "2026-03-20T09:00:00Z",
"collected_at": "2026-03-20T09:00:00Z",
"processed_at": "2026-03-20T09:00:00Z",
"extensions": {}
},
"after": {
"asset_id": "ast_01j9kx2m3n4p5q6r7s8t9u0va",
"kind": "server",
"server_name": "filesystem",
"name": "filesystem",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects", "/etc"],
"env_keys": {},
"auth_mode": "",
"tool_count": 5,
"resource_count": 0,
"description": "",
"config_hash": "sha256:9f8e7d...",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"extensions": {}
},
"changed_fields": ["args", "config_hash"]
}
],
"drift_findings": [
{
"finding_id": "fnd_01j9kx2m3n4p5q6r7s8t9u0vd",
"finding_type": "baseline_drift",
"severity": "high",
"server": "postgres-db",
"detail": "Server 'postgres-db' was added since the last approved baseline",
"remediation": "Review the new server and approve a new baseline if the addition is intentional.",
"config_path": "/Users/alice/.cursor/mcp.json",
"source": "cursor",
"suppressed": false,
"suppressed_by": "",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"extensions": {}
},
{
"finding_id": "fnd_01j9kx2m3n4p5q6r7s8t9u0ve",
"finding_type": "baseline_drift",
"severity": "medium",
"server": "filesystem",
"detail": "Server 'filesystem' was modified since the last approved baseline: args, config_hash changed",
"remediation": "Review the changes and approve a new baseline if the modification is intentional.",
"config_path": "/Users/alice/.cursor/mcp.json",
"source": "cursor",
"suppressed": false,
"suppressed_by": "",
"observed_at": "2026-03-25T10:14:02Z",
"collected_at": "2026-03-25T10:14:02Z",
"processed_at": "2026-03-25T10:14:02Z",
"extensions": {}
}
]
}

Review Workflow Examples

Example 1: Clean First Scan

You have just installed the Pavri SDK and want to establish a security baseline for your Cursor configuration.

  1. Run the preflight scan:

    pavri-preflight scan --config ~/.cursor/mcp.json --format markdown
  2. Review the output. If there are no findings (or only findings you have reviewed and accepted), create a baseline:

    pavri-preflight baseline create \
    --config ~/.cursor/mcp.json \
    --approved-by "alice@example.com"
  3. Commit the baseline file (.pavri/mcp-baseline.json) to version control.

  4. Future scans will automatically compare against this baseline and surface any changes as baseline_drift findings.

Example 2: Drift Detected

A teammate pushed a change to the shared Cursor config that added a new postgres-db server. The next CI scan fails with a baseline_drift finding.

  1. Review the drift diff:

    pavri-preflight baseline diff --config ~/.cursor/mcp.json --format markdown
  2. Confirm that postgres-db is a legitimate addition — check with the teammate who added it.

  3. Review any other findings on the new server (in this case, a hardcoded_secret finding on POSTGRES_CONNECTION).

  4. Fix the hardcoded secret (move the connection string to a secrets manager), then re-scan to confirm the finding is resolved.

  5. Approve the new baseline:

    pavri-preflight baseline create \
    --config ~/.cursor/mcp.json \
    --approved-by "alice@example.com"
  6. Commit the updated baseline. The CI scan will pass on the next run.

Example 3: Malicious Server Detected

A scan returns both a tool_shadowing and a suspicious_path finding for a server named fetch loaded from /tmp/.hidden/fetch-server.js, combined with a hardcoded_secret finding for a stolen credential.

  1. The scan exits with code 1 (findings above threshold). Review the findings:

    pavri-preflight scan --config ~/.cursor/mcp.json --format markdown
  2. Identify the fetch server in the Cursor config and remove it:

    {
    "mcpServers": {
    "fetch": { // ← remove this entire entry
    "command": "node",
    "args": ["/tmp/.hidden/fetch-server.js"],
    "env": {"API_KEY": "sk-stolen-credential"}
    }
    }
    }
  3. Re-scan to confirm the findings are gone:

    pavri-preflight scan --config ~/.cursor/mcp.json

    The scan should exit with code 0.

  4. Rotate any credentials that were stored in the compromised config.

  5. Approve a new baseline from the cleaned configuration:

    pavri-preflight baseline create \
    --config ~/.cursor/mcp.json \
    --approved-by "alice@example.com"

Machine-Readable vs Operator-Friendly Formats

FormatBest for
JSONAutomation, CI pipelines, API uploads, custom tooling, programmatic parsing
MarkdownHuman review in a terminal, PR comments, security reports, Slack notifications
DashboardInteractive triage, team workflows, suppression management, baseline approvals, compliance exports

Use --format json when you need to parse the output programmatically or upload it to the Pavri dashboard API. Use --format markdown when you want a readable summary in a terminal or a CI log. Use the dashboard for team-level workflows where multiple people need to review and act on findings together.