Skip to main content

Work with agents

Preview
This feature is in preview and is subject to change.

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.

tip

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.

note

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.

warning

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:

TaskThrough MCPThrough the CLI
Read a Dive or a Flightread_dive or get_flight returns the whole source in the responsedive pull or flight pull writes the files to disk, and the agent reads the part it needs
Save an editThe agent sends the changed content back as a tool argumentThe agent edits the file in place, and dive push or flight push reads it from disk
List Dives or Flightslist_dives or list_flights returns every field of every resultdive list --output json piped through jq returns the IDs alone
Return a large result setEvery row lands in the context windowRedirect it: motherduck query "..." --output csv > result.csv
Chain several stepsEach intermediate result passes through the modelOne 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.