Firefox Add-on Manifest Scaffold/Lint (unofficial)
Say this up front
This is an unofficial helper for manifest.json schema correctness — not a
Mozilla tool, and not a check of Mozilla's Add-on Policies (that's a
different skill in this suite: firefox-addon-policy-check). A manifest that
passes every check here can still be rejected on policy grounds, and facts
about the schema can drift as Firefox evolves — always also run web-ext lint and load the extension via about:debugging before trusting it's
submission-ready.
Two modes
Mode A: scaffolding a new manifest.json
Ask (conversationally, not necessarily all at once) whatever of this you don't already know from context:
- Extension name and one-line description.
- Manifest V2 or V3? Default to recommending V3 unless they have a concrete
reason to want V2 (existing MV2 codebase, a dependency on an MV2-only key).
Explain briefly why if they don't specify: MV3 is where Firefox and every
other browser are investing, but Firefox doesn't implement extension
service workers regardless of version, so
background.scriptsstill works fine under MV3. - What permissions/host access does it actually need? Push back gently on
<all_urls>if they haven't said why they need it — ask what URLs or APIs are actually involved. - Does it need a background script, a toolbar button/popup, an options page?
- Will it collect or transmit any data leaving the browser? If yes, don't
guess at
data_collection_permissionshere — point them at thefirefox-addon-data-taxonomyskill for that specific piece, then fold the result back into the manifest you're building.
Read references/manifest-schema-notes.md for the exact shape of each key
under MV2 vs MV3 (background, action/browser_action, content_security_policy,
web_accessible_resources, permissions/host_permissions,
browser_specific_settings.gecko) and write the manifest.json accordingly.
Set browser_specific_settings.gecko.id — mandatory for MV3 signing, and
worth setting for MV2 too so AMO doesn't hand out a random GUID on first
signing.
After writing it, run the linter on your own output (Mode B) as a sanity check before handing it to the user — cheap insurance against a copy-paste mistake.
Mode B: linting an existing manifest.json
python3 <skill_dir>/scripts/lint_manifest.py <path-to-manifest-or-extension-root> --json
This checks things like: mandatory keys present; default_locale/_locales
pairing; content_security_policy and web_accessible_resources shaped
correctly for the declared manifest_version (these are two of the most
common "why won't Firefox load my extension" bugs when porting between MV2
and MV3); action vs browser_action matching the manifest version;
background.persistent: true under MV3 (hard error); gecko.id format and
presence; data_collection_permissions key names being valid taxonomy
values (not their business logic — that's firefox-addon-data-taxonomy).
Findings come with a severity: error (Firefox will refuse to load this or
AMO's validator will reject it), warn (works, but likely friction), info.
Report all three, but lead with the errors — those are the ones actually
blocking the user, not just stylistic.
If the user is migrating MV2 → MV3, run the linter, then walk through each
error explaining the MV2 shape they have versus the MV3 shape they need,
using references/manifest-schema-notes.md as the source of truth for both.
When you're not sure
Firefox's actual behavior is the real spec; MDN and this reference file are both just documentation of it, and documentation can lag behind reality or be wrong. If a finding here contradicts what the user is actually observing in Firefox, trust Firefox and say so rather than insisting the lint is right.