Back to Articles

Anthropic's CLI: A Minimalist Wrapper with Smart File Handling and Transform Pipelines

[ View on GitHub ]

Anthropic's CLI: A Minimalist Wrapper with Smart File Handling and Transform Pipelines

Hook

The Anthropic CLI can automatically detect whether your file needs base64 encoding or plain text transmission, but it can't stream responses—a curious trade-off for a tool built around a conversational AI model.

Context

When Anthropic launched Claude, developers immediately faced a familiar problem: how do you quickly test API calls, script batch operations, or integrate Claude into CI/CD pipelines without writing boilerplate code? While the company provides SDKs in Python, TypeScript, and Go, these require scaffolding—authentication setup, error handling, response parsing—just to send a single prompt.

The anthropic-cli emerged as Anthropic's answer to this friction. Built in Go, it's a thin wrapper around their official Go SDK that maps API endpoints to command-line operations. Unlike rich CLI tools that add opinions about workflows (conversation history, template systems, interactive modes), this tool stays deliberately minimal: it's essentially a type-safe curl replacement with some quality-of-life features for handling Claude's multimodal capabilities. The architecture choice reveals Anthropic's philosophy—ship completeness fast by staying close to the API surface rather than innovating on CLI patterns.

Technical Insight

Output Processing

Input Processing

parse flags & args

read file content

text content

binary content

base64 encode

API response

filter/extract

formatted result

CLI Entry Point

Command Parser

File Reference Handler

@filename syntax

Content Type Detector

http.DetectContentType

Anthropic Go SDK

API Client

Transform Pipeline

GJSON processor

Output Formatter

JSON/YAML/JSONL

User Terminal

System architecture — auto-generated

The anthropic-cli follows the resource-command pattern you'd recognize from AWS CLI or gcloud: anthropic messages create, anthropic models list, etc. Each command corresponds almost 1:1 with an API endpoint, which means you're learning the HTTP API semantics rather than CLI-specific abstractions. This sounds boring until you hit the two features that actually justify installing this over raw curl: file references and transform pipelines.

The file reference system uses @ syntax to inject file contents into your JSON payloads. Here's where it gets clever:

# Text files get inserted as string literals
anthropic messages create \
  --model claude-3-5-sonnet-20241022 \
  --max-tokens 1024 \
  --messages '[{"role": "user", "content": @prompt.txt}]'

# Binary files (images) get base64-encoded automatically
anthropic messages create \
  --model claude-3-5-sonnet-20241022 \
  --max-tokens 1024 \
  --messages '[
    {
      "role": "user",
      "content": [
        {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": @file://diagram.jpg}},
        {"type": "text", "text": "What does this diagram show?"}
      ]
    }
  ]'

Under the hood, the CLI performs content-type sniffing using Go's http.DetectContentType, which reads the first 512 bytes to guess MIME types. If it detects binary data (images, PDFs), it base64-encodes before transmission. If it's text, it injects the raw string. This eliminates the manual encoding step that makes vision API calls tedious:

# Without CLI, you'd do this:
base64 -i diagram.jpg | jq -R '{type: "image", source: {type: "base64", media_type: "image/jpeg", data: .}}'

# With CLI, it's automatic when you use @file://

The second useful feature is the --transform flag, which embeds GJSON path syntax for extracting nested JSON fields. Instead of piping to jq, you can specify what you want directly:

# Get just the response text, one result per line
anthropic messages create \
  --model claude-3-5-sonnet-20241022 \
  --messages '[{"role": "user", "content": "List three colors"}]' \
  --transform 'content.0.text'

# In a loop for batch processing with JSONL output
cat prompts.txt | while read prompt; do
  anthropic messages create \
    --model claude-3-5-sonnet-20241022 \
    --messages "[{\"role\": \"user\", \"content\": \"$prompt\"}]" \
    --format jsonl \
    --transform 'content.0.text'
done > results.jsonl

The GJSON syntax supports wildcards and filters (content.#.text gets all content blocks, content.#(type=="text").text filters by type), which is legitimately helpful when Claude returns multi-part responses with tool calls or citations.

Architecturally, this CLI is code-generated or tightly coupled to the Go SDK—the repository includes a ./scripts/link utility for swapping SDK versions during development. This explains why every SDK method has a CLI equivalent: they're likely sharing type definitions from the same OpenAPI spec. The benefit is zero drift between SDK and CLI capabilities. The cost is that the CLI can't evolve independently or add conveniences (like conversation state management) without those features landing in the SDK first.

Authentication follows standard patterns: ANTHROPIC_API_KEY environment variable or --api-key flag. There's also an ANTHROPIC_AUTH_TOKEN variable, suggesting internal testing infrastructure beyond production keys. Output formatting supports JSON, YAML, and JSONL with a separate --format-error flag, which is thoughtful for scripts where you want structured success responses but human-readable errors in stderr.

Gotcha

The most glaring limitation is no streaming support. Claude's Messages API supports server-sent events for real-time token generation, but this CLI only implements request-response calls. For a conversational AI model where streaming dramatically improves perceived latency, this is a bizarre omission. If you need streaming, you're stuck using the SDK directly or switching to tools like mods or aider that prioritize terminal UX.

The file content-type sniffing, while clever, can misfire. UTF-8 text files with binary headers (some CSV exports, certain XML files) might get base64-encoded unexpectedly. There's no --dry-run or preview mode to see what encoding will be applied before sending a request. You also can't override the detected type—if the CLI guesses wrong, your only option is manual encoding.

Production scripting will hit rate limit issues immediately. There's no retry logic, exponential backoff, or request queuing. If you're processing a batch of 1,000 prompts, you'll get 429 errors and need to implement retries yourself in bash/Python. The JSONL output format suggests batch use cases, but the tooling doesn't support them.

Finally, there's no session or conversation management. Each messages create call is stateless—you manually build the message history array. For multi-turn interactions, you're concatenating JSON arrays yourself or reaching for a higher-level framework. This isn't necessarily wrong (CLIs should be composable primitives), but it means anyone experimenting with agentic workflows will immediately outgrow this tool.

Verdict

Use if: You're scripting one-off Claude API calls in CI/CD pipelines, need type-safe argument validation without writing Go, or frequently work with vision models and want automatic image encoding. The transform flags genuinely reduce bash glue code for batch inference jobs where you're extracting specific fields from Claude's responses. It's also ideal if you're already using Anthropic's Go SDK and want CLI parity for testing.

Skip if: You need streaming responses for conversational UX, want conversation history management, or are building agents that require multi-turn interactions. Also skip if you need production-grade retry logic or rate limiting—this CLI is a thin wrapper, not an orchestration tool. Developers building interactive coding assistants should use aider, teams wanting cross-LLM support with better terminal UX should try mods or llm, and anyone doing quick experiments might find that curl + jq offers more flexibility without the abstraction.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/ai-dev-tools/anthropics-anthropic-cli.svg)](https://starlog.is/api/badge-click/ai-dev-tools/anthropics-anthropic-cli)