Home
Operating

Recipes

End-to-end CLI examples for CI pipelines, local operating, and agent runners

Concrete patterns for the three audiences the CLI is built for.

CI pipelines

GitHub Actions: send a release notification

Use nitrosend mcp tools call to fire a transactional message after a deploy. --machine gives stable JSON, exit codes, and an auto-issued idempotency key.

name: Notify release
on:
  release:
    types: [published]

jobs:
  notify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm install -g @nitrosend/cli
      - name: Send release email
        env:
          NITROSEND_API_KEY: ${{ secrets.NITROSEND_API_KEY }}
        run: |
          nitrosend mcp tools call nitro_send_message --machine --args "$(cat <<EOF
          {
            "channel": "email",
            "to": "team@example.com",
            "subject": "Release ${{ github.event.release.tag_name }}",
            "body": "<p>${{ github.event.release.html_url }}</p>",
            "idempotency_key": "release-${{ github.event.release.id }}"
          }
          EOF
          )"

The idempotency_key inside --args is what the platform dedupes against — set it from a stable, retry-survivable value like the release ID so a re-run of the job won't double-send. Branch on exit codes for retry strategy: 75 retriable, 77 means rotate the API key, 78 means upgrade the CLI step.

Daily account health check

Run the dashboard read, fail the job on blockers:

#!/usr/bin/env bash
set -euo pipefail

result=$(nitrosend --machine)
blockers=$(echo "$result" | jq -r '.sidecars.blockers // [] | length')

if [ "$blockers" -gt 0 ]; then
  echo "$result" | jq '.sidecars.blockers'
  exit 1
fi

Local operating

Quick status snapshot

Three commands cover most "what's the state of my account" questions:

nitrosend                              # account state, blockers, next_action
nitrosend mcp resources read nitro://account
nitrosend mcp tools call nitro_get_insights --args '{"scope":"account"}'

Pipe campaign results into a spreadsheet

nitrosend mcp tools call nitro_query \
  --args '{"resource":"campaigns","page":1,"per_page":100}' \
  --csv > campaigns.csv

CSV mode is table-only — no envelope, no sidecars. Use it when you want raw rows.

Replay a recent command

nitrosend recent              # show the last 10 commands
nitrosend redo 1 --explain    # show what `redo 1` would run, without running

recent and redo are local-only — they never call the API and they redact obvious secrets in their stored history.

Tab completion

# bash
nitrosend completion bash >> ~/.bashrc

# zsh
nitrosend completion zsh > ~/.config/zsh/completions/_nitrosend

# fish
nitrosend completion fish > ~/.config/fish/completions/nitrosend.fish

Agent runners

Plan, then act

Use --explain to get a deterministic plan before deciding to execute. The plan is the same envelope shape as a real run, with would_execute: false.

plan=$(nitrosend mcp tools call nitro_compose_campaign \
  --args "$(cat brief.json)" --explain --machine)

# Inspect the plan, decide, then run for real
echo "$plan" | jq '.data'

if [ "$(echo "$plan" | jq -r '.data.would_execute')" = "false" ]; then
  nitrosend mcp tools call nitro_compose_campaign \
    --args "$(cat brief.json)" --machine
fi

Discover the contract before calling

Cache the descriptor at startup. Don't guess flags.

nitrosend describe mcp tools call --json | jq '.input_schema'

The descriptor includes safety class, dry-run support, idempotency policy, and an agent.suitable flag. Skip any command marked suitable: false from an autonomous runner.

Approval handoff

When an agent needs human sign-off mid-run, hand off to approve / reject and pause. The CLI exposes these as stable stubs today and will gain an SSE waiter endpoint in a future release.

# Agent emits a token, then waits.
nitrosend approve $TOKEN   # human runs this
nitrosend reject  $TOKEN   # or this

Read sidecars for next-action loops

Don't write your own "what should I do next" logic. The platform already returns one. Read sidecars.next_action and sidecars.suggested_tool_calls and let it drive the loop.

result=$(nitrosend mcp tools call nitro_get_status --machine)
next=$(echo "$result" | jq -r '.sidecars.next_action // empty')
[ -n "$next" ] && echo "Next: $next"

See also