Skip to content
Data Integration & ETL · 7 min

A CLI Your Agent Can Drive

Command line tools are written for people: aligned columns, color, a prompt before anything destructive, an error in a sentence. Every one of those is a hazard for a program. mammoth-cli was built for both readers — deterministic output, no prompts off a terminal, stable error codes, and a mutation class on every command.

Point a coding agent at an ordinary command line tool and it works, right up until it doesn't.

Command line tools are written for people. They print aligned columns, they use color, they ask before they delete something, and when they fail they explain it in a sentence. Each of those is good design for a reader and a hazard for a program. Columns get scraped and misparsed. A confirmation prompt becomes a process that hangs forever with nothing on stdout. An error message gets pattern-matched on its wording, and then the wording changes in a patch release and the automation breaks somewhere far from the cause.

The usual workaround is a wrapper: a layer of shell that passes --json everywhere it remembers to, greps for known failure strings, and pipes yes into anything that might prompt. It works until the day it silently doesn't.

mammoth-cli was written for both readers from the start. In a terminal it prints a table. Piped to a program it emits a versioned JSON envelope, with no flags required.

Machine behavior is the default, not a flag

The single most useful property is that an agent does not have to remember anything.

mammoth project list            # a table, when a human is looking at it
mammoth project list | jq '.data'   # a JSON envelope, because something is parsing it

Output mode defaults to auto: a table on a terminal, JSON when stdout is a pipe or a file. Prompting follows the same rule — --no-input turns itself on when standard input is not a real terminal. So the correct behavior for automation is what you get by not doing anything, and an agent that forgets a flag gets the safe result rather than a wrong one.

You can still be explicit, and in an unusual TTY you should be:

mammoth dataset list --project 180 --output json --no-input

Read the envelope, not the text

Every machine-mode result is one shape. Success goes to stdout:

{"schema_version": 1, "data": [{"id": 180, "name": "Sales"}], "meta": {"command": "project list", "profile": "default", "workspace_id": 4, "project_id": 180, "pagination": null}}

Failure goes to stderr, and carries the fields a program actually needs to decide what to do next:

{"schema_version": 1, "error": {"code": "...", "message": "...", "hint": "...", "details": {}, "request_id": null, "retryable": false, "authorization_required": false, "recovery_commands": ["..."]}}

code is a stable machine identifier and is the thing to branch on. message is prose for a human and may be reworded at any time — parsing it is the mistake the envelope exists to prevent. retryable says whether trying again could plausibly work. recovery_commands carries the exact commands to run next, which is the difference between an agent that reports a failure and an agent that resolves one.

The process exit code mirrors the error class, so a caller can branch before it parses anything: 0 success, 1 API error, 2 usage or confirmation failure, 4 authentication, 5 not found, 6 conflict, 7 retryable, 130 interrupted. There is no exit code 3.

schema_version is a compatibility contract rather than a version stamp. Within the 1.x series the envelope and the CLI surface are stable — fields get added, existing ones are preserved, and command names, flags and exit codes are not removed or repurposed. A breaking change to the envelope shape increments the number, so a long-lived agent has something to check.

It fails instead of hanging

Destructive commands require --yes. Off a terminal there is no prompt at all — a missing confirmation exits 2 with confirmation_required, immediately, with a parseable envelope explaining why.

This is a small decision with a large consequence. The failure mode of a prompt in a non-interactive context is a job that sits doing nothing until something else times out, usually with no output to diagnose. Here that class of hang does not exist.

A few other rules make repeat runs safe:

  • The CLI never retries a mutation without a real server idempotency contract. Only exit code 7 is safe to retry.
  • Downloads write to a partial file and rename atomically. An existing target needs --overwrite.
  • An interruption returns 130 and closes sessions and files rather than leaving them open.
  • Secrets never appear on the command line, in logs, or in any envelope.

The rate limits underneath belong to the API and are the same for every caller, not scoped by plan: 600 GET, 300 POST, 60 PATCH and 60 DELETE per minute, counted in a one-minute fixed window (verified against the implementation, August 2026). That is a number worth knowing before you write a loop.

Every command declares what it will do to your data

This is the part built specifically because agents act without a person watching each step.

