Output & Evidence¶
Every ToolClad execution produces structured, validated, hash-protected output. The output pipeline has three stages: parsing raw tool output into structured data, validating it against a declared schema, and wrapping it in an evidence envelope.
Output Parsers¶
Built-in Parsers¶
ToolClad ships with five built-in parsers that cover common output formats:
| Parser | Produces | Use Case |
|---|---|---|
builtin:json |
Passes through as-is | Tools with native JSON output |
builtin:xml |
Converts XML to JSON | nmap, Nessus, OWASP ZAP |
builtin:csv |
Array of objects (header row as keys) | Spreadsheet exports, log files |
builtin:jsonl |
Array of JSON objects (one per line) | Nuclei, streaming tools |
builtin:text |
{ "raw_output": "..." } |
Simple unstructured output (default) |
Declare the parser in the manifest:
When no parser is specified, builtin:text is the default.
Custom Parsers¶
For tools whose output requires domain-specific parsing, declare an external script:
Custom parsers:
- Receive the raw output file path as
argv[1] - Must emit valid JSON to stdout
- Run with the same timeout and process group isolation as the tool itself
- Their output is validated against
[output.schema]before reaching the agent
The parser's output replaces what would otherwise be the built-in parser's result in the results field of the evidence envelope.
Output Schema¶
Every manifest must declare the expected shape of its parsed results in [output.schema]. This is mandatory.
Purpose¶
- MCP
outputSchemageneration. The LLM sees what data shape it will receive before proposing a tool call. This enables more effective reasoning about whether and how to use a tool. - Parser output validation. The executor validates parsed results against the schema. Malformed parser output is rejected before it reaches the agent, preventing corrupted state in the reasoning loop.
Declaration¶
The schema uses JSON Schema-compatible syntax in TOML:
[output.schema]
type = "object"
[output.schema.properties.hosts]
type = "array"
description = "Discovered hosts with open ports and services"
[output.schema.properties.hosts.items.properties.ip]
type = "string"
description = "Host IP address"
[output.schema.properties.hosts.items.properties.ports]
type = "array"
description = "Open ports with service details"
[output.schema.properties.hosts.items.properties.ports.items.properties.port]
type = "integer"
[output.schema.properties.hosts.items.properties.ports.items.properties.service]
type = "string"
For tools with simple or opaque output, a minimal schema is acceptable:
[output.schema]
type = "object"
[output.schema.properties.raw_output]
type = "string"
description = "Raw command output text"
The schema is JSON Schema-compatible so it maps directly to MCP outputSchema with no transformation.
Evidence Envelopes¶
When envelope = true (the default), the executor wraps parsed output in a standard envelope.
Structure¶
{
"status": "success",
"scan_id": "1711929600-12345",
"tool": "nmap_scan",
"command": "nmap -sT -sV --max-rate 1000 -oX /evidence/...-nmap/scan.xml 10.0.1.0/24",
"duration_ms": 4523,
"timestamp": "2026-03-20T12:00:00Z",
"exit_code": 0,
"stderr": "",
"output_file": "/evidence/1711929600-12345-nmap/scan.xml",
"output_hash": "sha256:a1b2c3d4e5f6...",
"results": {
"hosts": [
{
"ip": "10.0.1.1",
"ports": [
{ "port": 22, "protocol": "tcp", "service": "ssh", "version": "OpenSSH 8.9" },
{ "port": 80, "protocol": "tcp", "service": "http", "version": "nginx 1.24" }
]
}
]
}
}
Envelope Fields¶
| Field | Type | Description |
|---|---|---|
status |
string | "success", "error", "timeout", or "delegated" (MCP proxy) |
scan_id |
string | Unique invocation identifier (unix_timestamp-short_uuid) |
tool |
string | Tool name from the manifest ([tool].name) |
command |
string | The constructed command that was executed |
duration_ms |
integer | Wall-clock execution time in milliseconds |
timestamp |
string | ISO 8601 execution timestamp (UTC) |
exit_code |
integer | Process exit code (0 = success, -1 = timeout/error) |
stderr |
string | Standard error output from the process |
output_file |
string | Path to the raw output file on disk (if evidence capture enabled) |
output_hash |
string | SHA-256 hash of raw output for tamper detection |
results |
object | Parsed and schema-validated output |
HTTP Backend Envelopes¶
HTTP tool envelopes include additional fields:
| Field | Type | Description |
|---|---|---|
http_status |
integer | HTTP response status code |
http_method |
string | HTTP method used (GET, POST, etc.) |
MCP Proxy Envelopes¶
MCP proxy tools produce delegation envelopes:
| Field | Type | Description |
|---|---|---|
status |
string | Always "delegated" |
mcp_server |
string | Upstream MCP server name |
mcp_tool |
string | Upstream MCP tool name |
mcp_args |
object | Arguments mapped through field_map |
SHA-256 Hashing¶
The output_hash field contains a SHA-256 hash of the raw tool output (before parsing). This provides tamper detection for the evidence chain.
Hash computation:
Properties:
- Computed over raw bytes, not the parsed JSON
- Independent of the parser implementation
- Different parsers producing different JSON from the same raw output share the same hash
- Anchors the evidence to the exact output the tool produced
Configure evidence hashing in the manifest:
Symbiont Audit Trail Integration¶
Evidence envelopes integrate directly with Symbiont's cryptographic audit trail:
- Every tool invocation produces an evidence envelope with a unique
scan_idandtimestamp. - The
output_hashanchors the evidence to the exact output bytes, enabling tamper detection across the evidence chain. - The envelope is stored in Symbiont's audit log alongside Cedar policy decisions, agent state, and ORGA loop context.
- Session and browser mode produce per-interaction envelopes, creating a complete transcript of every command, response, and policy decision.
- The evidence chain links parent and child sessions via
parent_session_id, providing full traceability across session boundaries.
The audit trail records:
- Which agent proposed the tool call
- What Cedar policy decisions were made
- What arguments were validated
- What command was constructed and executed
- What output was produced (with hash)
- How long execution took
This enables post-hoc review, compliance reporting, and forensic analysis of agent behavior.
Error Envelopes¶
When a tool fails, the envelope carries structured error information:
{
"status": "error",
"scan_id": "1711929600-12346",
"tool": "nmap_scan",
"command": "nmap -sT -sV --max-rate 1000 10.0.1.0/24",
"duration_ms": 1200,
"timestamp": "2026-03-20T12:05:00Z",
"exit_code": 1,
"stderr": "Failed to resolve \"10.0.1.0/24\": Name or service not known",
"output_hash": "sha256:0000...",
"results": null
}
The agent receives structured error information:
exit_codetells the agent the tool failed (non-zero)stderrtells the agent why it failed, in the tool's own words- The agent can reason about the error and adjust its next action
Timeout envelopes set status = "timeout" and exit_code = -1.