Skip to content

olgasafonova/miro-cli

Repository files navigation

Miro Developer Platform CLI

Drive Miro from your shell. Bulk-migrate stickies between boards, audit board access, or script Miro operations in CI. One verb per Miro REST endpoint, JSON in and out, with a local SQLite mirror for offline search.

Why use it

  • Shell-first. Every endpoint is a flag-driven command. No hand-rolled JSON envelopes, no pagination boilerplate, no curl + jq pipelines.
  • Agent-friendly. --json, --dry-run, --select, and --agent are built in so the output is predictable for scripts and LLMs.
  • Offline workflows. miro-cli sync mirrors boards and items into a local SQLite store; miro-cli query runs SQL and FTS5 search against it without spending API quota.
  • Bulk verbs. items bulk-create, items bulk-update, items bulk-delete, and stickies create-grid collapse N HTTP calls into one for workshop and migration flows.
  • Safe defaults. Destructive verbs refuse to run without --yes (or --agent, which implies it). --idempotent makes create retries safe.

When to reach for what

You want to... Use
Script Miro from bash, CI, or a Makefile miro-cli (this repo)
Drive Miro from Claude Code as a skill miro-cli + the bundled SKILL.md
Drive Miro from a Claude Desktop / MCP-compatible agent miro-mcp-server
Render Miro data as interactive UI in chat (cards, tables, SVG graphs) miro-mcp-apps
Embed Miro into a TypeScript or Python app The official Miro SDKs
Issue a one-off API call to test a payload miro-cli <verb> --dry-run, or curl

The CLI, the MCP server, and the MCP Apps server are complements, not alternatives: same author, overlapping API coverage, different runtimes. Use whichever ones fit. They all read the same MIRO_ACCESS_TOKEN.

Install

From source

go install github.com/olgasafonova/miro-cli/cmd/miro-cli@latest

This drops a miro-cli binary in $GOPATH/bin (typically ~/go/bin). Make sure that directory is on your PATH.

Pre-built binary

Download the appropriate archive for your platform from the latest release and extract the miro-cli binary into a directory on your PATH. On macOS, clear the Gatekeeper quarantine: xattr -d com.apple.quarantine miro-cli. On Unix, mark it executable: chmod +x miro-cli.

Homebrew

brew install olgasafonova/tap/miro-cli

Quick Start

1. Set your access token

Get an access token from the Miro developer portal and export it:

export MIRO_ACCESS_TOKEN="your-token-here"

Or pass --token on every invocation.

2. Try your first command

miro-cli boards list
miro-cli boards get --board-id <id>
miro-cli stickies create --board-id <id> --content "Hello, Miro" --color yellow

Run miro-cli --help for the full command reference, or miro-cli <group> --help for the verbs under any group.

Commands

Twenty-two resource groups today. The tables below summarize each group at a glance; run miro-cli <group> --help for the full verb list and flags.

Board management

Group Purpose Key verbs
boards Board lifecycle plus a few search / content helpers list, get, create, update, delete, copy, find, search, content, summary, picture, share, diagram, audit
members Board access control list, get, update, remove

Item CRUD (one group per item type)

Each group below ships create / get / update / delete against its own Miro REST resource. The table only calls out non-standard verbs.

Group Resource path Extras
stickies /v2/boards/{id}/sticky_notes create-grid (bulk row-major layout)
shapes /v2/boards/{id}/shapes create-flowchart (v2-experimental stencils)
texts /v2/boards/{id}/texts
frames /v2/boards/{id}/frames
cards /v2/boards/{id}/cards
app-cards /v2/boards/{id}/app_cards
connectors /v2/boards/{id}/connectors list
embeds /v2/boards/{id}/embeds
documents /v2/boards/{id}/documents and /v2/boards/{id}/docs upload (multipart from disk), update-from-file (replace bytes), create-doc (Markdown rich-text)
images /v2/boards/{id}/images upload, update-from-file
codewidgets /v2-experimental/boards/{id}/code_widgets read-only list (experimental)
mindmap /v2-experimental/boards/{id}/mindmap_nodes list
tables /v2/boards/{id}/data_table_formats read-only list and get

Cross-type item operations

items is the catch-all for verbs that don't care about the underlying type. Bulk verbs are here because Miro's bulk endpoint accepts a typed array, not per-resource arrays.

Verb What it does
items list / items list-all Cursor-paginated list, or fetch everything until the cursor exhausts
items get Get any item by ID (returns the right shape based on type)
items update / items delete Cross-type partial update + delete
items bulk-create Create up to 20 items per call, mixed types
items bulk-update PATCH many items in one logical call (serial under the hood)
items bulk-delete DELETE many items in one logical call
items get-within-frame List items inside a frame
items get-by-tag List items carrying a tag
items get-tags / attach-tag / detach-tag Tag membership for a single item

Tags and grouping

Group Purpose Verbs
tags Tag definitions on a board list, get, create, update, delete
groups Group items so they move/resize as one list, get, get-items, create, update (full PUT), delete (ungroup, items survive)

Local store (offline workflow)

Command What it does
sync Mirror boards + items into a local SQLite store (incremental by default)
query Run a read-only SQL or FTS5 query against the store

See the Local Store section below for the full workflow.

Enterprise / audit

