Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

README.md

miro

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).

Enabling and configuration

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.

Rotating or clearing the token

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.

Tools

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.

Architecture

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.

Development

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.

Configuration

Options reference

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.

How to set these

Three supported routes, in the order most people want them:

  1. Interactively. Claude Code prompts for declared options when you enable the plugin. To change them later: /plugin configure miro@<marketplace>.

  2. Headless. Repeat --config for 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 configure masks 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 not claude plugin uninstall to reconfigure either: uninstalling drops this plugin's whole stored pluginConfigs entry, resetting every option in the table above to its default.

  3. By hand, in settings. Add the value under pluginConfigs in 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's enabledPlugins instead 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.

Upstream documentation