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.

Three adult colleagues explain agent cli
AI-generated teaching illustration, not an actual operating screen or performance record.

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.

A browser report containing the actual stdout from print_json() and print_error() in LLMStockTrading/lib/output.py. SAMPLE is invented and contains no account, key or real incident. The original ok/data/error envelope is simpler than the article’s recommended contract and does not demonstrate versioning, request IDs or strict type validation.
A browser report containing the actual stdout from print_json() and print_error() in LLMStockTrading/lib/output.py. SAMPLE is invented and contains no account, key or real incident. The original ok/data/error envelope is simpler than the article’s recommended contract and does not demonstrate versioning, request IDs or strict type validation.

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

댓글