miro-board — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited miro-board (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Generates Miro board content in two modes:
Supports the following analyst board types:
| Board type | Use case |
|---|---|
| User story map | Epics across top, stories in columns below, personas as swim lanes |
| Process flow | AS-IS or TO-BE swim-lane diagrams |
| Stakeholder map | Influence × interest grid |
| Gap analysis | Current state / gap / target state columns |
| Impact/effort matrix | 2×2 prioritisation quadrant |
| Retrospective | Start / Stop / Continue (or 4Ls, Mad-Sad-Glad, etc.) |
| Affinity map | Clustered sticky notes from research data |
write-user-story, stakeholder-map) and wants it on a board.requests installed (uv pip install requests).Ask the user (if not stated):
API mode — use when:
Prompt mode — use when:
Follow the board-type instructions below, then produce the output using the format for the chosen mode.
"""
Miro board creator — [Board type]: [Title]
Requirements: pip install requests
Set MIRO_TOKEN env variable before running.
"""
import os
import requests
TOKEN = os.environ["MIRO_TOKEN"]
HEADERS = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
BASE_URL = "https://api.miro.com/v2"
def create_board(name: str) -> str:
"""Create a new Miro board and return its ID."""
response = requests.post(
f"{BASE_URL}/boards",
headers=HEADERS,
json={"name": name, "policy": {"permissionsPolicy": {"collaborationToolsStartAccess": "all_editors"}}},
)
response.raise_for_status()
board_id = response.json()["id"]
print(f"Board created: {response.json()['viewLink']}")
return board_id
def add_sticky(board_id: str, content: str, x: float, y: float, color: str = "yellow") -> str:
"""Add a sticky note to the board."""
response = requests.post(
f"{BASE_URL}/boards/{board_id}/sticky_notes",
headers=HEADERS,
json={
"data": {"content": content, "shape": "square"},
"style": {"fillColor": color},
"position": {"x": x, "y": y, "origin": "center"},
},
)
response.raise_for_status()
return response.json()["id"]
def add_frame(board_id: str, title: str, x: float, y: float, width: float, height: float) -> str:
"""Add a frame (container) to the board."""
response = requests.post(
f"{BASE_URL}/boards/{board_id}/frames",
headers=HEADERS,
json={
"data": {"title": title, "format": "custom", "type": "freeform"},
"position": {"x": x, "y": y, "origin": "center"},
"geometry": {"width": width, "height": height},
},
)
response.raise_for_status()
return response.json()["id"]
def add_text(board_id: str, content: str, x: float, y: float, font_size: int = 14) -> str:
"""Add a text label to the board."""
response = requests.post(
f"{BASE_URL}/boards/{board_id}/texts",
headers=HEADERS,
json={
"data": {"content": content},
"style": {"fontSize": str(font_size), "textAlign": "center"},
"position": {"x": x, "y": y, "origin": "center"},
},
)
response.raise_for_status()
return response.json()["id"]
def add_connector(board_id: str, start_id: str, end_id: str) -> str:
"""Add a connector between two items."""
response = requests.post(
f"{BASE_URL}/boards/{board_id}/connectors",
headers=HEADERS,
json={
"startItem": {"id": start_id},
"endItem": {"id": end_id},
"style": {"strokeColor": "#333333", "strokeWidth": "2"},
},
)
response.raise_for_status()
return response.json()["id"]
# ── Board content ──────────────────────────────────────────────────────────────
def build_board():
board_id = create_board("[Board title]")
# [Board-type-specific creation code goes here]
# Example: add_frame, add_sticky, add_text, add_connector calls
# with x/y coordinates laid out on a grid
print("Done.")
if __name__ == "__main__":
build_board()The skill fills in `build_board()` with board-type-specific calls using the content provided by the user.
## Miro AI prompt: [Board type] — [Title]
> Paste this prompt into Miro AI (board → AI assistant → "Generate board").
---
Create a [board type] Miro board titled "[Title]".
[Board-type-specific layout description]
Content to include:
[Structured list of items, epics, stakeholders, steps, etc.]
Formatting:
- Use [colour] sticky notes for [category].
- Use [colour] sticky notes for [category].
- Group related items in labelled frames.
- Add connectors between [items] to show [relationship].Input: User provides an epic with 6 stories across 2 personas. Expected output (API mode): Python script that creates a board, adds the epic as a header frame, adds 6 sticky notes in columns beneath it, adds 2 persona label texts on the left margin, and draws a release line after story 3.
Input: "Create a Miro stakeholder map for these 8 people: [list with influence/interest ratings]." Expected output (prompt mode): A Miro AI prompt that describes the 2×2 grid, places each person in the correct quadrant, and colour-codes by team.
Input: "I have a gap analysis output from the gap-analysis skill. Put it on a Miro board." Expected output: Three-column board (Current State / Gap / Target State) with one row per topic, sticky notes colour-coded by priority, and a legend frame.
"yellow", "light_yellow", "orange", "light_green", "cyan", "light_pink", "violet", "red", "light_blue", "blue", "dark_blue", "black", "gray", "dark_gray", "white".permissionsPolicy in create_board() if the board is sensitive.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.