Work with agents
The MotherDuck CLI is designed for both AI agents and people. An agent can read authoring guides to learn how to best build Dives and Flights with the CLI. Because the CLI works through files and stdout rather than tool results, it handles large files and multi-step automation with far less context than the MCP server, which is the better fit for exploring data from a chat client. See choosing between the CLI and MCP.
Whether through an agent, in your local development environment, or in CI, the CLI lets you create, publish, and automate your MotherDuck workflows with output both humans and machines can understand.
Point the agent at the built-in guides
motherduck dive guide and motherduck flight guide print the authoring
guide for each. They cover the shape the runtime requires, the query APIs, the
libraries you can import, and the patterns that don't work.
motherduck dive guide
motherduck flight guide
These guides are long and specific, which is what an agent needs. Have the agent run the relevant one before it writes any code, and you avoid the usual failure where a model invents a component or an import the runtime doesn't have.
Put the instruction in your project's agent memory file, such as CLAUDE.md or
AGENTS.md, so it applies to every session:
Before writing or editing a Dive or a Flight, get the latest instructions from
the output of running `motherduck [dive | flight] guide`.
Give the agent a task
With the guides available, the prompt can stay short. Ask for the outcome and let the agent discover the rest:
Build a Dive that charts daily taxi trip counts and average fare for
November 2022 from sample_data.nyc.taxi, with a day-of-week filter.
Preview it locally, and once it renders, publish it.
A capable agent works through something close to this:
motherduck dive guide # read the authoring guide
motherduck query "DESCRIBE sample_data.nyc.taxi" --output json
motherduck dive init taxi_trips --title "Taxi trips" # scaffold the directory
# ... writes index.tsx ...
motherduck dive watch taxi_trips --no-open # render it, read the events
motherduck dive push taxi_trips --output json # publish, capture the URL
--no-open keeps the preview from stealing focus, and --log-file writes
render and query outcomes as NDJSON so the agent can read whether its component
compiled instead of asking you to look:
motherduck dive watch taxi_trips --no-open --log-file preview.ndjson
JSON output everywhere for programmatic use
The --output json option makes the CLI's output easy to parse
programmatically. Commands that act on a Dive or a Flight return it under a key
named for the resource, described under
result shape:
motherduck dive push taxi_trips --output json
{
"success": true,
"dive": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"title": "Taxi trips",
"version": 2,
"url": "https://app.motherduck.com/dives/taxi-trips-123e4567-e89b-12d3-a456-426614174000"
}
}
So a script reads one field instead of the whole message:
motherduck dive push taxi_trips --output json | jq -r '.dive.url'
A failure prints {"success": false, "error": "..."} and exits non-zero, so an
agent checks one field rather than reading prose.
The success field doesn't appear in the output of
query, which returns its rows as a
bare JSON array. See
output formats.
That's what lets an agent chain steps in a script rather than in its context window. Each command hands the next one a single field, so a multi-step workflow costs a few tokens instead of a transcript of full outputs:
#!/usr/bin/env bash
set -euo pipefail
# Trigger a Flight, then wait for the run to settle.
RUN=$(motherduck flight run nightly_load --output json | jq -r '.run.run_number')
while :; do
STATUS=$(motherduck flight list-runs nightly_load --limit 1 --output json \
| jq -r '.runs[0].status')
[[ "$STATUS" == "PENDING" || "$STATUS" == "RUNNING" ]] || break
sleep 10
done
# On failure, surface the reason and stop.
if [[ "$STATUS" != "SUCCEEDED" ]]; then
motherduck flight logs nightly_load --run "$RUN" | tail -20 >&2
exit 1
fi
# The data landed, so publish a Dive over it.
motherduck dive push daily_totals --output json | jq -r '.dive.url'
The agent writes that once and reads one URL back, instead of holding every intermediate result in its context.
Give the run its own credentials
Pass a token rather than running the browser flow, and point the CLI at a directory of its own:
export MOTHERDUCK_TOKEN=<your_token>
export MOTHERDUCK_HOME=/workspace/.motherduck
MOTHERDUCK_HOME gives the run its own credentials and asset directory, which
keeps parallel agents from sharing state. It has to be an absolute path.
Where there's no account to get a token from,
motherduck new creates one from the terminal without a
browser or a signup form.
An agent with a MotherDuck token can read and write whatever that token can. Scope it to what the task needs, and prefer a read-only token for agents that only query. See securing read-only access.
Choosing between the CLI and MCP
Both let an agent work with MotherDuck. The deciding question is whether the agent has a shell and a filesystem:
- The CLI fits agents that run commands and write files: a coding agent building a Dive or a Flight in a repository, a CI job, or a shell script.
- The MCP server fits agents in a chat client with no shell, such as Claude or ChatGPT on the web. Use it to explore data, answer a question, and render a Dive inline in the conversation.
They work together: an agent can explore through MCP, then use the CLI to build and publish what it found.
Why the CLI costs fewer tokens for file-shaped work
An MCP tool result is a message. Whatever the server returns, a Dive's component code, a Flight's source, a list of every Dive in the workspace, or a thousand query rows, is serialized into the model's context. It takes up the context window and gets resent on every turn that follows.
The CLI writes to stdout or to files on disk, and the agent picks what to read
back. It can filter a listing through jq, read only the function it's changing
out of a Dive it pulled, or hand a file straight to the next command. Only what
the agent reads reaches the context window.
So for anything file-shaped, prefer the CLI:
| Task | Through MCP | Through the CLI |
|---|---|---|
| Read a Dive or a Flight | read_dive or get_flight returns the whole source in the response | dive pull or flight pull writes the files to disk, and the agent reads the part it needs |
| Save an edit | The agent sends the changed content back as a tool argument | The agent edits the file in place, and dive push or flight push reads it from disk |
| List Dives or Flights | list_dives or list_flights returns every field of every result | dive list --output json piped through jq returns the IDs alone |
| Return a large result set | Every row lands in the context window | Redirect it: motherduck query "..." --output csv > result.csv |
| Chain several steps | Each intermediate result passes through the model | One shell script hands each command's output to the next |
The gap widens the more you iterate. Pull a Dive once and the local file carries every revision after that, so the agent patches a few lines instead of moving the whole component through the conversation twice per round.