Every command carries a mutation class — what it does — and a confirmation policy — what you must pass before it runs. Both are readable at runtime with mammoth schema get <command.id>, so an agent can check the blast radius of an operation before invoking it rather than inferring it from the verb in the command name.

Mutation classMeaning
readNo change.
benign_mutationA small, low-risk change.
reversible_pipelineA pipeline edit you can undo.
destructiveDeletes or overwrites data.
external_effectActs outside Mammoth — sends a message, writes to an external store.
high_impactWorkspace or account level, hard to reverse.

The policies escalate with the class: none, prompt_or_yes, yes_always, and confirm_target — which requires you to pass the exact identifier of the thing you are acting on.

# a normal delete
mammoth dataset delete 2340 --project 180 --yes

# high impact: --confirm must match the workspace id exactly
mammoth workspace delete --yes --confirm 9

An agent that has guessed the wrong id cannot satisfy --confirm. That is the point: the guard is not a warning it can agree to, it is a value it has to already know.

Discovery at runtime

An agent should not have to be trained on a command surface that changes. It can ask:

mammoth capability list          # every operation
mammoth schema list              # every command's schema
mammoth schema get <command.id>  # one request shape, with its mutation class

The generated documentation ships in agent-readable form too — docs/llms.txt indexes the guides, and docs/llms-full.txt lists every command with its mutation class, confirmation policy and backing SDK symbol. Verify a request shape against the installed CLI rather than against a page that may describe a different version.

Multi-field requests go in as one document instead of a long flag list, which is considerably easier for a model to construct correctly:

mammoth view transform math 1039 --project 180 \
  --input '{"expression": "price * qty", "new_column": "total"}'

The reliable loop is short: discover the shape with schema get, run reads in machine mode and keep the returned ids, supply structured input plus the required confirmation for a mutation, branch on the exit code and error.code, retry only 7, and clean up by id rather than by name match.

The playbook installs with the tool

Knowing the commands is not the same as knowing the conventions — how login works without a terminal, when to use structured input, how jobs are waited on, which confirmations apply. That guidance ships as an installable skill for Claude Code, Codex and Cursor:

mammoth skill install
mammoth skill list

mammoth skill update refreshes copies the installer owns after an upgrade, and reports modified copies instead of silently replacing them.

Installing it

One line, no prerequisites. It installs uv if you don't have it, the mammoth command, and the agent skill:

curl -fsSL https://github.com/EdgeMetric/mammothsdk/releases/latest/download/mammoth-install.sh | sh

If you already run a Python tool manager, uv tool install mammoth-cli, pipx install mammoth-cli or python -m pip install mammoth-cli all work; the CLI supports Python 3.12, 3.13 and 3.14. Piping a download to a shell does not verify it first — the release ships SHA256SUMS and a Sigstore bundle, and the verified flow is documented in the repo.

Then:

mammoth doctor    # checks config, credentials, endpoint, connectivity

For an agent or a CI job, log in from a permission-checked file rather than putting credentials in arguments:

chmod 600 creds.json
mammoth auth login --input creds.json --output json --no-input

What you need, and what doesn't exist

Programmatic access — the API, the SDK and this CLI — is a Pro and Enterprise capability. Free and Starter workspaces are not the audience for this piece. Signup starts on a 21-day Pro trial with no credit card, which is enough to try the whole surface before deciding anything; the tiers are on the pricing page.

The CLI is built on the public mammoth-io Python SDK. It adds no second HTTP client and calls no private SDK members, so the two cannot drift into disagreeing about what an operation does. If you are writing Python, use the SDK directly; if you are driving Mammoth from an agent, a shell or CI, use the CLI.

There is no JavaScript or TypeScript SDK. If you need one from Node, call the REST API.

What this is not

None of this is autonomy. The CLI does not decide anything. It executes what it is told, declares in advance what that will do, refuses to guess when a confirmation is missing, and reports the result in a shape that does not change underneath a program.

That is a deliberately unexciting claim, and it is the one that matters when the caller is not a person who can notice something looks wrong. An agent inherits the guarantees of its tools. Given a tool with none, it produces confident output nobody can check — the same failure the rest of the platform is built to avoid.

Source, full command reference and the guides: github.com/EdgeMetric/mammothsdk.

Try it on your data. Today.

The argument in this post is easier to check than to read about. Start free and see.

  • 21-day Pro trial
  • No credit card
  • Viewers always free