Designing CLI Tools for LLM Agents
TECH INSIGHT / EXPLAINED
An agent-friendly CLI has predictable data, bounded execution, and explicit authority. The model may propose a decision, but the executable must still validate inputs and enforce limits before a side effect.
Local execution of the actual project
These captures show the existing project UI or functions executed locally with non-private sample inputs. Each caption states the scope of the execution.
One JSON document on stdout
Use a versioned success/error envelope with a request ID and a matching exit status. Send diagnostic logs to stderr. Avoid default=str as a universal serializer: an unexpected object silently becoming a string hides a contract error. Encode known types deliberately and reject nonfinite numeric values. Set UTF-8 output on Windows; consumers should specify UTF-8 as well.
Errors should be useful without leaking secrets
Return a stable error code such as STALE_INPUT or LIMIT_EXCEEDED and a short safe message. Keep tracebacks in restricted local logs with credentials and sensitive response bodies removed. Printing every exception and full traceback into an agent context can disclose tokens, account details, or local paths. Distinguish a rejected request from an unknown result after a timeout.
Snapshots need transactional thinking
Keep a compact snapshot plus an append-only event ledger. Write a temporary file in the same directory, flush it, and atomically replace the snapshot where supported; coordinate multiple writers or use a transactional database. Record schema version, observed_at and source. Read external balances and orders for reconciliation, while retaining durable local intent IDs: the exchange alone does not explain why your application sent an action.
A concrete two-step contract
A quote tool returns a proposed action, input timestamps, maximum size and a short-lived plan ID. An execute tool accepts that ID, rechecks expiry, symbol allowlist, available balance and hard limits, then writes an idempotency key before submission. If a 100-unit cap exists, an agent asking for 101 must be rejected by code regardless of its reasoning. A timeout moves the action to reconciliation, not an automatic fresh submission.
Keep read ticks separate from actions
A tick endpoint may gather account, positions and signals without trading. Include per-source timestamps so a partly failed query is visible. Model recommendations are untrusted inputs to a deterministic risk gate; moving rules into a prompt must never remove safety limits from code. Run subprocesses with an argument list, bounded output and deadlines. Avoid passing model-provided text through a shell.
Example messages
These hypothetical messages show the quote response, an execution request, and its rejection. The plan must be stored server-side and expiry evaluated at execution; a client-supplied max_quantity is not authority.
{"ok":true,"schema_version":1,"request_id":"r-42","data":{"plan_id":"p-7","expires_at":"2026-09-29T01:01:00Z","symbol":"EXAMPLE","max_quantity":100,"observed_at":"2026-09-29T01:00:00Z"}}
{"request_id":"r-43","plan_id":"p-7","quantity":101,"idempotency_key":"intent-7"}
{"ok":false,"schema_version":1,"request_id":"r-43","error":{"code":"LIMIT_EXCEEDED","message":"quantity exceeds plan limit"}}Documentation and next steps
Content and examples reviewed: 2026-09-29
댓글
댓글 쓰기