This guide covers creating Claude Code plugins for Miro integration in this repository.
Scope notice. This repo deliberately ships skills + MCP only. We do not accept slash commands, agents, hooks, scripts, or template files in source plugins. See CONTRIBUTING → Scope and Conventions for the rationale and the converter contract that enforces it.
Claude Code as a platform supports a much broader plugin model (commands, agents, hooks, etc.). For a guide to those features, see Anthropic's plugin documentation. This guide intentionally covers only the surface this repo uses.
A Miro plugin in this repository is a directory containing:
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)
Adding commands/, agents/, hooks/, scripts/, or templates/ produces no output — the converter under validation/src/converters/ ignores those paths.
The plugin.json manifest is required. Located at .claude-plugin/plugin.json:
{
"name": "miro",
"version": "x.y.z",
"description": "What this plugin does",
"author": {
"name": "Miro",
"email": "support@miro.com"
},
"homepage": "https://github.com/miroapp/miro-ai",
"repository": "https://github.com/miroapp/miro-ai",
"license": "MIT",
"keywords": ["miro", "mcp", "skills"]
}name— plugin identifier (lowercase, hyphens). Must match the plugin directory name.version— semantic version. Bump on every meaningful change to source skills or MCP config.description— human-readable description. Surfaces in marketplaces.
author,homepage,repository,license,keywords— propagated to all generated targets.
Skills are the only behavioral primitive this repo ships. They auto-activate from natural language using their description field, replacing what would otherwise be slash commands.
skills/
└── skill-name/
├── SKILL.md # Main skill file (required)
└── references/ # Supporting docs (optional)
├── tools.md
└── examples.md
All skill directory names must start with miro- (enforced by validation). The name field in frontmatter must match the directory name. Both rules ensure unique, predictable activation across all targets.
---
name: miro-example
description: Use when the user wants to <do X> on a Miro board (<key triggers>). Be specific so Claude can match user phrasing reliably.
---
# Skill Title
Steps the model should follow when this skill is active.
## Inputs
What to extract from the user's request (board URL, parameters, etc.).
## Workflow
1. Validate inputs (ask the user if missing).
2. Call the relevant Miro MCP tool.
3. Present results in the agreed format.
## Examples
**User input:** `<a phrase a real user would say>`
**Action:** <what the model does>| Field | Required | Description |
|---|---|---|
name |
Yes | Skill identifier — must match the directory name |
description |
Yes | When this skill should activate. Be specific about triggers. |
The description is the most important field — it determines whether a user's request reaches this skill. Patterns that work well:
- Lead with
Use when the user wants to ... - Enumerate concrete trigger phrases / artifact types (frames, sticky notes, flowcharts, …)
- Mention the board URL or other inputs the user is likely to include
See claude-plugins/miro/skills/miro-browse/SKILL.md and miro-diagram/SKILL.md for canonical examples.
Configure MCP servers in .mcp.json at the plugin root:
{
"miro": {
"type": "http",
"url": "https://mcp.miro.com/",
"headers": {
"X-AI-Source": "claude-code-plugin"
}
}
}The converter rewrites X-AI-Source per target (cursor-plugin, gemini-extension, codex-plugin). The URL must stay consistent across all targets — this is checked by bun run validate.
HTTP Server (used here):
{
"miro": {
"type": "http",
"url": "https://mcp.miro.com/",
"headers": {}
}
}Stdio Server (supported by Claude Code, not used in this repo):
{
"server-name": {
"command": "npx",
"args": ["-y", "@example/mcp-server"],
"env": {
"API_KEY": "${API_KEY}"
}
}
}- Edit a skill or
.mcp.jsonunderclaude-plugins/miro/. - Start Claude Code with the plugin loaded:
claude --plugin-dir ./claude-plugins/miro
- Trigger the skill with the kind of phrase a real user would type.
- Run
bun run validateandbun run convertto confirm no regressions.
-
plugin.jsonis valid JSON and matches Claude's plugin schema (bun run validate) - Each
SKILL.mdhasnamematching the directory and adescriptionwith clear triggers -
.mcp.jsonURL matches downstream targets (bun run validate) - No Claude-only tool names (
Write tool,TaskCreate,AskUserQuestion, …) leak into Codex output -
bun run convertis idempotent — second run produces zero diff
"list frames on https://miro.com/app/board/abc=" # → miro-browse
"create a flowchart on <board URL> showing <process>" # → miro-diagram
"extract the specs from https://miro.com/app/board/abc=" # → miro-code-spec
If a skill doesn't activate, the description is the lever — make it more specific and rerun.
- miro plugin — example of MCP integration with bundled skills
- CONTRIBUTING.md — full contributor guide and scope rationale