SCUA is a strong target for one-shot tools an agent (or a shell harness) generates and runs: default-deny I/O, fuel/memory kill-switches, exact money, and a predictable stdout / stderr / exit contract. This page is the full shape of that contract.
For plain positional args without a tool envelope, see Read command-line arguments.
#The contract
| Channel | Role |
|---|---|
| stdin | JSON payload (or empty) |
| stdout | Exactly one JSON result |
| stderr | Diagnostics only |
| exit status | 0 success · 1 clean negative ·
2 usage / bad args · other = crash |
import sys
record Args {
a: int where a in 0:1000000,
b: int where b in 0:1000000,
}
let args = sys.parse_args(Args)
sys.emit({ ok = true, sum = args.a + args.b })
$ echo '{"a":2,"b":3}' | scua tool.scua
{"ok":true,"sum":5}
$ scua tool.scua --a 2 --b 3
{"ok":true,"sum":5}
$ scua tool.scua --a 1
{"error":{…missing field "b"…}} # exit 2
Runnable end-to-end: examples/agent_tool.scua.
#Building the argument record
sys.parse_args(RecordType)
Declares the tool's interface once. The same record:
- validates CLI + stdin,
- runs every
whererefinement, - fills defaults,
- is what
scua schematurns into JSON Schema for callers (MCPinputSchema, etc.).
RecordType is a type name, not a value:
write sys.parse_args(Args), never Args().
Sources of fields (CLI overlays stdin):
| Source | Shape |
|---|---|
| stdin | one JSON object (if non-empty) |
| CLI | --key value, --key=value, or
--flag (→ true) |
Positionals without -- are ignored by
parse_args. A boundary violation (missing required field,
wrong type, failed where) never runs the tool body — it
calls sys.fail(…, 2).
#Lower-level pieces
| Call | Use when |
|---|---|
sys.stdin() |
You want the raw UTF-8 payload yourself |
sys.args() |
Positional strings only |
sys.args_table() |
Named flags as a loose table, no record type |
#Emitting a result or a failure
| Call | stdout | stderr | exit |
|---|---|---|---|
sys.emit(value) |
one JSON value (via json.encode) |
— | 0 |
sys.fail(reason) |
— | {"error": …} |
1 |
sys.fail(reason, code) |
— | {"error": …} |
code (0–255) |
sys.exit(code) |
flushes prior prints |
— | code |
sys.emit exits. Code after it never
runs. For streaming / multi-line output use print and a
final sys.exit.
decimal and money encode as
lossless strings (never JSON numbers):
import sys
sys.emit({ total = 19.99 USD })
$ scua tool.scua
{"total":"19.99 USD"}
A fault (bug) is different from sys.fail: faults print a
traceback and are for the author, not a chosen tool outcome.
#JSON Schema for callers
$ scua schema tool.scua --type Args
Emits JSON Schema draft 2020-12 for the decidable
subset of the record (where ranges, membership enums,
s.len() bounds, nested records via $defs).
Arbitrary where predicates stay authoritative in
SCUA; the schema says so in $comment and field
descriptions.
#Teaching a model SCUA
$ scua pack --repo=. --tier=compact --out=-
$ scua pack --repo=. --tier=both
Builds a markdown teach-pack: compact grammar, the
I/O paragraph above, collapsing primitives from the stdlib, few-shots
from examples/, reserved words, and common pitfalls. Token
estimates go to stderr. Prefer compact in a system
prompt; regenerate after language changes.
#Sandbox the run
Agent-generated code should not get ambient authority:
$ scua --max-ops=10m --max-mem=64M --allow-fs=./scratch tool.scua < in.json
--max-ops/--max-mem— kill-switches the script cannot disarm--allow-fs=DIR— rooted filesystem only (..cannot climb out)- no
--allow-net/--allow-envunless the tool needs them
See Read and write files and Read environment variables.