findings.v1
The shared wire format every Capframe module emits or consumes. View raw JSON Schema →
Top-level envelope
Every findings.v1 document is an object with these required fields. Anything else is rejected by the schema.
| field | type | notes | required? |
|---|---|---|---|
| schema_version | const | literal capframe.findings.v1 | required |
| scanned_at | string · date-time | RFC 3339 | required |
| scan_id | string · uuid | stable per scan | optional |
| scanner | Scanner | who produced this | required |
| target | Target | what was scanned | required |
| tools | array of Tool | tool surface seen | required |
| findings | array of Finding | what the classifier emitted | required |
| summary | Summary | aggregated counts + mappings | required |
Scanner
Identifies the tool that emitted the document.
| field | type | notes | required? |
|---|---|---|---|
| name | string | e.g. capframe-find, mcp-recon | required |
| version | string | e.g. 0.2.0 | required |
Target
Describes the artefact under scan.
| field | type | notes | required? |
|---|---|---|---|
| kind | TargetKind | discriminator | required |
| name | string | free-form display name | optional |
| url | string · uri | for HTTP targets | optional |
| path | string | for filesystem targets | optional |
| transport | enum: stdio | http | sse | websocket | for MCP servers | optional |
TargetKind enum
mcp_server— An MCP server (stdio or HTTP)openai_function— An OpenAI function-calling tool specanthropic_tool— An Anthropic tool_use blocklanggraph_node— A LangGraph node definitioncustom— Anything else; the scanner attaches its own shape
Tool
One entry per tool in the scanned surface. Same shape regardless of TargetKind.
| field | type | notes | required? |
|---|---|---|---|
| name | string | tool identity | required |
| description | string | free-form blurb | optional |
| parameters | object · JSON Schema | the tool's input contract | optional |
| side_effects | array of SideEffect | declared, not inferred | optional |
| auth_required | boolean | did the manifest claim auth? | optional |
| rate_limited | boolean | manifest claim | optional |
SideEffect enum
read— Reads bounded statewrite— Mutates state inside the bounded scopenetwork— Egresses to the networkfilesystem— Touches the host filesystemexecute— Runs code or shell commandsmoney— Moves funds, charges, refundsirreversible— Destructive — delete, drop, send
Finding
One rule firing on one tool (or, occasionally, server-level). Findings are the unit of cross-tool exchange — the same Finding object can flow from mcp-recon → capnagent for caveat issuance, or → capframe-report for OWASP/NIST/ATLAS reporting.
| field | type | notes | required? |
|---|---|---|---|
| id | string | stable hash; safe to diff across scans | required |
| severity | Severity | info | low | medium | high | critical | required |
| category | Category | stable taxonomy | required |
| title | string · max 200 | one-line summary | required |
| description | string | longer body | optional |
| tool | string | name of the tool this finding relates to | optional |
| evidence | object | scanner-specific structured detail | optional |
| remediation | string | actionable fix text | optional |
| mappings | Mappings | compliance-framework IDs | optional |
| first_seen | string · date-time | carried across scans by the consumer | optional |
| last_seen | string · date-time | carried similarly | optional |
Severity enum
info— Informational; no action requiredlow— Worth noting; doesn't blockmedium— Should be remediated; typical forunconstrained_inputhigh— Blocks production for security-sensitive deploymentscritical— Maximal authority leak (e.g. arbitrary code execution surface — see R7)
Category enum
indirect_injection— Tool fetches/forwards untrusted contentexcessive_agency— Tool has more power than its caveats declareunconstrained_input— String / object parameter with no boundmissing_authz— Side-effect tool with no auth claiminsecure_output_handling— Output not sanitised back to the agentsecret_exposure— Schema implies leaking credentialstool_naming_conflict— Two tools with overlapping namesdeserialization— Accepts opaque blobs that could be exploitsssrf_surface— URL parameter with no host allowlistfilesystem_egress— Reads outside its declared scopenetwork_egress— Egresses without rate limit / domain bounduntrusted_dependency— Pulls in a dep we can't vouch forother— Doesn't fit the above; subject to taxonomy change
Mappings — compliance IDs
Each finding can carry compliance-framework references. Patterns are enforced by the schema, so consumers can trust the strings.
| field | type | notes | required? |
|---|---|---|---|
| owasp_llm[] | string | pattern ^LLM(0[1-9]|10)$ | e.g. LLM08 |
| nist_rmf[] | string | pattern ^(GOVERN|MAP|MEASURE|MANAGE)-[0-9]+(\.[0-9]+)*$ | e.g. MANAGE-2.2 |
| mitre_atlas[] | string | pattern ^T[0-9]{4}(\.[0-9]{3})?$ | e.g. T0051 |
Summary
Aggregated counts. Computed by the scanner; consumers should not recompute. The fields are flat and stable for diffing across scans.
| field | type | notes | required? |
|---|---|---|---|
| total | integer ≥ 0 | findings.length | required |
| by_severity | SeverityCounts | five-key tally | required |
| by_category | object · keys are Category | k → count of findings in that category | optional |
| mappings | object | roll-up of Mappings IDs across all findings | optional |
SeverityCounts
All five severity keys present, default 0. Sum equals summary.total.
Example envelope
Minimal but valid. Strip the tools and findings arrays empty and you have the empty-scan shape that mcp-recon emits when its input can't be parsed.
{
"schema_version": "capframe.findings.v1",
"scanned_at": "2026-05-30T13:47:37Z",
"scan_id": "0c81b6d2-7a91-4c52-9c1e-aa8e90e3f6b1",
"scanner": { "name": "mcp-recon", "version": "0.2.0" },
"target": { "kind": "mcp_server", "name": "shopify-mcp", "transport": "stdio" },
"tools": [
{
"name": "order.refund",
"description": "Issue a refund on an order",
"side_effects": ["write", "money", "irreversible"],
"auth_required": true
}
],
"findings": [
{
"id": "f-order-refund-unbounded",
"severity": "high",
"category": "excessive_agency",
"title": "order.refund has no monetary cap",
"description": "Accepts arbitrary amount with no policy-side cap.",
"tool": "order.refund",
"remediation": "Bind a capability with --max-refund.",
"mappings": {
"owasp_llm": ["LLM08"],
"nist_rmf": ["MANAGE-2.2"],
"mitre_atlas": ["T0051"]
}
}
],
"summary": {
"total": 1,
"by_severity": { "info": 0, "low": 0, "medium": 0, "high": 1, "critical": 0 },
"by_category": { "excessive_agency": 1 }
}
}Versioning
Schema changes that add optional fields are backwards-compatible and don't bump the version. Renaming, removing, or changing semantics of an existing field bumps the version (capframe.findings.v2, etc.). Consumers should accept unknown optional fields silently.
Canonical JSON Schema: raw.githubusercontent.com/capframe/capframe/main/schemas/findings.v1.json