Three hard lessons from building a Claude Code plugin for MV3 Chrome extensions
Harish Kumar details three concrete failures encountered while building Chrome Extension Builder, a Claude Code plugin for MV3 Chrome extensions, revealing that Claude writes code well but struggles with MV3-specific manifest rules, dependency pinning, and validator gaps.
Score breakdown
The post demonstrates that agentic coding tools for constrained ecosystems like MV3 require deterministic validators, pinned dependencies, and real-environment CI checks — not just better prompts — because the gap between a model's plausible output and a runtime's actual requirements only surfaces at install time.
- 01Claude Code produces reasonable MV3 extension code on the first try, but fails on surrounding constraints like manifest rules, CSP, and Chrome Web Store requirements.
- 02The plugin exposes five slash commands: `/chrome-ext:new`, `/chrome-ext:validate`, `/chrome-ext:add-feature`, `/chrome-ext:publish`, and `/chrome-ext:migrate-mv2`.
- 03Three agents back the commands: an architect with no Bash access, a read-only manifest auditor, and a test runner that can build and lint but cannot edit files.
Harish Kumar's post-mortem on building Chrome Extension Builder — a Claude Code plugin for scaffolding MV3 Chrome extensions — centers on a core observation: Claude Code writes extension code competently, but the real failures happen in the surrounding ecosystem. MV3-specific manifest rules, the permission model, content security policy constraints, and Chrome Web Store review expectations are where things break, and they break late and quietly. The plugin's design response was to catch MV3-specific mistakes deterministically through validators and hooks rather than through cleverer prompts. Its command surface is intentionally small: five slash commands (`/chrome-ext:new`, `/chrome-ext:validate`, `/chrome-ext:add-feature`, `/chrome-ext:publish`, `/chrome-ext:migrate-mv2`) backed by three agents — an architect with no Bash access, a read-only manifest auditor, and a test runner that can build and lint but cannot edit files. Defaults are opinionated: MV3 only, TypeScript, `activeTab` over broad host permissions, strict CSP with no `unsafe-eval`, no inline scripts, no remote scripts. WXT is the default framework but Plasmo, CRXJS, and vanilla MV3 are all supported paths.
Second, WXT `0.20.26` removed the `wxt/sandbox` export, breaking scaffolded imports of `defineBackground` and `defineContentScript` because the dependency was pinned at `^0.20.0`.
Three failures drove the design. First, Claude generates "plausible" manifests that diverge from "valid" MV3 manifests — `unsafe-eval` in `content_security_policy.extension_pages`, MV2 background pages instead of service workers, and over-broad host permissions all look reasonable to a model trained on MV2 examples. Second, WXT `0.20.26` removed the `wxt/sandbox` export, breaking scaffolded imports of `defineBackground` and `defineContentScript` because the dependency was pinned at `^0.20.0`. The fix was to update the import paths and switch to a tilde pin (`~0.20.26`), and the lesson is that a scaffold's value is that it works on the first run — a floating caret silently delegates that promise to an upstream maintainer's versioning discipline. Third, `claude plugin validate` passed two manifests that the runtime loader then rejected — once for an enum key in a `userConfig` field, once for a missing outer `"hooks"` wrapper in `hooks.json`. The resolution was to add a CI job that runs the actual `claude plugin install` against the built plugin, treating real installation as the only authoritative ground truth for correctness.
Key facts
- 01Claude Code produces reasonable MV3 extension code on the first try, but fails on surrounding constraints like manifest rules, CSP, and Chrome Web Store requirements.
- 02The plugin exposes five slash commands: `/chrome-ext:new`, `/chrome-ext:validate`, `/chrome-ext:add-feature`, `/chrome-ext:publish`, and `/chrome-ext:migrate-mv2`.
- 03Three agents back the commands: an architect with no Bash access, a read-only manifest auditor, and a test runner that can build and lint but cannot edit files.
- 04WXT `0.20.26` removed the `wxt/sandbox` export, breaking scaffolds pinned at `^0.20.0`; the fix was updating import paths and switching to a tilde pin (`~0.20.26`).
- 05`claude plugin validate` passed manifests that the runtime loader subsequently rejected in two separate cases, exposing a gap between the CLI validator and the full runtime schema.
- 06The author added a CI job running `claude plugin install` against the built plugin, treating actual installation as the only ground truth for whether a plugin loads.
- 07WXT is the default framework but Plasmo, CRXJS, and vanilla MV3 are all supported, so a break in the default does not affect all users.
Topics
Summary and scoring are generated automatically from the original article. We always link back to the publisher and never republish images or paywalled content. Last processed Jun 13, 2026 · 08:58 UTC. How this works →