Thank you for your interest in contributing to Miro AI. This guide covers development workflows for each platform.
- Scope and Conventions
- Local Development Setup
- Claude Code Plugins
- Kiro Powers
- Gemini CLI Extensions
- Codex Plugins
- Cursor Plugins
- Copilot Cowork Packages
- General Guidelines
This repository ships skills + MCP only. We deliberately do not accept slash commands, autonomous agents, hooks, shell scripts, or template files in the source Claude plugin or in any generated downstream target.
| Component | Source | Generated targets |
|---|---|---|
skills/*/SKILL.md (+ optional references/) |
claude-plugins/miro/skills/ |
All targets — copied verbatim |
.mcp.json |
claude-plugins/miro/.mcp.json |
All targets — adapted to platform-native MCP shape |
| Manifest | claude-plugins/miro/.claude-plugin/plugin.json |
All targets — synthesized per platform |
README.md |
claude-plugins/miro/README.md |
Cursor, Gemini (verbatim); Codex (generated) |
commands/*.md— slash commands. Use natural-language skill activation instead.agents/*.md— autonomous subagents..claude-plugin/hooks.json,hooks/hooks.json— event hooks.scripts/*.sh— shell scripts (only relevant if hooks or commands need them).templates/— template files.
The converters under validation/src/converters/ only handle skills, MCP, and the manifest. The validators under validation/src/ only enforce that contract. Adding a commands/, agents/, hooks/, scripts/, or templates/ directory to the source plugin will not produce any output — the writers ignore those paths by design.
- Skills auto-activate from natural language using their
descriptionfield. They cover the same surface as slash commands without forcing users to memorize syntax (/miro:review 123 <url>becomesreview PR 123 on <url>). - MCP gives the model direct tool access. Hooks and scripts mostly existed to bridge gaps that MCP now fills.
- One source of truth. Vendors implement these primitives differently (Cursor flattens hook structure, Gemini converts commands to TOML, Codex omits commands entirely). Sticking to skills + MCP gives every target byte-identical content for the same source.
- Smaller blast radius. Less converter code to maintain, fewer cross-platform edge cases, no platform-specific text adaptation — source skills are authored in platform-neutral phrasing and copied verbatim to every target.
Author it as a skill with a clear trigger description. The skill body can prompt for a board URL or other inputs the way a slash command would. See claude-plugins/miro/skills/miro-code-review/SKILL.md for the canonical pattern.
If you have a use case that genuinely cannot be expressed as a skill + MCP, open a discussion before adding new component types — re-introducing commands/agents/hooks is a deliberate scope expansion, not a one-off feature add.
- Git
- Bun — required for validation and converters
- Your AI tool of choice (Claude Code, Cursor, Gemini CLI, Codex, Kiro)
git clone https://github.com/miroapp/miro-ai.git
cd miro-ai
bun installThis installs dependencies and sets up pre-commit hooks automatically via Husky.
Run validation before committing (also runs automatically on pre-commit):
bun run validate| What's Validated | How |
|---|---|
| Claude plugin.json files | claude plugin validate CLI |
| SKILL.md frontmatter | JSON schema (requires description) |
| Skill content | No hardcoded MCP tool names (see below) |
| Kiro POWER.md frontmatter | JSON schema (requires name, displayName, description, keywords) |
| All JSON files | Syntax validation |
| MCP configurations | URL consistency across platforms |
| Codex manifests | JSON schema + marketplace checks |
| Codex generated content | No Claude-only tool references leak through |
| Copilot Cowork package | Manifest schema + identity + skills + connectors |
Individual filters are not exposed as scripts — bun run validate and bun run convert are bulk operations. For ad-hoc debugging, call the CLI directly, e.g. bun validation/src/index.ts --codex-only or bun validation/src/converters/index.ts --cursor --plugin=miro --dry-run.
Skills name tools by role — "the Miro MCP table tool", "the appropriate item-retrieval tool" — not by identifier. A skill that hardcodes diagram_create_mermaid keeps shipping after the tool is renamed or replaced, and the only symptom is an agent calling something that no longer exists. bun run validate fails on any <family>_<verb> token in a SKILL.md or its references/.
The ALLOWED list in validation/src/skill-tool-refs-validator.ts holds parameter and field names only, and should stay that way. Call ordering is not a reason to name a tool: the MCP tools state their own prerequisites in their descriptions, so a skill that repeats them is duplicating the server and will drift from it.
The check is best-effort. It matches against a list of known tool-name families, so a family the server adds later goes unnoticed until someone extends TOOL_FAMILIES. It fails open by design — a passing run means nothing known was hardcoded, not that nothing was.
See Validation Documentation for detailed information on schemas, troubleshooting, and extending validators.
Three driver scripts install or uninstall the miro plugin across Claude Code, Gemini CLI, Codex CLI, and Cursor in one go (always at user scope):
bun run plugins:install:local # install from the working tree (runs `bun run convert` first)
bun run plugins:install:main # install from miroapp/miro-ai @ main
bun run plugins:uninstall # remove from all four targetsEach install is a clean re-install — the script removes any existing copy first, so re-running is safe. Targets whose CLI is missing from PATH are skipped with a warning; real failures abort that target and the command exits non-zero.
For per-tool manual workflows (e.g. claude --plugin-dir ./claude-plugins/miro for live development), see the per-tool sections below.
miro-ai/
├── .agents/
│ └── skills/ # Repo-local helper skills for Codex/agent workflows
├── claude-plugins/ # Claude Code plugins (source of truth)
│ └── miro/ # Core MCP integration with bundled skills
│ └── skills/ # code-explain-on-board, code-review, code-spec
├── gemini-extension.json # Gemini CLI extension manifest at repo root (auto-generated)
├── codex-plugins/ # Codex plugins (auto-generated)
│ └── miro/
├── .agents/plugins/ # Codex repo-local marketplace (auto-generated)
├── copilot-cowork-plugins/ # Copilot Cowork packages (auto-generated)
│ └── miro/
├── skills/ # Agent Skills (auto-generated from claude-plugins)
├── cursor-plugins/ # Cursor plugins (auto-generated from claude-plugins)
├── powers/ # Kiro powers
│ └── code-gen/ # Design-to-code
├── validation/ # Validators, converters, installer
│ └── src/
│ ├── converters/ # bun run convert
│ └── installer/ # bun run plugins:install:*
├── docs/ # Documentation
│ ├── claude-code/ # Plugin docs
│ ├── kiro/ # Power docs
│ ├── gemini-cli/ # Extension docs
│ └── mcp/ # MCP reference
└── README.md
Repo-local helper skills live in .agents/skills/. .claude/skills is kept as a compatibility symlink for Claude-oriented tooling.
Option 1: Using --plugin-dir (Recommended for development)
Start Claude Code with your plugin directory loaded directly:
claude --plugin-dir ./claude-plugins/miroThis approach:
- Loads the plugin from your local directory
- Picks up changes when you restart Claude Code
- Doesn't require installing/uninstalling the plugin
Option 2: Local installation with /plugin add
# Install from local directory
/plugin add ./claude-plugins/miro
# Uninstall when done testing
/plugin uninstall miro-
Start Claude Code with your plugin:
claude --plugin-dir ./claude-plugins/miro
-
Verify the plugin loaded:
/plugin listYou should see your plugin in the list.
-
Test skill activation: Ask a question that should trigger the skill:
"create a flowchart on https://miro.com/app/board/test= for the user login flow" "list frames on https://miro.com/app/board/test="The skill matching the request should activate without any
/invocation.
If you're modifying .claude-plugin/marketplace.json:
-
Add local marketplace:
/plugin marketplace add /path/to/miro-ai
-
Install plugins from it:
/plugin install miro@miro-ai
-
Verify all plugins appear:
/plugin marketplace list
-
Validate JSON:
claude plugin validate .
- Edit files in
claude-plugins/your-plugin/ - Restart Claude Code to pick up changes
- Test the affected functionality
- Repeat until working
The miro plugin is intentionally minimal — see Scope and Conventions.
plugin-name/
├── .claude-plugin/
│ └── plugin.json # Plugin manifest (required)
├── .mcp.json # MCP server config
├── skills/ # Knowledge skills with auto-activation
│ └── skill-name/
│ ├── SKILL.md
│ └── references/ # (optional)
└── README.md # User-facing readme (optional)
commands/, agents/, hooks/, scripts/, and templates/ are not part of this repo's convention. Adding them produces no output — the converter ignores those directories.
Before submitting a PR, run bun run validate to automatically check:
-
plugin.jsonis valid JSON and matches Claude's plugin schema - Each
SKILL.mdhasnameanddescriptionin frontmatter, andnamematches the directory - All skill directory names start with
miro-(enforced) -
.mcp.jsonis valid JSON and the MCP URL is consistent with downstream targets - No Claude-only tool names (
Write tool,TaskCreate,AskUserQuestion, …) leak into Codex output
Manual verification still needed:
- MCP server is reachable from your environment
- Each skill activates from its triggering phrase
Plugin not loading?
- Check
plugin.jsonsyntax withcat plugin.json | jq . - Ensure you're using the correct path with
--plugin-dir
Skill not activating?
- Make sure
SKILL.mdexists at the skill root andnamematches the directory - Check that the
descriptionfield contains the trigger keywords the user is likely to use - The user phrasing should match the skill's "Use when..." description, not the skill name
MCP tools missing?
- Confirm
.mcp.jsonis valid JSON and the URL is reachable - Re-authenticate if the OAuth token has expired
-
Create or edit power directory:
# Powers live in the powers/ directory cd powers/code-gen
-
Power files:
POWER.md— Steering instructions for the AI (required)mcp.json— MCP server configuration (optional, only needed for MCP tools)
-
Test with Kiro using Local Path:
- In Kiro, open the Powers panel
- Click Add power from Local Path
- Select your power directory (e.g.,
miro-ai/powers/code-gen/) - Test with sample prompts
power-name/
├── POWER.md # Steering instructions (required)
└── mcp.json # MCP configuration (optional)
---
name: "power-name"
displayName: "Human Readable Name"
description: "What this power does"
keywords: ["keyword1", "keyword2", "keyword3"]
---
# Power Name
Steering instructions for the AI.
## Onboarding
Setup and authentication steps.
## Workflow
Steps the AI should follow.Required frontmatter fields: name, displayName, description, keywords
Run bun run validate to automatically check:
-
POWER.mdhas valid YAML frontmatter (name,displayName,description,keywords) -
mcp.jsonis valid JSON
Manual verification still needed:
- MCP server is reachable
- Steering instructions are clear and actionable
Per Gemini CLI's extension model, the repo root is the extension. bun run convert regenerates gemini-extension.json at the root from the source Claude plugin's manifest and .mcp.json. Skills come from the same root-level skills/ directory used by the agent-skills mirror — no per-extension copy.
-
Edit the source Claude plugin:
vim claude-plugins/miro/skills/miro-code-review/SKILL.md
-
Regenerate all targets (bulk):
bun run convert
For ad-hoc debugging of a single target/plugin, call the CLI directly:
bun validation/src/converters/index.ts --gemini --plugin=miro --dry-run
-
Link the repo root for local testing:
gemini extensions link . -
Restart Gemini CLI
-
Test:
- Verify the extension loads in Gemini CLI
- Verify MCP tools are accessible
- Trigger a skill via natural language
miro-ai/ # Repo root = Gemini extension root
├── gemini-extension.json # Manifest with MCP config (auto-generated)
└── skills/ # 3 skills, byte-identical to source
Run bun run validate to automatically check:
- JSON is valid
- MCP server URL is consistent with other platforms
-
X-AI-Sourceheader isgemini-extension
Manual verification still needed:
- Extension loads in Gemini CLI
- MCP tools work end-to-end
- Each skill activates from its triggering phrase
See Agent Skills Overview for user-facing documentation.
Skills are auto-generated from Claude plugin skills as part of the bulk bun run convert pipeline. They live in skills/*/ following the agentskills.io specification.
-
Edit the source Claude plugin skill:
vim claude-plugins/miro/skills/miro-code-review/SKILL.md
-
Regenerate all targets (bulk):
bun run convert
For ad-hoc debugging of just the skills target, call the CLI directly:
bun validation/src/converters/index.ts --skills --dry-run
-
Test locally:
npx skills add ./
All skill directory names under claude-plugins/ must start with miro- (enforced by validation). This ensures unique names when published as Agent Skills.
See Codex Plugins Overview for the generated platform reference.
Plugins are auto-generated from Claude plugins as part of the bulk bun run convert pipeline. The Codex target is intentionally narrow: it generates only codex-plugins/miro/ plus a repo-local marketplace at .agents/plugins/marketplace.json.
-
Edit the source Claude plugin:
vim claude-plugins/miro/skills/miro-code-review/SKILL.md
-
Regenerate all targets (bulk):
bun run convert
For ad-hoc debugging of just the Codex target, call the CLI directly:
bun validation/src/converters/index.ts --codex --dry-run
-
Validate generated output:
bun run validate
-
Test in Codex:
- Open the repository in Codex so it can discover
.agents/plugins/marketplace.json - Install the generated
miroplugin from themiro-aimarketplace - Verify plugin
$skills appear for$miro:miro-code-review - Verify the Codex slash menu still shows only built-in commands
- Open the repository in Codex so it can discover
The generated Codex output in codex-plugins/miro/:
miro/
├── .codex-plugin/plugin.json # Codex manifest
├── skills/ # Converted native skills
├── .mcp.json # MCP config (miro only)
└── README.md # Generated platform README
- Codex plugins do not support a
commandsmanifest component. This repository does not convert Claude commands for Codex. - Codex CLI slash commands are built-ins. Use
$miro:<skill-name>plus the Miro MCP tools. - Only
codex-plugins/miro/.mcp.jsonandcodex-plugins/miro/skills/should exist in generated Codex output.
See Cursor Plugins Overview for user-facing documentation.
Plugins are auto-generated from Claude plugins as part of the bulk bun run convert pipeline. They live in cursor-plugins/*/ with .cursor-plugin/plugin.json, .mcp.json, and skills/ only. Per Scope and Conventions, no commands, agents, or hooks are emitted.
-
Edit the source Claude plugin:
vim claude-plugins/miro/skills/miro-code-review/SKILL.md
-
Regenerate all targets (bulk):
bun run convert
For ad-hoc debugging of just the Cursor target, call the CLI directly:
bun validation/src/converters/index.ts --cursor --plugin=miro --dry-run
-
Copy to local plugins dir and restart Cursor:
cp -r cursor-plugins/miro ~/.cursor/plugins/miro # Restart Cursor (or Cmd+Shift+P → Reload Window)
-
Verify plugins load and test MCP connection.
Note: Local plugin loading is a community workaround, not officially documented. The official install method is via the Cursor Marketplace (
/add-plugin).
Copilot Cowork packages are auto-generated from Claude plugins. Only the miro plugin is converted for this target. The generated package folder lives in copilot-cowork-plugins/miro/ and is committed to the repo. Cowork icons under assets/copilot-cowork/ are packaging assets, not Claude plugin source files.
This section documents the developer workflow for generating, validating, and packaging the Cowork app package. The committed folder under copilot-cowork-plugins/miro/ and the zip created in dist/ are packaging artifacts, not a public end-user installation path.
-
Edit the source Claude plugin:
vim claude-plugins/miro/skills/miro-code-review/SKILL.md
-
Keep the required Cowork icons in the package asset folder:
ls assets/copilot-cowork/miro/color.png assets/copilot-cowork/miro/outline.png
-
Regenerate all targets (bulk):
bun run convert
For ad-hoc debugging of just the Cowork target, call the CLI directly:
bun validation/src/converters/index.ts --copilot-cowork --dry-run
-
Create the local zip archive:
bun run package:copilot-cowork
This writes
dist/miro-copilot-cowork-<version>.zip. Thedist/directory is gitignored, so local archives never show up in commits. -
Validate before committing:
bun run validate
-
After merge to
main, download the generated zip artifact from GitHub Actions. You can also trigger the same packaging flow manually from the Actions UI withRun workflow.
Copilot branding is separate from the source Claude plugin key. For miro, the mapping is fixed and must stay stable across rebuilds. Cowork-specific display text comes from the Cowork config, not from the source Claude plugin manifest.
- source plugin key and output folder:
miro - Copilot display name:
Miro Cowork - Copilot description source:
validation/src/copilot-cowork-config.ts - stable manifest
id:1b72f048-929d-554f-9995-9bc8e90f4c4f - stable manifest
packageName:com.cowork.plugin.miro - stable connector
id:miro - stable connector
authorization.referenceId:miro-miro-auth
The converter always writes the Copilot brand as Miro Cowork, but it must not regenerate new IDs when copilot-cowork-plugins/miro/manifest.json already exists. If the generated manifest is missing, the converter bootstraps the same canonical IDs from the fixed Copilot mapping above.
The generated package artifacts include:
manifest.jsoncolor.pngoutline.pngskills/- a local distributable zip via
bun run package:copilot-cowork
- Use clear, descriptive names
- Add comments for complex logic
- Follow existing patterns in the codebase
- Update docs when changing functionality
- Include practical examples
- Verify all links resolve correctly
- Search existing issues to avoid duplicates
- Open a new issue with:
- Clear description of the problem
- Steps to reproduce
- Expected vs actual behavior
- Environment details (OS, AI tool, versions)
- Open an issue describing the feature
- Explain the use case and benefits
- Include examples if possible
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Make your changes
- Test thoroughly using the workflows above
- Submit a pull request with:
- Clear description of changes
- Reference to related issues
- Test steps you performed
- Screenshots if UI-related
- Open a discussion
- Join the Miro Developer Community
By contributing, you agree that your contributions will be licensed under the MIT License.