Source profileQuality 93/100

samber/cc-skills/skills/chrome-extension/SKILL.md

chrome-extension

Comprehensive guide for building Chrome extensions with Manifest V3. Use this skill whenever the user mentions Chrome extension, browser extension, manifest.json, content script, service worker (in extension context), popup, side panel, chrome.runtime, chrome.tabs, chrome.storage, chrome.scripting, background script, MV3, Manifest V3, or any Chrome extension API. Also trigger when the user wants to inject scripts into web pages, communicate between page and background, bypass CSP from a content

Source repository stars
194
Declared platforms
2
Static risk flags
2
Last source update
2026-08-24
Source checked
2026-08-25

Decision brief

What it does: where it fits

This skill covers everything needed to build, debug, and publish Chrome extensions with MV3. It is organized as a routing document: read this file first to understand the architecture and decision points, then load the relevant reference file for implementation details.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexDeclaredSource recordInstall path and trigger
    Claude CodeDeclaredSource recordInstall path and trigger
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/samber/cc-skills --skill "skills/chrome-extension"
    Safe inspection promptEditorial

    Inspect the Agent Skill "chrome-extension" from https://github.com/samber/cc-skills/blob/5d4470251be7f9959be6da9a3d5d1158f8816eab/skills/chrome-extension/SKILL.md at commit 5d4470251be7f9959be6da9a3d5d1158f8816eab. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Workflow: new extension from scratch

      1. Define the manifest with minimum permissions. Start with activeTab + scripting. → Read references/manifest-v3.md

      Define the manifest with minimum permissions. Start with activeTab + scripting. → Read references/manifest-v3.mdSet up TypeScript and build tooling (or use CRXJS for Vite-based dev). → Read references/typescript-build.mdImplement the service worker with all event listeners at the top level. → Read references/service-worker.md
    2. 02

      Workflow: adding a feature to an existing extension

      1. Identify which context the feature belongs to (see decision tree above). 2. Read the relevant reference file(s) for that context. 3. Check if new permissions are needed. Prefer optionalpermissions for new capabilities. → Read references/permissions.md 4. Update the manifest i…

      Identify which context the feature belongs to (see decision tree above).Read the relevant reference file(s) for that context.Check if new permissions are needed. Prefer optionalpermissions for new capabilities. → Read references/permissions.md
    3. 03

      Reference files

      Read only the reference files relevant to the current task. Each file is self-contained.

      Read only the reference files relevant to the current task. Each file is self-contained.
    4. 04

      Architecture overview

      A Chrome extension has up to 5 execution contexts that communicate via message passing:

      Service worker is ephemeral. It terminates after 30s of inactivity. All state must be persisted to chrome.storage. All event listeners must be registered synchronously at the top level. Never use setTimeout/setInterval…Content scripts run in the page's origin. Network requests from content scripts are subject to the page's CSP and CORS. To bypass, relay through the service worker. → Read references/network-csp.mdMessaging is the backbone. Every cross-context interaction uses chrome.runtime messaging. The 1 bug: forgetting to return true from async message listeners. → Read references/messaging-rpc.md
    5. 05

      Communication flows (labeled channels)

      For detailed flow diagrams (three-layer bridge, cross-extension, storage broadcast) and a per-context breakdown of permissions, limits, and workarounds: → Read references/execution-contexts.md

      For detailed flow diagrams (three-layer bridge, cross-extension, storage broadcast) and a per-context breakdown of permissions, limits, and workarounds: → Read references/execution-contexts.md

    Permission review

    Static risk signals and limitations

    Reads files

    low · line 4

    The documentation asks the agent to read local files, directories, or repositories.

    This skill covers everything needed to build, debug, and publish Chrome extensions with MV3. It is organized as a routing document: read this file first to understand the architecture and decision points, then load the relevant reference fi

    Network access

    medium · line 129

    The documentation includes network, browsing, or remote request actions.

    ### "I need to make an HTTP request to my API"

    Reads files

    low · line 186

    The documentation asks the agent to read local files, directories, or repositories.

    Read the relevant reference file(s) for that context.

    Network access

    medium · line 246

    The documentation includes network, browsing, or remote request actions.

    const res = await fetch(msg.url);

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score93/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars194SourceRepository attention, not individual Skill quality
    Compatibility2 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    samber/cc-skills
    Skill path
    skills/chrome-extension/SKILL.md
    Commit
    5d4470251be7f9959be6da9a3d5d1158f8816eab
    License
    MIT
    Collected
    2026-08-25
    Default branch
    main
    View the original SKILL.md

    Chrome Extension Development (Manifest V3)

    This skill covers everything needed to build, debug, and publish Chrome extensions with MV3. It is organized as a routing document: read this file first to understand the architecture and decision points, then load the relevant reference file for implementation details.

    Reference files

    Read only the reference files relevant to the current task. Each file is self-contained.

    FileWhen to read
    references/manifest-v3.mdSetting up or modifying manifest.json, configuring icons, versioning
    references/service-worker.mdBackground logic, lifecycle, state persistence, alarms, events
    references/content-scripts.mdInjecting code into pages, isolated/main world, dynamic injection, SPA handling, orphaning
    references/messaging-rpc.mdCommunication between any contexts, typed protocols, RPC layer, async handler patterns
    references/ui-surfaces.mdPopup, options page, side panel, context menus, commands, notifications, omnibox, devtools panel
    references/storage.mdchrome.storage (local/sync/session), quotas, reactive patterns, framework hooks
    references/network-csp.mdHTTP requests from content scripts, CSP bypass relay, declarativeNetRequest, offscreen docs, CORS
    references/permissions.mdRequired/optional permissions, host permissions, activeTab, runtime request flow
    references/web-accessible-resources.mdExposing extension files to web pages, security implications
    references/typescript-build.mdTypeScript setup, project structure, build tools comparison, bundling
    references/publishing.mdChrome Web Store submission, review process, rejection reasons, updates, privacy policy
    references/execution-contexts.mdCommunication flow diagrams, per-context capabilities/limits, choosing the right messaging method
    references/debugging-mistakes.mdDevTools for extensions, testing SW termination, common gotchas, error patterns

    Architecture overview

    A Chrome extension has up to 5 execution contexts that communicate via message passing:

    ┌──────────────────────────────────────────────────────────┐
    │ Extension Process                                        │
    │  ┌─────────────────┐  ┌───────┐  ┌─────────┐  ┌──────┐ │
    │  │ Service Worker   │  │ Popup │  │ Options │  │ Side │ │
    │  │ (background)     │  │       │  │  Page   │  │Panel │ │
    │  │ - No DOM         │  │ Full  │  │  Full   │  │ Full │ │
    │  │ - Ephemeral      │  │ DOM   │  │  DOM    │  │ DOM  │ │
    │  │ - All chrome.*   │  │ All   │  │  All    │  │ All  │ │
    │  │   APIs           │  │ APIs  │  │  APIs   │  │ APIs │ │
    │  └────────┬─────────┘  └───┬───┘  └────┬────┘  └──┬───┘ │
    │           │ chrome.runtime.sendMessage / connect   │     │
    └───────────┼────────────────┼───────────┼──────────┼──────┘
                │                │           │          │
        chrome.tabs.sendMessage  │           │          │
                │                │           │          │
    ┌───────────┼────────────────┼───────────┼──────────┼──────┐
    │ Web Page  ▼                                              │
    │  ┌──────────────────┐    ┌──────────────────┐            │
    │  │ Content Script    │    │ Main World Script │            │
    │  │ (isolated world)  │◄──►│ (page context)    │            │
    │  │ - Shared DOM      │    │ - Shared DOM      │            │
    │  │ - Own JS scope    │    │ - Page JS scope   │            │
    │  │ - chrome.runtime  │    │ - No chrome.* API │            │
    │  │ - chrome.storage  │    │ - Full page access│            │
    │  │ - Subject to CSP  │    │ - Subject to CSP  │            │
    │  │   (network only)  │    │   (fully)         │            │
    │  └──────────────────┘    └──────────────────┘            │
    │           ▲ window.postMessage                           │
    │           │ (through shared DOM)                         │
    └──────────────────────────────────────────────────────────┘
    

    Communication flows (labeled channels)

    ┌───────────────────────────────────────────────────────────────────────────┐
    │ Extension Process                                                         │
    │                                                                           │
    │  ┌─────────────────┐  chrome.runtime   ┌───────┐  ┌─────────┐  ┌──────┐ │
    │  │ Service Worker   │◄─.sendMessage()──│ Popup │  │ Options │  │ Side │ │
    │  │ (background)     │◄─.connect()──────│       │  │  Page   │  │Panel │ │
    │  │                  │                  └───────┘  └─────────┘  └──────┘ │
    │  │ - No DOM         │  ┌────────────────────────────────────────────┐   │
    │  │ - Ephemeral 30s  │  │ SW cannot push to these pages.             │   │
    │  │ - All chrome.*   │  │ Use: ports (.connect) or storage.onChanged │   │
    │  └────────┬─────────┘  └────────────────────────────────────────────┘   │
    │           │                                                              │
    │  chrome.storage.onChanged ◄── fires across ALL contexts simultaneously  │
    │                                                                           │
    └───────────┼──────────────────────────────────────────────────────────────┘
                │ chrome.tabs.sendMessage(tabId, ...) [SW must know tabId]
                │
    ┌───────────┼──────────────────────────────────────────────────────────────┐
    │ Web Page  ▼                                                              │
    │  ┌──────────────────┐  window.postMessage  ┌──────────────────┐         │
    │  │ Content Script    │◄───────────────────►│ Main World Script │         │
    │  │ (isolated world)  │  Custom DOM events  │ (page context)    │         │
    │  │                   │                     │                   │         │
    │  │ chrome.runtime ───┼── to/from SW        │ No chrome.* APIs  │         │
    │  │ chrome.storage    │                     │ Full page JS      │         │
    │  │ Shared DOM        │                     │ Shared DOM        │         │
    │  │ Page CSP (network)│                     │ Page CSP (full)   │         │
    │  └──────────────────┘                     └──────────────────┘         │
    └──────────────────────────────────────────────────────────────────────────┘
    

    For detailed flow diagrams (three-layer bridge, cross-extension, storage broadcast) and a per-context breakdown of permissions, limits, and workarounds: → Read references/execution-contexts.md

    Communication methods at a glance

    MethodDirectionBest for
    chrome.runtime.sendMessageAny ext context → SWOne-shot request/response (90% of cases)
    chrome.tabs.sendMessageSW → content script (by tabId)Pushing data to a specific tab
    chrome.runtime.connect (Port)BidirectionalStreaming, progress, SW ↔ popup
    window.postMessageBetween worlds on same pagePage JS ↔ content script bridge
    chrome.storage.onChangedBroadcast to all contextsSettings sync, no messaging needed

    → Full matrix with limits and edge cases: references/execution-contexts.md → Implementation patterns, typed protocols, RPC layer: references/messaging-rpc.md

    Key architectural rules

    1. Service worker is ephemeral. It terminates after 30s of inactivity. All state must be persisted to chrome.storage. All event listeners must be registered synchronously at the top level. Never use setTimeout/setInterval for anything beyond a few seconds. → Read references/service-worker.md

    2. Content scripts run in the page's origin. Network requests from content scripts are subject to the page's CSP and CORS. To bypass, relay through the service worker. → Read references/network-csp.md

    3. Messaging is the backbone. Every cross-context interaction uses chrome.runtime messaging. The #1 bug: forgetting to return true from async message listeners. → Read references/messaging-rpc.md

    4. Permissions determine CWS review speed. Broad host_permissions trigger manual review (weeks). activeTab + optional permissions = fast automated review. → Read references/permissions.md

    5. Popup is destroyed on blur. Side panel persists. Choose based on interaction duration. → Read references/ui-surfaces.md

    Decision tree: which context handles what?

    "I need to run code when the user visits a page"

    → Content script. Static (manifest) for known URL patterns, dynamic (chrome.scripting) for user-triggered injection. Default to isolated world unless you need page JS access. → Read references/content-scripts.md

    "I need to make an HTTP request to my API"

    • From popup/options/side panel: direct fetch() works (extension origin, no CSP issues)
    • From content script on a page with restrictive CSP: relay through service worker
    • From service worker: direct fetch() works (requires host_permissions for the target domain) → Read references/network-csp.md

    "I need to store user settings"

    • Settings that sync across devices: chrome.storage.sync (100KB limit)
    • Large data or caches: chrome.storage.local (10MB, or unlimited with permission)
    • Ephemeral state surviving SW restarts: chrome.storage.session → Read references/storage.md

    "I need to modify HTTP headers or block requests"

    → declarativeNetRequest (NOT webRequest, which lost blocking in MV3) → Read references/network-csp.md

    "I need the page's JavaScript to talk to my extension"

    → Three-layer bridge: page (window.postMessage) → content script → service worker → Read references/messaging-rpc.md

    "I need to understand what each context can and cannot do"

    → Read references/execution-contexts.md — per-context cards listing chrome.* access, DOM, network, storage, lifetime, hard limits, and practical workarounds.

    "I need periodic background tasks"

    → chrome.alarms (minimum 30s interval). NOT setTimeout. → Read references/service-worker.md

    "I need DOM APIs in the background" (DOMParser, Canvas, Audio)

    → Offscreen document. One per extension, only chrome.runtime available. → Read references/network-csp.md

    "I need to authenticate with OAuth"

    → chrome.identity.launchWebAuthFlow() or chrome.identity.getAuthToken() (Google only) → Read references/service-worker.md (identity section)

    Workflow: new extension from scratch

    1. Define the manifest with minimum permissions. Start with activeTab + scripting. → Read references/manifest-v3.md

    2. Set up TypeScript and build tooling (or use CRXJS for Vite-based dev). → Read references/typescript-build.md

    3. Implement the service worker with all event listeners at the top level. → Read references/service-worker.md

    4. Add content scripts if you need page interaction. → Read references/content-scripts.md

    5. Build UI surfaces (popup, options, side panel) as needed. → Read references/ui-surfaces.md

    6. Wire up messaging between all contexts. → Read references/messaging-rpc.md

    7. Test with DevTools, specifically test service worker termination. → Read references/debugging-mistakes.md

    8. Publish to Chrome Web Store. → Read references/publishing.md

    Workflow: adding a feature to an existing extension

    1. Identify which context the feature belongs to (see decision tree above).
    2. Read the relevant reference file(s) for that context.
    3. Check if new permissions are needed. Prefer optional_permissions for new capabilities. → Read references/permissions.md
    4. Update the manifest if adding new content scripts, UI surfaces, or permissions.
    5. Handle extension updates gracefully (content script orphaning). → Read references/content-scripts.md (orphaning section)

    Minimal manifest.json template

    {
      "manifest_version": 3,
      "name": "My Extension",
      "version": "1.0.0",
      "description": "What it does in one sentence",
      "permissions": ["storage", "activeTab", "scripting"],
      "action": {
        "default_popup": "popup.html",
        "default_icon": {
          "16": "icons/icon16.png",
          "48": "icons/icon48.png",
          "128": "icons/icon128.png"
        }
      },
      "background": {
        "service_worker": "background.js",
        "type": "module"
      },
      "icons": {
        "16": "icons/icon16.png",
        "48": "icons/icon48.png",
        "128": "icons/icon128.png"
      }
    }
    

    → For the full manifest reference with all fields: references/manifest-v3.md

    Code patterns quick reference

    Async message handler (the safe pattern)

    // Wrap async handlers to avoid the return-true trap
    function asyncHandler(
      fn: (msg: any, sender: chrome.runtime.MessageSender) => Promise<any>,
    ) {
      return (
        message: any,
        sender: chrome.runtime.MessageSender,
        sendResponse: (r: any) => void,
      ) => {
        fn(message, sender)
          .then(sendResponse)
          .catch((e) => sendResponse({ __error: true, message: e.message }));
        return true; // literal true, not Promise<true>
      };
    }
    
    chrome.runtime.onMessage.addListener(
      asyncHandler(async (msg, sender) => {
        if (msg.type === "FETCH") {
          const res = await fetch(msg.url);
          return { ok: res.ok, data: await res.text() };
        }
      }),
    );
    

    CSP bypass relay (content script → service worker → API)

    // content-script.ts
    async function apiCall(endpoint: string, options?: RequestInit) {
      return chrome.runtime.sendMessage({ type: "API_RELAY", endpoint, options });
    }
    
    // background.ts
    const ALLOWED_ENDPOINTS = ["https://api.example.com"];
    chrome.runtime.onMessage.addListener(
      asyncHandler(async (msg) => {
        if (msg.type !== "API_RELAY") return;
        if (!ALLOWED_ENDPOINTS.some((e) => msg.endpoint.startsWith(e))) {
          throw new Error("Blocked endpoint");
        }
        const res = await fetch(msg.endpoint, msg.options);
        return { ok: res.ok, status: res.status, data: await res.text() };
      }),
    );
    

    Persist state across SW restarts

    // Use chrome.storage.session for ephemeral state
    chrome.storage.session.setAccessLevel({
      accessLevel: "TRUSTED_AND_UNTRUSTED_CONTEXTS",
    });
    
    async function getState<T>(key: string, fallback: T): Promise<T> {
      const result = await chrome.storage.session.get(key);
      return result[key] ?? fallback;
    }
    async function setState<T>(key: string, value: T): Promise<void> {
      await chrome.storage.session.set({ [key]: value });
    }
    

    Orphaned content script detection

    function isExtensionContextValid(): boolean {
      try {
        return !!chrome.runtime?.id;
      } catch {
        return false;
      }
    }
    
    // Before any chrome.runtime call
    if (!isExtensionContextValid()) {
      showRefreshBanner();
      return;
    }
    

    What NOT to do

    • Do NOT use eval(), new Function(), or load remote scripts. MV3 forbids it.
    • Do NOT use setTimeout/setInterval for anything > 5s in service workers.
    • Do NOT register event listeners inside callbacks or async functions.
    • Do NOT use <all_urls> host permission unless absolutely necessary.
    • Do NOT rely on DevTools keeping the service worker alive during testing.
    • Do NOT forget return true in async message listeners.
    • Do NOT use localStorage or sessionStorage in service workers (they don't exist there).
    • Do NOT assume content scripts survive extension updates.
    • Do NOT use webRequest blocking (removed in MV3). Use declarativeNetRequest.
    • Do NOT use chrome.extension.getBackgroundPage() (removed in MV3).

    Frequently asked questions

    What to verify before installation and use

    What does the chrome-extension source document cover?

    This skill covers everything needed to build, debug, and publish Chrome extensions with MV3. It is organized as a routing document: read this file first to understand the architecture and decision points, then load the relevant reference file for implementation details.

    How do I install chrome-extension?

    The source record exposes this install command: npx skills add https://github.com/samber/cc-skills --skill "skills/chrome-extension". Inspect the command and pinned source before running it.

    Which Agent platforms does the source record declare?

    The pinned source record declares support for: codex, claude code.

    Which permission-related actions were detected?

    Static rules flagged read-files, network in the source; the page lists the matching lines and excerpts.

    Alternatives

    Compare before choosing