Symbiont Integration¶
ToolClad is the tools/ directory convention for Symbiont. The runtime auto-discovers .clad.toml manifests, registers them as MCP tools, wires them into the ORGA reasoning loop, and enforces Cedar policies -- all without writing Rust code.
Runtime Discovery¶
On startup, the Symbiont runtime scans the tools/ directory for .clad.toml files, the same way it scans agents/ for .dsl files and policies/ for .cedar files:
project/
agents/
recon.dsl
enum.dsl
policies/
tool-authorization.cedar
scope.cedar
tools/
nmap_scan.clad.toml
whois_lookup.clad.toml
nuclei_scan.clad.toml
browser.clad.toml
toolclad.toml # Optional: project-level config, custom types
symbiont.toml
The runtime:
- Parses each
.clad.tomlfile - Validates the manifest (required fields, type definitions, template references, output schema)
- Registers each tool as an MCP tool with
inputSchemaderived from[args]andoutputSchemaderived from[output.schema] - Wires the tool to the ToolClad executor in the
ActionExecutorpipeline
DSL Tool Reference Resolution¶
DSL agents reference tools by name:
The runtime resolves tool.nmap_scan by looking up nmap_scan in the tool registry. Resolution order:
- ToolClad manifests (
tools/nmap_scan.clad.toml) - Rust-registered tools (legacy
register_tools()insrc/*.rs) - MCP server tools (from connected MCP servers in
symbiont.toml)
ToolClad manifests take priority. This allows gradual migration: existing Rust tool definitions continue to work, and teams can convert them to manifests one at a time.
MCP Schema Auto-Generation¶
The [args] section maps to MCP inputSchema and [output.schema] maps to MCP outputSchema:
# From nmap_scan.clad.toml
[args.target]
position = 1
required = true
type = "scope_target"
description = "Target CIDR, IP, or hostname"
[args.scan_type]
position = 2
required = true
type = "enum"
allowed = ["ping", "service", "version", "syn"]
description = "Type of scan to perform"
[output.schema.properties.hosts]
type = "array"
description = "Discovered hosts with open ports and services"
Generates:
{
"name": "nmap_scan",
"description": "Network port scanning and service detection",
"inputSchema": {
"type": "object",
"properties": {
"target": {
"type": "string",
"description": "Target CIDR, IP, or hostname"
},
"scan_type": {
"type": "string",
"enum": ["ping", "service", "version", "syn"],
"description": "Type of scan to perform"
}
},
"required": ["target", "scan_type"]
},
"outputSchema": {
"type": "object",
"properties": {
"status": { "type": "string" },
"scan_id": { "type": "string" },
"results": {
"type": "object",
"properties": {
"hosts": {
"type": "array",
"description": "Discovered hosts with open ports and services"
}
}
}
}
}
}
The LLM sees both schemas in the Reason phase: inputSchema to know what parameters to provide, outputSchema to know what data it will receive back.
Cedar Policy Auto-Generation¶
The runtime can generate baseline Cedar policies from manifest metadata:
risk_tier = "low"generates a permissive policy (allow for any agent)risk_tier = "medium"generates a policy requiring phase contextrisk_tier = "high"generates a policy requiring human approval
# Auto-generated from nmap_scan.clad.toml (risk_tier = "low")
permit (
principal,
action == PenTest::Action::"execute_tool",
resource
)
when {
resource.tool_name == "nmap_scan"
};
Teams refine the generated policies with phase restrictions, environment constraints, and custom approval gates.
ORGA Loop Integration¶
The ToolClad executor plugs into the existing ORGA action execution pipeline:
Observe: Agent receives input/observations
|
Reason: LLM proposes tool calls using MCP schemas (generated from manifests)
|
Gate: Cedar evaluates policy using manifest-declared resource/action
| + ToolClad validates all arguments against manifest type system
|
Act: ToolClad executor constructs command from template, executes,
parses output, wraps in evidence envelope
|
Observe: Agent receives structured JSON results
The Gate phase has two validation layers:
- Cedar policy evaluation: Is this agent, in this phase, allowed to invoke this tool on this target? (Authorization)
- ToolClad argument validation: Do all parameters satisfy their declared types, ranges, patterns, and scope constraints? (Input correctness)
Both execute outside LLM influence. Both must pass for execution to proceed.
ActionExecutor Pipeline¶
ProposedAction
-> ToolInvocationEnforcer (SchemaPin verification check)
-> Cedar policy evaluation
-> ToolClad argument validation (if manifest exists)
-> ToolClad command construction + execution (if manifest exists)
OR Rust tool function (if legacy registration)
OR MCP server forwarding (if external MCP tool)
-> Evidence envelope wrapping
-> Observation returned to loop
Scope Enforcement¶
Any parameter with type scope_target, url (with scope_check = true), cidr, or ip_address is automatically validated against the project's scope definition (scope/scope.toml). This replaces manual scope-checking in every wrapper script.
The scope check runs in the executor, after Cedar authorization but before command execution. Even if a Cedar policy bug allows an out-of-scope target, the ToolClad type system catches it.
For browser mode, URL scope enforcement extends to navigation, redirects, and link clicks via [browser.scope].
Hot-Reload in Dev Mode¶
- Development mode (
symbiont.toml: mode = "development"): The runtime watchestools/for changes and reloads manifests automatically. Useful for iterating on tool definitions. - Production mode: Manifests are loaded at startup and frozen. Changes require a restart. This matches the behavior of Cedar policies, which are also frozen at startup in production.
symbi tools CLI¶
The Symbiont CLI provides subcommands for working with ToolClad manifests:
symbi tools list¶
Lists all discovered tools with their source:
TOOL SOURCE RISK CEDAR RESOURCE
nmap_scan tools/nmap_scan low PenTest::ScanTarget
hydra_bruteforce tools/hydra high PenTest::ScanTarget
custom_scanner src/custom.rs - Custom::Scanner
mcp_tool mcp://server/tool - (external)
symbi tools validate¶
Validates all .clad.toml files in tools/:
tools/nmap_scan.clad.toml OK
tools/hydra_bruteforce.clad.toml OK
tools/broken_tool.clad.toml ERROR: unknown type "target_ip" (did you mean "ip_address"?)
symbi tools test <name>¶
Dry-run a tool invocation: validates arguments and shows the constructed command without executing:
$ symbi tools test nmap_scan --arg target=10.0.1.0/24 --arg scan_type=service
Manifest: tools/nmap_scan.clad.toml
Arguments: target=10.0.1.0/24 (scope_target: OK) scan_type=service (enum: OK)
Command: nmap -sT -sV --version-intensity 5 --max-rate 1000 -oX /evidence/...-nmap/scan.xml --no-stylesheet -v 10.0.1.0/24
Cedar: PenTest::ScanTarget / execute_tool
Timeout: 600s
[dry run -- command not executed]
symbi tools schema <name>¶
Outputs the auto-generated MCP JSON Schema for a tool:
$ symbi tools schema nmap_scan
{
"name": "nmap_scan",
"description": "Network port scanning and service detection",
"inputSchema": { ... },
"outputSchema": { ... }
}
symbi tools init <name>¶
Scaffolds a new .clad.toml from a template:
$ symbi tools init my_scanner
Created tools/my_scanner.clad.toml -- edit to define your tool interface.
symbi tools sessions¶
Lists active session-mode tool sessions:
SESSION TOOL AGENT STATE INTERACTIONS UPTIME
a1b2c3d4-5678-... msfconsole_session exploit module_loaded 7 4m 23s
e5f6a7b8-9012-... psql_session data_agent ready 12 1m 05s
Custom Types via toolclad.toml¶
Define reusable types in toolclad.toml at the project root:
# toolclad.toml
[types.service_protocol]
base = "enum"
allowed = ["ssh", "ftp", "http", "https", "smb", "rdp", "mysql", "postgres"]
[types.severity_level]
base = "enum"
allowed = ["info", "low", "medium", "high", "critical"]
[types.nuclei_template_id]
base = "string"
pattern = "^[a-zA-Z0-9_-]+(/[a-zA-Z0-9_-]+)*$"
Reference them in any manifest:
The runtime resolves custom types by loading toolclad.toml at startup and making the definitions available to all manifest validators.
Session and Browser Executors¶
The Symbiont runtime includes specialized executors for stateful modes:
- SessionExecutor: PTY management, prompt detection, state inference, per-interaction Cedar gating, evidence transcript logging, child session registration
- BrowserExecutor: CDP-direct WebSocket, persistent daemon per tab, live browser attachment, accessibility tree extraction, page state inference, redirect interception, screenshot evidence
Both executors plug into the same ORGA loop and Cedar policy infrastructure as oneshot tools. The governance model is identical; only the transport differs.