Group Purpose Notes
audit Enterprise audit log (list-logs) Last 90 days; CSV export for older data
exports Org-scoped board export jobs (eDiscovery) create-job, list-job-tasks, get-job-status, get-job-results, get-task-link, update-job

Both require Enterprise-plan tokens with the matching scopes.

Output Formats

# Human-readable table (default in terminal, JSON when piped)
miro-cli boards get

# JSON for scripting and agents
miro-cli boards get --json

# Filter to specific fields
miro-cli boards get --json --select id,name,status

# Dry run, show the request without sending
miro-cli boards get --dry-run

# Agent mode, JSON + compact + no prompts in one flag
miro-cli boards get --agent

Local Store

miro-cli ships a local SQLite mirror of your boards and items so you can run ad-hoc SQL and full-text search without burning API calls or waiting for network round-trips. The default store path is $XDG_DATA_HOME/miro-cli/store.db (or ~/.local/share/miro-cli/store.db); override it for any command with --store-path.

miro-cli sync, populate the store

# Incremental: re-fetches a board's items only when modifiedAt has advanced
# past the stored watermark. First run does a full sweep.
miro-cli sync

# Force a full re-fetch (after a schema change, or when you suspect drift)
miro-cli sync --full

# Pin the watermark for one run, useful when a previous sync was interrupted
miro-cli sync --since 2026-05-14T00:00:00Z

Conflict policy is last-write-wins: the API is the source of truth, and rows are upserted by id. The watermark is stamped at the start of a successful run, so a mid-run failure leaves the previous watermark in place and the next run resumes.

miro-cli query, SQL passthrough

# Read-only SELECT against the store. Output is JSON when piped,
# tab-separated table when stdout is a terminal.
miro-cli query "SELECT id, name FROM boards ORDER BY modified_at DESC LIMIT 10"

# Items on a board, by type
miro-cli query "SELECT type, COUNT(*) FROM items WHERE board_id = 'b1' GROUP BY type"

The connection is opened with mode=ro and PRAGMA query_only=ON; non-SELECT input is rejected before execution. --limit (default 1000) caps the rows returned per query, pass --limit 0 to disable the cap.

Full-text search

An items_fts FTS5 virtual table is kept in lockstep with items via triggers. It indexes the textual fields Miro models as data.content, data.title, and data.description: sticky notes, cards, text widgets, shapes, app cards, and frame titles.

# Find every item mentioning "roadmap" across all synced boards
miro-cli query "SELECT item_id, board_id, item_type FROM items_fts WHERE items_fts MATCH 'roadmap'"

# Phrase match, adjacent tokens in any indexed field
miro-cli query 'SELECT item_id FROM items_fts WHERE items_fts MATCH "Q3 review"'

# Join back to items for richer columns
miro-cli query "SELECT i.id, i.type, i.position_x, i.position_y
  FROM items_fts f JOIN items i ON i.id = f.item_id
  WHERE items_fts MATCH 'design'"

The tokenizer is unicode61 (FTS5's default) and does not stem, so fox will not match foxes. Use the * suffix for prefix matching: MATCH 'fox*'.

Agent Usage

This CLI is designed for AI agent consumption:

  • Non-interactive. Never prompts; every input is a flag.
  • Pipeable. --json writes structured output to stdout; errors go to stderr.
  • Filterable. --select id,name,status returns only the fields you need.
  • Previewable. --dry-run prints the request without sending it.
  • Idempotent create. --idempotent makes create-then-retry safe.
  • Confirmable. --yes (or --agent, which implies it) is required for destructive verbs.
  • Offline-friendly. miro-cli sync mirrors boards + items locally; miro-cli query runs SQL and FTS5 search against the mirror without API round-trips.
  • Agent-safe by default. No ANSI colors or formatting in output.

Exit codes: 0 success, 2 usage error, 3 not found, 4 auth error, 5 API error, 7 rate limited, 10 config error.

Use with Claude Code

This repo ships a SKILL.md that drives the CLI from Claude Code. Install the binary first (see Install above), then either drop SKILL.md into your project's .claude/skills/miro-cli/ directory or load it via your skills-installer of choice. The skill verifies the binary is on $PATH before running any command.

For Miro MCP tools (board operations, stickies, frames, diagrams), use the separate miro-mcp-server project instead. This CLI is shell + skill only.

Configuration

miro-cli reads credentials from two sources, in precedence order:

  1. --token <value> on any command
  2. MIRO_ACCESS_TOKEN environment variable

A config file is not currently supported.

Check what the CLI resolved without touching a board:

miro-cli auth status            # is a token configured, and from where?
miro-cli auth status --verify   # also confirm the token is currently valid
miro-cli auth status --json     # machine-readable {token_present, source, verified, status}

auth status exits 0 when a token is present (and, with --verify, valid), 10 when none is configured, and 4 when --verify is rejected by the API. Print the installed build with miro-cli --version or miro-cli version --json.

Troubleshooting

Authentication errors (exit code 4)

Not found errors (exit code 3)

  • Check the resource ID is correct
  • Run the list verb under the relevant group to see available items

Further reading

Miro Developer Platform background, in case this is your first time touching the REST API:

About

Single-binary CLI that wraps the Miro REST API as shell commands, with a local SQLite mirror for offline search.

Topics

Resources

License

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages