Skip to main content

miro-board-python

Python CLI and library for reading, writing, and updating a Miro board through the Miro REST API v2 using an access token.

Setup

Create and activate a virtual environment at .venv:

python3 -m venv .venv
source .venv/bin/activate        # macOS/Linux
# Windows (PowerShell): .venv\Scripts\activate

Install the dependencies, then set up your environment file:

pip install -r requirements.txt
cp .env.example .env
# put your token and board id in .env, then:
set -a && source .env && set +a

Get a token from the Miro REST API section of a board's share menu, or from a Miro developer app. The token needs boards:read and boards:write scopes.

Note: the "Miro REST API" token from a board's Share menu is read-only. It works for read_board.py, but write_board.py and update_board.py will fail with HTTP 405. For write access, create a developer app and use an OAuth 2.0 access token with the boards:read and boards:write scopes.

Board id is the numeric part of the board URL, e.g. for https://miro.com/app/board/uxXXXXXXXXXXXXX/ the id is uxXXXXXXXXXXXXX.

Read

python read_board.py --board-id uxXXXXXXXXXXXXX
python read_board.py --board-id uxXXXXXXXXXXXXX --json items.json
python read_board.py --board-id uxXXXXXXXXXXXXX --item-type card

Write

python write_board.py --board-id uxXXXXXXXXXXXXX sticky "Hello world" --x 100 --y 200
python write_board.py --board-id uxXXXXXXXXXXXXX card --title "Task" --description "Do it"
python write_board.py --board-id uxXXXXXXXXXXXXX text "Notes" --x 600
python write_board.py --board-id uxXXXXXXXXXXXXX shape "Decision" --shape-type rhombus
python write_board.py --board-id uxXXXXXXXXXXXXX frame --title "Section" --width 1000
python write_board.py --board-id uxXXXXXXXXXXXXX image --url https://example.com/pic.png
python write_board.py --board-id uxXXXXXXXXXXXXX document --title "Spec" --url https://example.com/doc
python write_board.py --board-id uxXXXXXXXXXXXXX embed --url https://example.com
python write_board.py --board-id uxXXXXXXXXXXXXX connector --start-item-id <id> --end-item-id <id> --caption "links to"
python write_board.py --board-id uxXXXXXXXXXXXXX tag --title urgent --fill-color red
python write_board.py --board-id uxXXXXXXXXXXXXX --file items.json --dry-run

Batch file (items.json):

[
  {"type": "sticky_note", "content": "Idea", "color": "light_yellow", "x": 0, "y": 0},
  {"type": "card", "title": "Task 1", "description": "Details", "x": 300, "y": 0},
  {"type": "text", "content": "Section header", "x": 600, "y": 0},
  {"type": "shape", "content": "Decision", "shape_type": "rhombus", "x": 900, "y": 0},
  {"type": "frame", "title": "Section", "width": 1000, "height": 700},
  {"type": "image", "url": "https://example.com/pic.png", "width": 400, "height": 300},
  {"type": "connector", "start_item_id": "123", "end_item_id": "456", "caption": "links to"}
]

Supported types in batch files: sticky_note, card, text, shape, frame, image, document, embed, connector, tag.

Update / delete

python update_board.py --board-id uxXXXXXXXXXXXXX set-text <item-id> "New text"
python update_board.py --board-id uxXXXXXXXXXXXXX set-color <item-id> --color light_blue
python update_board.py --board-id uxXXXXXXXXXXXXX resize <item-id> --width 300 --height 200
python update_board.py --board-id uxXXXXXXXXXXXXX move <item-id> --x 500 --y 300
python update_board.py --board-id uxXXXXXXXXXXXXX tag <item-id> <tag-id>
python update_board.py --board-id uxXXXXXXXXXXXXX delete <item-id>
python update_board.py --board-id uxXXXXXXXXXXXXX update --file changes.json --dry-run

Batch file (changes.json):

[
  {"op": "set-text", "item_id": "3456789012345", "content": "Updated"},
  {"op": "set-color", "item_id": "3456789012345", "color": "light_blue", "item_type": "sticky_note"},
  {"op": "resize", "item_id": "3456789012345", "width": 300, "height": 200, "item_type": "shape"},
  {"op": "move", "item_id": "3456789012345", "x": 100, "y": 200},
  {"op": "tag", "item_id": "3456789012345", "tag_id": "7654321098765"},
  {"op": "delete", "item_id": "3456789012345"},
  {"op": "update", "item_id": "3456789012345", "item_type": "shape",
   "data": {"content": "x"}, "style": {"fillColor": "green"}}
]

Item ids come from read_board.py --json items.json. Updates that change an item's content, style, or size need an item_type (sticky_note, card, text, shape, frame, image, document, embed); when omitted the type is looked up automatically.

MCP server

The same library powers an MCP server so MCP clients (e.g. Claude Code) can read and write Miro boards directly.

python server.py

Configure the client in your MCP client config. For Claude Code, add to .claude/settings.json (or your opencode config):

{
  "mcpServers": {
    "miro": {
      "command": "python",
      "args": ["/path/to/miro-poc/server.py"],
      "env": {
        "MIRO_ACCESS_TOKEN": "your_token",
        "MIRO_BOARD_ID": "uxXXXXXXXXXXXXX"
      }
    }
  }
}

The transport defaults to stdio; set MIRO_MCP_TRANSPORT to sse or streamable-http to change it. Board ids fall back to MIRO_BOARD_ID when not passed per call. The server exposes 24 tools: boards, items (create/update/ move/resize/delete), sticky notes, cards, text, shapes, frames, images, documents, embeds, connectors, and tags.

Testing

With the venv active, install the dev dependencies and run the tests:

pip install -r requirements-dev.txt
pytest

Coverage runs by default and must be 100%, otherwise the test run fails. Tests use a fake HTTP layer, so no Miro token or network is required.

Tests also run automatically on every push to GitHub via a CI workflow; pushes that only touch irrelevant files (such as *.md) are skipped.

Library

miro_client.py also works as a library:

from miro_client import MiroClient

client = MiroClient.from_env()
board = client.get_board("uxXXXXXXXXXXXXX")
sticky = client.create_sticky_note(board["id"], "Hello", x=0, y=0)
client.set_sticky_note_text(board["id"], sticky["id"], "Updated")

Supported item helpers: sticky notes, cards, text, shapes, frames, images, documents, embeds, connectors, and tags. Arbitrary item types can be created through client.create_item(...).

Release files for miro-board 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for miro-board 0.1.0
File Size Uploaded
miro_board-0.1.0.tar.gz 24.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for miro-board 0.1.0
File Interpreter ABI Platform
miro_board-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 47.1 kB

Release files / miro_board-0.1.0.tar.gz

Download URL miro_board-0.1.0.tar.gz
Size 24.2 kB
Tags Source
SHA-256 checksum
How to use checksums
85ad4ba9de4d4090b2127b9b37de858a3202550edb8aa667210db845d5fc5ee3
BLAKE2b-256 checksum
How to use checksums
f6184beac11511210e674001d9698e9d742c8d33944556e2f0bfa6c3d660bfa2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 19, 2026.

Transparency log

Release files / miro_board-0.1.0-py3-none-any.whl

Download URL miro_board-0.1.0-py3-none-any.whl
Size 22.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b3abc73aae30a104dba2d744e048833470695a166752855f459c0f466f5154a7
BLAKE2b-256 checksum
How to use checksums
20659c149ce3579b4ef01d2f2457101fb841bb22ec0168e906c321245d00315e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 19, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.1

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page