A Claude Code plugin that bundles a local Miro MCP server, giving Claude tools to create and manage boards, sticky notes, shapes, frames, connectors, and tags for EventStorming, brainstorming, and diagramming workflows.
This is the marketplace's first plugin to ship its own MCP server. The server is a
single self-contained Node artifact (server/dist/index.min.js) invoked over local stdio, so
enabling the plugin adds the Miro tools with no separate install, no registry token,
and no npx dependency (a bundled node <server> sidesteps the Windows bare-npx
spawn bug, anthropics/claude-code#58510).
The plugin installs disabled (defaultEnabled: false). A bundled MCP server that
connects to an external, credentialed service is opt-in, not on by default. Enable it
with claude plugin enable miro or the /plugin interface, and provide a token:
| Option | Storage | Purpose |
|---|---|---|
miro_api_token |
Claude Code secure credential storage (never settings.json) |
Miro REST API token. Required. The server exits at startup without it. |
Get a token from https://miro.com/app/settings/user-profile/apps. Claude Code prompts
for it at enable time (masked input). Sensitive values use the macOS Keychain, or
~/.claude/.credentials.json on platforms where no supported keychain is available;
the token is substituted into the server's
MIRO_API_TOKEN environment variable at launch.
Run /miro:setup to check enablement and MCP availability or, with explicit confirmation,
perform a minimal read-only API credential check. The setup skill never reads or exposes the
token and never invokes a mutating Miro tool.
Once set, a sensitive userConfig value has no dedicated reconfigure entry in the /plugin
detail view, and the /mcp server menu's "Clear authentication" applies to OAuth-based servers
only. It is not the rotation path for a token supplied through userConfig (the bundled stdio
server receives miro_api_token as its MIRO_API_TOKEN environment variable, never through an
OAuth flow). To change or clear the token at any time, run:
/plugin configure miro@<marketplace>
That reopens the same configuration screen shown at first enable, letting you overwrite or blank the stored token. Prefer it regardless. It masks input, where a token passed on the command line lands in shell history and the process table.
The older claim here, that --config is ignored once the plugin is installed, was never
version-stamped. Headless --config against an already-installed plugin writes a
non-sensitive option; whether that holds for a sensitive option such as
miro_api_token has not been verified, so do not rely on it for a credential. Do not
uninstall to rotate: that drops this plugin's entire stored pluginConfigs entry,
resetting every option in the Options reference table below to its manifest default.
The verified-version record lives in the
plugin-reconfiguration convention.
The server registers Miro operations grouped by concern: boards, sticky notes, frames,
tags, connectors, bulk create, and overlap detection. Read-only tools annotate
readOnlyHint so Claude can parallelize them; mutating tools serialize.
Stdio MCP server (@modelcontextprotocol/sdk)
on Node ≥ 24. Cross-platform, no per-OS path divergence at the stdio boundary. Tool
definitions are thin wrappers over the @mirohq/miro-api
client; the request/response and error-shaping logic lives in server/src/.
The TypeScript in server/src/ is the single source of truth. server/dist/index.min.js is
generated build output: an esbuild single-file bundle of the
source and all runtime dependencies. Plugin install runs no build step, so the bundle
is committed; CI rebuilds it from source with the pinned toolchain and fails on any
drift, so the committed artifact is always exactly what the source produces.
The whole Node project (package.json, the lockfile, src/, dist/, and the tool
configs) lives under server/ rather than at the plugin root. Claude Code runs
npm ci --ignore-scripts inside a consumer's plugin cache whenever the plugin root
holds both a package.json and a supported lockfile, and that install cannot be turned
off; it would materialise this project's devDependencies (the TypeScript, biome, esbuild
and vitest toolchain) on every install even though the bundle needs none of them at
runtime. Keeping the project one level down leaves the plugin root without a lockfile,
so nothing is installed, while CI and Dependabot still pin and rebuild from the same
lockfile. Basis: plugins-reference.md,
"Node.js package dependencies", verified 2026-09-11; recheck when that section changes.
cd plugins/miro/server
npm install
npm run typecheck # tsc --noEmit
npm test # vitest (with coverage + typecheck)
npm run lint # biome check
npm run bundle # regenerate dist/index.min.js from src/
npm run verify-bundle # fail if dist/index.min.js drifts from src/After editing server/src/, run npm run bundle and commit the regenerated
server/dist/index.min.js alongside the source change.
Generated from this plugin's .claude-plugin/plugin.json. Every option Claude Code
will prompt for when the plugin is enabled, with the environment variable each hook
reads it from.
| Option | Type | Default | Environment variable | Description |
|---|---|---|---|---|
miro_api_token |
string required |
(none) | CLAUDE_PLUGIN_OPTION_MIRO_API_TOKEN |
Sensitive: stored in the OS keychain or protected credentials file. Miro REST API token from https://miro.com/app/settings/user-profile/apps. Required, because the bundled MCP server exits at startup without it. Stored by Claude Code in secure credential storage, never settings.json. |
Three supported routes, in the order most people want them:
-
Interactively. Claude Code prompts for declared options when you enable the plugin. To change them later:
/plugin configure miro@<marketplace>. -
Headless. Repeat
--configfor each option. Replace<marketplace>with the marketplace you installed this plugin from:claude plugin install miro@<marketplace> -s <scope> --config miro_api_token=<value>
Route 1 is the rotation path for this plugin, not this one. Every option here is
sensitive, and/plugin configuremasks input. A secret passed on the command line lands in shell history and the process table. Do not rely on this command to rotate a credential; the verified-version record lives in the plugin-reconfiguration convention. Do notclaude plugin uninstallto reconfigure either: uninstalling drops this plugin's whole storedpluginConfigsentry, resetting every option in the table above to its default. -
By hand, in settings. Add the value under
pluginConfigsin your user settings (~/.claude/settings.json):{ "pluginConfigs": { "miro@<marketplace>": { "options": { "miro_api_token": <value> } } } }Plugin option values are read from user,
--settings, and managed settings only, not from a project's.claude/settings.json. To vary behavior per repository, enable or disable the plugin in that project'senabledPluginsinstead of setting an option there.
Do not set the CLAUDE_PLUGIN_OPTION_* variables yourself. They are how Claude Code
hands a configured value to a hook process; the value comes from the routes above.
- User configuration: the
userConfigschema and theCLAUDE_PLUGIN_OPTION_<KEY>export - Plugin install options: the
--configflag's reference entry - Plugins and skills settings:
enabledPlugins,extraKnownMarketplaces,pluginConfigs - Settings files and who they affect: user vs project vs local precedence
- Manage installed plugins: enabling, disabling,
/plugin list