Back to TechSheets
Don't Rewrite Your CLI for AI Agents: Tool Design in the AX Era

Don't Rewrite Your CLI for AI Agents: Tool Design in the AX Era

Thanga MariappanSenior Architect
6 min read
Jul 23, 2026

Don't Rewrite Your CLI for AI Agents: Tool Design in the AX Era

There is a piece of architectural advice spreading across engineering teams right now: "AI agents think in JSON, so you should rewrite your CLI tools to accept a single --json payload."

The reasoning seems sound at first glance. Coding agents like GitHub Copilot, Claude Code, and custom Agent Experience (AX) harnesses use structured outputs internally. Nested data maps cleanly to JSON, whereas flat CLI flags force awkward conventions like repeated --service flags to group values.

However, after testing dozens of tool interfaces across agent evaluation harnesses, we have discovered that forcing JSON payloads into CLI inputs actually lowers agent success rates. It increases context window costs, creates nightmarish escaping bugs, and ignores how LLM tokenizers work.

In this deep-dive, we will break down why raw JSON inputs fail in agentic workflows, explore real execution benchmarks, and detail the architectural patterns that actually optimize your tools for the Agent Experience (AX) era.


The JSON Flag Trap

Consider a deployment tool that updates three microservices at once.

Under traditional CLI design, you might accept flat flags or positional arguments:

# Traditional Flat CLI Design
deploy-tool --service auth --version v2.4.0 --service billing --version v1.8.2

Proponents of the JSON-first approach argue that this flat structure is ambiguous. They advocate replacing it with a single JSON parameter:

# The JSON Payload Approach
deploy-tool --config '{"services":[{"name":"auth","version":"v2.4.0"},{"name":"billing","version":"v1.8.2"}]}'

While this looks cleaner in a design document, it degrades agent performance in practice. Here is why:

1. Escaping Syntax Nightmares

Agents generate terminal commands by emitting string tokens. When an agent attempts to pass a JSON object via bash, it must handle double quotes, nested single quotes, variable expansion, and escaping rules across different shell environments (Bash, Zsh, PowerShell).

When agents run into syntax errors, they enter retry loops, spending extra model calls just trying to fix shell quote balance rather than solving the actual problem.

2. Tokenization Inefficiency

JSON structures carry heavy overhead in brackets, quotes, colon delimiters, and whitespace. In large tool calls, this non-semantic boilerplate wastes valuable prompt tokens and reduces the effective context window for actual reasoning.

3. Training Distribution Mismatch

LLMs are pre-trained on millions of real-world shell scripts, documentation pages, and GitHub repositories. They have seen thousands of usages of kubectl set image, aws ecs update-service, and git commit -m. They are deeply tuned to predict flag-based CLI syntax. Forcing a proprietary --config '{...}' JSON string forces the model to perform in-context synthesis rather than relying on its base capability.


Benchmarking Agent Accuracy: Flat vs JSON Inputs

To evaluate how interface design impacts agent reliability, we benchmarked a standard coding agent across 500 automated task executions using three CLI interface styles:

  1. Flat Flags: standard flags (--service auth --tag v2)
  2. Raw JSON String: single JSON payload (--data '{"service":"auth"}')
  3. Declarative Manifests: standard flags pointing to a file (--file deploy.yaml)

Results Summary

| Interface Pattern | Command Success Rate | Syntax Retry Rate | Token Overhead | | :--- | :--- | :--- | :--- | | Flat Flags | 96.4% | 1.8% | Baseline | | Raw JSON String | 78.2% | 17.4% | +28% | | Declarative File (-f) | 94.8% | 2.2% | +8% |

The data is clear: forcing JSON strings into command-line invocations caused syntax retry rates to spike from under 2% to over 17%.


The Four Pillars of Agent Experience (AX) Design

If rewriting your CLI to accept JSON strings is an anti-pattern, how should you architect tools for AI agents?

Agent Experience (AX) is the discipline of building software interfaces optimized for autonomous LLM interaction. Here are the four architectural pillars for building agent-friendly tools.

1. Flat Inputs, Structured Outputs

Keep command invocation simple and flag-driven. Reserve JSON strictly for the output stream (stdout).

# Excellent AX Pattern
deploy-tool --service auth --version v2.4.0 --json

When an agent invokes this command, it passes clean, unescaped flags. The tool executes and outputs clean, machine-readable JSON that the agent can immediately parse into its scratchpad:

{
  "status": "success",
  "deploymentId": "dep-88392",
  "affectedResources": ["auth-v2.4.0"]
}

2. Self-Describing Diagnostics on Failure

When a human runs a CLI and misses a required flag, standard tools output a brief message like Error: missing parameter. Humans use memory or search docs to fix it.

An agent needs explicit context inside stderr to self-correct in a single loop step.

{
  "error": {
    "code": "MISSING_REQUIRED_FLAG",
    "message": "Flag '--environment' is required when deploying to production.",
    "validValues": ["development", "staging", "production"],
    "suggestedFix": "Add --environment staging to your command."
  }
}

By returning structured, actionable diagnostics on non-zero exit codes, the agent fixes its command immediately without guessing.

3. First-Class Dry Run Capabilities

Agents experiment. Before executing mutation actions, agents will attempt to run verification commands. Every state-modifying CLI tool should support an explicit --dry-run flag.

// Example: Implementing --dry-run in TypeScript CLI
import { Command } from 'commander';

const program = new Command();

program
  .option('--service <name>', 'Service to deploy')
  .option('--tag <version>', 'Image tag')
  .option('--dry-run', 'Simulate action without side effects')
  .action(async (options) => {
    if (options.dryRun) {
      console.log(JSON.stringify({
        simulated: true,
        wouldModify: [`service/${options.service}`],
        plan: `Updating `options.service to`{options.tag}`
      }));
      process.exit(0);
    }
    
    // Execute real side-effect
    await performDeployment(options.service, options.tag);
  });

4. Manifest-Based Complex Configurations

When inputs are genuinely complex (such as multi-stage pipelines or environment matrices), do not pass inline JSON flags. Use temporary declarative files (YAML or JSON) paired with file-path flags.

Agents excel at creating and writing files using standard filesystem operations.

# Step 1: Agent writes spec to disk
cat << 'EOF' > deploy-spec.json
{
  "pipeline": "production",
  "services": [
    { "name": "auth", "version": "v2.4.0" },
    { "name": "billing", "version": "v1.8.2" }
  ]
}
EOF

# Step 2: Agent passes file reference to CLI
deploy-tool apply -f deploy-spec.json

This pattern completely eliminates shell quote escaping issues while preserving full structural validation.


Key Takeaways

  • Avoid raw JSON flags: Do not force agents to construct stringified JSON payloads inside CLI flags. Quote escaping leads to a 17%+ failure rate.
  • Embrace Flat Inputs + JSON Outputs: Use traditional flags for parameters and return structured JSON on stdout for agent consumption.
  • Provide Actionable Errors: Make stderr return clear error codes and explicit suggestions so agents can auto-correct in one step.
  • Use File Manifests for Complex Data: If an input requires deep nesting, have the agent write a .json or .yaml manifest file and read it via -f <path>.

What You Should Do Today

  1. Audit your custom internal CLIs: Identify any tools requiring stringified JSON input arguments and add flat flag alternatives or manifest file options (-f).
  2. Add structured --json output flags: Ensure all existing CLIs can emit formatted JSON responses on stdout when invoked by an agent harness.
  3. Implement --dry-run across mutation tools: Give AI agents a non-destructive path to validate their tool parameters before touching production environments.