addedOutput schema / $defs
Added value: +{
+ "CipherInfo": {
+ "additionalProperties": true,
+ "properties": {
+ "bits": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Effective symmetric key length in bits (e.g. 256 for AES-256-GCM). Null on handshake failure.",
+ "title": "Bits"
+ },
+ "name": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Cipher suite name as reported by OpenSSL, e.g. 'TLS_AES_256_GCM_SHA384' (TLS 1.3) or 'ECDHE-RSA-AES256-GCM-SHA384' (TLS 1.2). Null on handshake failure.",
+ "title": "Name"
+ },
+ "protocol": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "TLS protocol version negotiated for this cipher, e.g. 'TLSv1.3', 'TLSv1.2'. Mirrors SslResponse.protocol and is null on handshake failure.",
+ "title": "Protocol"
+ }
+ },
+ "title": "CipherInfo",
+ "type": "object"
+ },
+ "ErrorDetail": {
+ "description": "Structured failure body. Codes mirror app/exceptions.AppException\nsubclasses; agent retry / upgrade decisions key off `code`, not `message`.",
+ "properties": {
+ "code": {
+ "description": "Stable machine-readable failure category. Agents key retry/upgrade decisions off this.",
+ "enum": [
+ "invalid_argument",
+ "not_found",
+ "rate_limit_exceeded",
+ "auth_required",
+ "tier_limit",
+ "upstream_timeout",
+ "upstream_error",
+ "internal_error"
+ ],
+ "title": "Code",
+ "type": "string"
+ },
+ "docs_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Documentation pointer (e.g. tool input contract) when code='invalid_argument'.",
+ "title": "Docs Url"
+ },
+ "message": {
+ "description": "Human-readable detail. Free text — never parse. Capped at 500 chars to prevent oversized upstream errors from bloating responses.",
+ "maxLength": 500,
+ "title": "Message",
+ "type": "string"
+ },
+ "retry_after_seconds": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "When code='rate_limit_exceeded', the minimum seconds to wait before retrying.",
+ "title": "Retry After Seconds"
+ },
+ "upgrade_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Pricing/upgrade URL when code='tier_limit' or 'rate_limit_exceeded' on the Free tier.",
+ "title": "Upgrade Url"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ],
+ "title": "ErrorDetail",
+ "type": "object"
+ },
+ "ErrorResponse": {
+ "description": "MCP error envelope. Tool return type is always\n`SpecificResponse | ErrorResponse` — Union flag tells the agent which arm\narrived without parsing the inner body.",
+ "properties": {
+ "error": {
+ "$ref": "#/$defs/ErrorDetail"
+ }
+ },
+ "required": [
+ "error"
+ ],
+ "title": "ErrorResponse",
+ "type": "object"
+ },
+ "PivotHint": {
+ "additionalProperties": true,
+ "description": "A suggested follow-up MCP tool call. Surfaced inside response.next_calls so\nLLM agents can chain related lookups without manual prompting. Each hint names\nthe tool, the input value to pass, and a short reason explaining why this\npivot adds value in the current context.",
+ "properties": {
+ "input": {
+ "description": "Suggested input value to pass to the tool — typically a CVE ID, CWE ID, domain, or IP. Pre-populated from the current response so the agent can call the next tool without re-deriving the argument.",
+ "title": "Input",
+ "type": "string"
+ },
+ "params": {
+ "anyOf": [
+ {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "type": "object"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional extra kwargs to pass alongside `input`. Used by pivot generators when the next call benefits from a secondary parameter, e.g. {'exclude_id': 'AML.T0051'} to skip the originating technique from a sibling-tactic search. Omitted when no extra args are needed.",
+ "title": "Params"
+ },
+ "reason": {
+ "description": "Short rationale (one sentence) for why this follow-up call adds value, e.g. 'Federal patch deadline + ransomware association', 'Public exploits / PoC availability'.",
+ "title": "Reason",
+ "type": "string"
+ },
+ "tool": {
+ "description": "Canonical MCP tool name to call next. Constrained to known operation_ids in tools/list — adding a new tool here requires expanding the Literal.",
+ "enum": [
+ "cve_lookup",
+ "cve_search",
+ "cve_leading",
+ "bulk_cve_lookup",
+ "exploit_lookup",
+ "kev_detail",
+ "cwe_lookup",
+ "subdomain_enum",
+ "ssl_check",
+ "tech_fingerprint",
+ "asn_lookup",
+ "ip_lookup",
+ "ioc_lookup",
+ "bulk_ioc_lookup",
+ "hash_lookup",
+ "threat_intel",
+ "threat_report",
+ "audit_domain",
+ "domain_report",
+ "dns_lookup",
+ "whois_lookup",
+ "wayback_lookup",
+ "scan_headers",
+ "check_headers",
+ "check_secrets",
+ "check_injection",
+ "check_dependencies",
+ "email_mx",
+ "email_disposable",
+ "phone_lookup",
+ "username_lookup",
+ "password_check",
+ "phishing_check",
+ "atlas_technique_lookup",
+ "atlas_technique_search",
+ "bulk_atlas_technique_lookup",
+ "atlas_case_study_lookup",
+ "atlas_case_study_search",
+ "d3fend_defense_lookup",
+ "d3fend_defense_search",
+ "d3fend_defense_for_attack",
+ "d3fend_attack_coverage"
+ ],
+ "title": "Tool",
+ "type": "string"
+ }
+ },
+ "required": [
+ "tool",
+ "input",
+ "reason"
+ ],
+ "title": "PivotHint",
+ "type": "object"
+ },
+ "SslChainItem": {
+ "properties": {
+ "issuer": {
+ "default": "",
+ "description": "Issuer DN of the chain certificate (the CA that signed it).",
+ "title": "Issuer",
+ "type": "string"
+ },
+ "not_after": {
+ "default": "",
+ "description": "Certificate's expiry timestamp (ISO 8601, UTC).",
+ "title": "Not After",
+ "type": "string"
+ },
+ "source": {
+ "default": "handshake",
+ "description": "How this chain entry was discovered: 'handshake' (server-sent) or 'aia_fetch' (AIA chase-up).",
+ "title": "Source",
+ "type": "string"
+ },
+ "subject": {
+ "default": "",
+ "description": "Subject DN of the chain certificate, e.g. 'CN=*.example.com'.",
+ "title": "Subject",
+ "type": "string"
+ }
+ },
+ "title": "SslChainItem",
+ "type": "object"
+ },
+ "SslResponse": {
+ "properties": {
+ "chain": {
+ "description": "Full cert chain from leaf upward (excluding system root). Includes AIA-fetched intermediates when needed.",
+ "items": {
+ "$ref": "#/$defs/SslChainItem"
+ },
+ "title": "Chain",
+ "type": "array"
+ },
+ "cipher": {
+ "$ref": "#/$defs/CipherInfo",
+ "description": "Negotiated cipher suite with name, negotiated TLS protocol, and key length. All fields are null on handshake failure (empty CipherInfo)."
+ },
+ "days_remaining": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Days until leaf cert expires (negative if already expired). Null when not_after could not be parsed.",
+ "title": "Days Remaining"
+ },
+ "domain": {
+ "description": "Queried domain (echoed). SNI-matched against the leaf cert.",
+ "title": "Domain",
+ "type": "string"
+ },
+ "grade": {
+ "default": "F",
+ "description": "Overall SSL configuration grade. 'A' (cert_valid + TLSv1.3 + >=30 days remaining), 'B' (cert_valid + (TLSv1.3 <30d OR TLSv1.2 healthy)), 'C' (cert_valid + (TLSv1.2 <14d OR TLSv1.3 <7d OR unknown protocol)), 'D' (cert readable but invalid: hostname_mismatch / untrusted_root / self_signed), 'F' (probe failure, expired, OR TLSv1/TLSv1.1). Canonical grader is _ssl_grade() in domain/recon.py; same helper powers /v1/domain/ ssl section (single source of truth).",
+ "enum": [
+ "A",
+ "B",
+ "C",
+ "D",
+ "F"
+ ],
+ "title": "Grade",
+ "type": "string"
+ },
+ "issuer": {
+ "default": "",
+ "description": "Issuer DN of the leaf cert, e.g. \"CN=Let's Encrypt R3, O=Let's Encrypt, C=US\".",
+ "title": "Issuer",
+ "type": "string"
+ },
+ "next_calls": {
+ "anyOf": [
+ {
+ "items": {
+ "$ref": "#/$defs/PivotHint"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user.",
+ "title": "Next Calls"
+ },
+ "not_after": {
+ "default": "",
+ "description": "Leaf cert's notAfter timestamp (ISO 8601, UTC) — expiry moment.",
+ "title": "Not After",
+ "type": "string"
+ },
+ "not_before": {
+ "default": "",
+ "description": "Leaf cert's notBefore timestamp (ISO 8601, UTC) — earliest valid moment.",
+ "title": "Not Before",
+ "type": "string"
+ },
+ "protocol": {
+ "default": "",
+ "description": "Negotiated TLS protocol version string as reported by OpenSSL: 'TLSv1.3', 'TLSv1.2', 'TLSv1.1', 'TLSv1'. Empty on handshake failure. Grade F is forced for TLSv1/TLSv1.1.",
+ "title": "Protocol",
+ "type": "string"
+ },
+ "san": {
+ "description": "Subject Alternative Names — all DNS names the cert is valid for (including CN when distinct).",
+ "items": {
+ "type": "string"
+ },
+ "title": "San",
+ "type": "array"
+ },
+ "serial_number": {
+ "default": "",
+ "description": "Hex-encoded leaf cert serial number.",
+ "title": "Serial Number",
+ "type": "string"
+ },
+ "signature_algorithm": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Signature algorithm name, e.g. 'sha256WithRSAEncryption', 'ecdsa-with-SHA384'.",
+ "title": "Signature Algorithm"
+ },
+ "subject": {
+ "default": "",
+ "description": "Subject DN of the leaf cert, e.g. 'CN=example.com'.",
+ "title": "Subject",
+ "type": "string"
+ },
+ "summary": {
+ "default": "",
+ "description": "One-line human summary, e.g. 'example.com valid until 2026-07-04 (71 days) · TLSv1.3 · grade A'.",
+ "title": "Summary",
+ "type": "string"
+ },
+ "valid": {
+ "default": false,
+ "description": "True when TLS handshake succeeded AND cert is unexpired AND chain verified. False on any failure (handshake error, expired, hostname mismatch, untrusted CA).",
+ "title": "Valid",
+ "type": "boolean"
+ },
+ "validation_errors": {
+ "description": "Canonical cert validation failure tags when cert is readable but invalid. Values: 'expired', 'self_signed', 'hostname_mismatch', 'untrusted_root', 'chain_incomplete'. Empty when cert validates cleanly. See also: 'valid' (boolean overall) and 'warnings' (human-readable).",
+ "items": {
+ "type": "string"
+ },
+ "maxItems": 10,
+ "title": "Validation Errors",
+ "type": "array"
+ },
+ "verdict": {
+ "anyOf": [
+ {
+ "$ref": "#/$defs/Verdict"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."
+ },
+ "warnings": {
+ "description": "Human-readable warnings: deprecated protocol, near-expiry, self-signed chain, weak signature algorithm, etc.",
+ "items": {
+ "type": "string"
+ },
+ "maxItems": 10,
+ "title": "Warnings",
+ "type": "array"
+ }
+ },
+ "required": [
+ "domain"
+ ],
+ "title": "SslResponse",
+ "type": "object"
+ },
+ "Verdict": {
+ "properties": {
+ "completeness": {
+ "default": "complete",
+ "description": "'complete' = every planned source returned data; 'partial' = at least one source in sources_unavailable failed or was skipped; 'minimal' = only the primary/required source returned, optional enrichment missing.",
+ "enum": [
+ "complete",
+ "partial",
+ "minimal"
+ ],
+ "title": "Completeness",
+ "type": "string"
+ },
+ "data_age_seconds": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Seconds elapsed since the oldest cached source was fetched, or null when every source was queried live for this request. Use to judge freshness.",
+ "title": "Data Age Seconds"
+ },
+ "deterministic": {
+ "description": "True when the response is fully reproducible from the listed sources for the same input at the same moment (no randomness, no model inference). False for endpoints that include probabilistic scoring or LLM output.",
+ "title": "Deterministic",
+ "type": "boolean"
+ },
+ "falsifiable_fields": {
+ "description": "Top-level response fields whose values a caller can independently re-derive from the named upstream sources (e.g. 'dns', 'ssl', 'whois'). Fields not in this list are derived/computed and cannot be directly re-verified.",
+ "items": {
+ "type": "string"
+ },
+ "title": "Falsifiable Fields",
+ "type": "array"
+ },
+ "sources_queried": {
+ "description": "Canonical source identifiers successfully consulted for this response (e.g. 'ripe_stat', 'shodan_internetdb', 'firehol'). Agent-readable list, order not significant.",
+ "items": {
+ "type": "string"
+ },
+ "title": "Sources Queried",
+ "type": "array"
+ },
+ "sources_unavailable": {
+ "description": "Sources that were expected but not returned — either intentionally skipped (lite mode, tier gating) or failed (quota, timeout, upstream down). Empty list means every planned source produced data.",
+ "items": {
+ "type": "string"
+ },
+ "title": "Sources Unavailable",
+ "type": "array"
+ }
+ },
+ "required": [
+ "deterministic"
+ ],
+ "title": "Verdict",
+ "type": "object"
+ }
+}