ToolClad
Declarative tool interface contracts for agentic runtimes.
ToolClad is a manifest format (.clad.toml) that defines the complete behavioral contract for a tool: typed parameters, validation rules, invocation mechanism, output parsing, and policy metadata. Three execution modes share a common governance layer:
- Oneshot (default): Single CLI command execution
- Session: Interactive CLI tools via PTY (msfconsole, psql, redis-cli) with per-interaction Cedar gating
- Browser: Governed headless browser via CDP/Playwright with URL scope enforcement and page state policies
The Problem
Every team building agentic systems writes custom glue code per tool: argument sanitization, timeout enforcement, output parsing, evidence capture, Cedar mappings, DSL capabilities, and policies. That's 7 steps per tool. It doesn't scale.
The Solution
# tools/whois_lookup.clad.toml
[tool]
name = "whois_lookup"
version = "1.0.0"
binary = "whois"
description = "WHOIS domain/IP registration lookup"
timeout_seconds = 30
risk_tier = "low"
[args.target]
position = 1
required = true
type = "scope_target"
description = "Domain name or IP address to query"
[command]
template = "whois {target}"
[output]
format = "text"
envelope = true
[output.schema]
type = "object"
[output.schema.properties.raw_output]
type = "string"
The agent fills typed parameters. The executor validates, constructs the command, executes with timeout, and returns structured JSON. The agent never sees or generates a shell command.
Security Model
ToolClad inverts the sandbox approach:
- Sandbox: LLM generates command -> sandbox intercepts -> allow/deny (deny-list)
- ToolClad: LLM fills typed parameters -> policy gate -> executor validates -> constructs command from template (allow-list)
The dangerous action cannot be expressed because the interface doesn't permit it.
Security Features
- Shell injection prevention: All string types reject metacharacters (
;|&$\(){}[]<>!\n\r`) by default - Array-based execution: Commands dispatched via direct
execve(nosh -cshell interpretation) - Process group isolation: Tools spawned in new PGID; timeout kills entire process group (no zombies)
- Absolute path blocking:
pathtype rejects/etc/shadow,C:\...style paths - Newline injection blocking:
\nand\rrejected in all string-based types - No eval: Conditional evaluators use closed-vocabulary parsers, never dynamic code execution
Packages
Install from your language's package registry:
cargo install toolclad # Rust / crates.io
pip install toolclad # Python / PyPI
npm install toolclad # JavaScript / npm
| Language | Registry | Package |
|---|---|---|
| Rust | crates.io | cargo install toolclad |
| Python | PyPI | pip install toolclad |
| JavaScript | npm | npm install toolclad |
| Go | Source | go install ./go/cmd/toolclad |
Reference Implementations
Each implementation provides:
- Manifest parsing -- load and validate
.clad.tomlfiles (oneshot, session, browser modes) - 14 type validators -- 10 core + 4 extended, all with injection sanitization
- Command construction -- template interpolation with mappings, conditionals, defaults
- Execution -- direct argv dispatch, process group kill on timeout, SHA-256 evidence hashing
- Output parsers -- builtin:json, builtin:xml, builtin:csv, builtin:jsonl, builtin:text, custom scripts
- Output schema validation -- validates parsed results against
[output.schema] - MCP schema generation -- auto-generate inputSchema + outputSchema for LLM tool use
- Evidence envelopes -- structured JSON with scan_id, timestamps, exit_code, stderr, output_hash
- CLI --
validate,run,schema,test(dry run) subcommands
Quick Start
# Rust
cd rust && cargo run -- test ../examples/whois_lookup.clad.toml --arg target=example.com
# Python
cd python && pip install -e . && toolclad test ../examples/whois_lookup.clad.toml --arg target=example.com
# JavaScript
cd js && npm install && node src/cli.js test ../examples/whois_lookup.clad.toml --arg target=example.com
# Go
cd go && go run ./cmd/toolclad test ../examples/whois_lookup.clad.toml --arg target=example.com
Type System
Core Types
| Type | Validates | Examples |
|---|---|---|
string |
Non-empty, injection-safe, optional regex pattern |
General text |
integer |
Numeric, optional min/max with clamp |
Thread counts |
port |
1-65535 | Network ports |
boolean |
Exactly "true" or "false" |
Feature flags |
enum |
Value in declared allowed list |
Scan types |
scope_target |
Injection-safe, no wildcards, valid IP/CIDR/hostname | Targets |
url |
Valid URL, optional schemes restriction, scope_check |
Web targets |
path |
No traversal (../), no absolute paths |
File paths |
ip_address |
Valid IPv4 or IPv6 | Addresses |
cidr |
Valid CIDR notation (IPv4 + IPv6) | Network ranges |
Extended Types
| Type | Validates | Use Case |
|---|---|---|
msf_options |
Semicolon-delimited set KEY VALUE pairs |
Metasploit options |
credential_file |
Relative path + must exist | Username/password lists |
duration |
Integer with suffix (30, 5m, 2h) |
Timeout overrides |
regex_match |
Matches declared pattern (required) |
Module paths |
Custom Types
Define reusable types in toolclad.toml at the project root:
[types.service_protocol]
base = "enum"
allowed = ["ssh", "ftp", "http", "https", "smb", "rdp"]
[types.severity_level]
base = "enum"
allowed = ["info", "low", "medium", "high", "critical"]
Reference in manifests: type = "service_protocol"
Output Handling
Built-in Parsers
| Parser | Use Case |
|---|---|
builtin:json |
Tools with native JSON output |
builtin:xml |
nmap, Nessus, OWASP ZAP |
builtin:csv |
Spreadsheet exports, log files |
builtin:jsonl |
Nuclei, streaming tools |
builtin:text |
Simple unstructured output (default) |
Custom Parsers
[output]
parser = "scripts/parse-outputs/parse-nmap-xml.py"
Custom parsers receive the raw output file path as argv[1] and emit JSON to stdout.
Evidence Envelope
Every execution returns a structured envelope:
{
"status": "success",
"scan_id": "1711929600-12345",
"tool": "nmap_scan",
"command": "nmap -sT -sV --max-rate 1000 10.0.1.0/24",
"duration_ms": 4523,
"timestamp": "2026-03-20T12:00:00Z",
"exit_code": 0,
"stderr": "",
"output_hash": "sha256:a1b2c3...",
"results": { "hosts": [...] }
}
On error, exit_code and stderr are included so LLM agents can self-correct.
Symbiont Integration
ToolClad is the tools/ directory convention for Symbiont:
- Runtime auto-discovers
.clad.tomlfiles at startup - Registers each tool as an MCP tool in the ORGA reasoning loop
- Cedar policy evaluation using manifest-declared
[tool.cedar]resource/action - Scope enforcement against
scope/scope.toml - Cedar policy auto-generation from manifest
risk_tier - Hot-reload in development mode
symbi tools list/validate/test/schema/initCLI
symbi tools list # show discovered tools
symbi tools test nmap_scan --arg target=10.0.1.0/24 --arg scan_type=service
symbi tools schema nmap_scan # output MCP JSON Schema
symbi tools init my_scanner # scaffold new manifest
SchemaPin Integration
SchemaPin signs .clad.toml files directly as first-class artifacts:
schemapin-sign tools/nmap_scan.clad.toml
The signature covers the entire behavioral contract. If anyone tampers with a command template, validation rule, scope constraint, or output schema, the hash changes and verification fails.
See TOOLCLAD_DESIGN_SPEC.md for the full specification.
License
- Protocol specification (manifest format, type system, evidence envelope): MIT
- Symbiont integration (Cedar gating, ORGA enforcement, scope enforcement): Apache 2.0
Metadata
Release files for toolclad 0.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| toolclad-0.6.1.tar.gz | 32.1 kB | Without provenance | Details for toolclad-0.6.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| toolclad-0.6.1-py3-none-any.whl | Python 3 | none | any | Details for toolclad-0.6.1-py3-none-any.whl |
Total release size: 63.3 kB
Release files / toolclad-0.6.1.tar.gz
| Download URL | toolclad-0.6.1.tar.gz |
|---|---|
| Size | 32.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d69a755d1e458de32fc183f74fa15bb0ba62b0bdf48ed4188b50932b2ddd6d33
|
|
BLAKE2b-256 checksum How to use checksums |
c9e2d3ffafa9e65309541b52e43b0bf3f8752f9c1590c8ac362afc017bb8d262
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / toolclad-0.6.1-py3-none-any.whl
| Download URL | toolclad-0.6.1-py3-none-any.whl |
|---|---|
| Size | 31.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
83971646693077137eb229b018c1ec14fc9b96566c3609625cd3cbe628912ad9
|
|
BLAKE2b-256 checksum How to use checksums |
cb77fcf85126807e53438b500e40e9e20e957d77498e01030870195aea30e5e7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Security for toolclad 0.6.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.