Use Cases (Diperbarui: 2/6/2026)

Membuat Chrome Extension dengan Claude Code: MV3, Messaging, dan Izin Minimal

Bangun Chrome MV3 extension dengan Claude Code: manifest, service worker, content script, izin, storage, dan testing.

Membuat Chrome Extension dengan Claude Code: MV3, Messaging, dan Izin Minimal

Tentukan batas extension sebelum membuat UI

Chrome extension terlihat kecil, tetapi sebenarnya berisi banyak keputusan produk: manifest.json, Manifest V3, service worker, content script, messaging, permissions, storage, security review, packaging, dan penjelasan untuk Chrome Web Store. Kalau kita hanya meminta Claude Code “buatkan Chrome extension”, hasilnya bisa terlalu besar: <all_urls>, tabs, popup, React, Vite, dan script eksternal bisa ikut muncul tanpa benar-benar dibutuhkan.

Artikel ini memakai contoh yang lebih aman: extension Manifest V3 yang menyorot teks terpilih di https://example.com/* melalui context menu. Kita memakai JavaScript biasa, chrome.storage.local, service worker, content script, dan runtime messages. Kita tidak memakai permission luas, remote code, CDN script, atau host access yang tidak perlu. Dengan begitu, kodenya mudah dicopy, diuji, dan dijelaskan saat review.

Beberapa istilah perlu disamakan. Manifest V3 adalah format manifest utama untuk Chrome extension saat ini. Service worker adalah proses background berbasis event yang bisa berhenti di antara event. Content script adalah script yang masuk ke halaman web dan bisa membaca atau mengubah DOM. Message passing adalah cara mengirim pesan JSON antar-konteks extension. Kalau dalam workflow agentic muncul kata harness, anggap saja sebagai “kerangka kerja” yang membantu agent bekerja dengan aman.

Selalu cek dokumentasi resmi: Chrome Extensions Manifest, About extension service workers, Chrome Content scripts, MDN Content scripts, Chrome Declare permissions, dan MDN permissions. Untuk membuat instruksi ke Claude Code lebih rapi, baca juga tips produktivitas Claude Code.

Tiga use case yang realistis

Use case pertama adalah review dokumentasi internal. Extension bisa dibatasi ke https://docs.example.com/* dan dipakai untuk menyorot istilah produk saat membaca release note. Karena URL pattern sempit, alasan permission mudah dijelaskan.

Use case kedua adalah bantuan untuk support team. Agent support bisa memilih order ID di halaman admin lalu memakai context menu untuk menyorot atau menyalin informasi. Karena ada risiko data pelanggan, minta Claude Code membuat tabel: data apa yang dibaca, disimpan, dikirim, dan sengaja tidak disentuh.

Use case ketiga adalah QA konten dan SEO. Content script bisa memeriksa panjang description, jumlah h2, internal link, dan external link resmi pada halaman draft. Ini cocok digabung dengan pemeriksaan CLI dari pengembangan CLI tool dengan Claude Code dan kebiasaan review dari best practice security Claude Code.

Prompt untuk Claude Code

Prompt harus menyertakan fitur, larangan, dan cara verifikasi. Untuk MV3, tekankan bahwa service worker tidak selalu hidup dan permission harus minimal.

Buat contoh Chrome extension Manifest V3.

Requirements:
- Batasi target URL ke https://example.com/*
- Tambahkan context menu untuk menyorot teks yang dipilih
- Gunakan JavaScript biasa: manifest.json, service-worker.js, content-script.js
- Simpan enabled dan color di chrome.storage.local
- Komunikasi dengan content script memakai runtime messages
- Jangan gunakan <all_urls>, tabs, eval, script CDN eksternal, atau remote code
- Tambahkan Playwright smoke script untuk memastikan extension berhasil dimuat
- Sertakan tabel review permission berdasarkan dokumentasi Chrome

Tujuannya adalah menjaga versi pertama tetap kecil. Kalau Claude Code menambahkan popup, options page, icon, bundler, dan permission luas, minta ia memangkas contoh sebelum behavior direview.

Struktur file dan Manifest

Folder ini bisa langsung dimuat lewat “Load unpacked” di chrome://extensions.

mv3-highlighter/
  manifest.json
  service-worker.js
  content-script.js
  package.json
  playwright-extension-smoke.mjs
{
  "manifest_version": 3,
  "name": "Claude Code MV3 Highlighter",
  "version": "0.1.0",
  "description": "Highlights selected text on example.com from a context menu.",
  "action": {
    "default_title": "MV3 Highlighter"
  },
  "permissions": ["contextMenus", "storage"],
  "background": {
    "service_worker": "service-worker.js",
    "type": "module"
  },
  "content_scripts": [
    {
      "matches": ["https://example.com/*"],
      "js": ["content-script.js"],
      "run_at": "document_idle"
    }
  ]
}
AreaSettingAlasanTidak dipakai
API permissionscontextMenus, storageMenu dan settingtabs, scripting, downloads
Akses halamanhttps://example.com/*Satu domain demo<all_urls>
Host permissionsTidak adaStatic content script cukupHost permission luas
Remote codeTidak adaReview MV3 lebih sederhanaCDN, eval

Service worker

Service worker menerima event dan mengirim pesan ke content script. Karena bisa berhenti di antara event, jangan simpan state penting hanya di memory.

const MENU_ID = "highlight-selection";
const DEFAULT_SETTINGS = {
  enabled: true,
  color: "#fff176"
};

async function readSettings() {
  return chrome.storage.local.get(DEFAULT_SETTINGS);
}

chrome.runtime.onInstalled.addListener(async () => {
  const settings = await readSettings();
  await chrome.storage.local.set(settings);

  chrome.contextMenus.create({
    id: MENU_ID,
    title: "Highlight selected text",
    contexts: ["selection"],
    documentUrlPatterns: ["https://example.com/*"]
  });
});

chrome.contextMenus.onClicked.addListener(async (info, tab) => {
  if (info.menuItemId !== MENU_ID || !tab?.id || !info.selectionText) {
    return;
  }

  const settings = await readSettings();
  await chrome.tabs.sendMessage(tab.id, {
    type: "HIGHLIGHT_SELECTION",
    text: info.selectionText.slice(0, 120),
    enabled: settings.enabled,
    color: settings.color
  });
});

chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
  if (message?.type === "GET_SETTINGS") {
    readSettings().then(sendResponse);
    return true;
  }

  if (message?.type === "SAVE_SETTINGS") {
    const nextSettings = {
      enabled: Boolean(message.enabled),
      color: String(message.color || DEFAULT_SETTINGS.color)
    };
    chrome.storage.local.set(nextSettings).then(() => {
      sendResponse({ ok: true, settings: nextSettings });
    });
    return true;
  }

  return false;
});

Kesalahan umum adalah lupa return true saat sendResponse dipanggil secara async setelah Promise. Channel pesan bisa tertutup terlalu cepat. Cocokkan dengan Chrome Message passing.

Content script

Content script mengubah DOM halaman, tetapi bukan tempat untuk menyimpan secret, token Claude, atau API key. Ia menunggu pesan, menyorot teks, lalu menaruh marker data-* agar Playwright bisa memastikan script sudah masuk.

const HIGHLIGHT_CLASS = "cc-mv3-highlight";

document.documentElement.dataset.ccMv3Highlighter = "ready";

function shouldSkipNode(node) {
  const parent = node.parentElement;
  if (!parent) return true;
  return Boolean(
    parent.closest(
      `script, style, textarea, input, [contenteditable="true"], .${HIGHLIGHT_CLASS}`
    )
  );
}

function clearHighlights() {
  for (const mark of document.querySelectorAll(`.${HIGHLIGHT_CLASS}`)) {
    const text = document.createTextNode(mark.textContent || "");
    mark.replaceWith(text);
    text.parentElement?.normalize();
  }
}

function createMark(text, color) {
  const mark = document.createElement("mark");
  mark.className = HIGHLIGHT_CLASS;
  mark.textContent = text;
  mark.style.backgroundColor = color;
  mark.style.color = "#111";
  mark.style.padding = "0 2px";
  return mark;
}

function highlightText(term, color) {
  const query = term.trim();
  if (!query) return 0;

  clearHighlights();

  const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
    acceptNode(node) {
      if (shouldSkipNode(node)) return NodeFilter.FILTER_REJECT;
      return node.nodeValue.toLowerCase().includes(query.toLowerCase())
        ? NodeFilter.FILTER_ACCEPT
        : NodeFilter.FILTER_SKIP;
    }
  });

  const matches = [];
  while (walker.nextNode()) {
    const node = walker.currentNode;
    const index = node.nodeValue.toLowerCase().indexOf(query.toLowerCase());
    if (index >= 0) matches.push({ node, index });
  }

  for (const { node, index } of matches.reverse()) {
    const selected = node.splitText(index);
    selected.splitText(query.length);
    selected.replaceWith(createMark(selected.nodeValue, color));
  }

  return matches.length;
}

chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
  if (message?.type !== "HIGHLIGHT_SELECTION") {
    return false;
  }

  if (!message.enabled) {
    clearHighlights();
    sendResponse({ ok: true, count: 0 });
    return false;
  }

  const count = highlightText(String(message.text || ""), message.color || "#fff176");
  sendResponse({ ok: true, count });
  return false;
});

Highlighter ini sengaja sederhana. Ia menyorot match pertama di setiap text node dan melewati form, script, style, serta area editable. Untuk produk nyata, tambahkan test untuk multiple matches, Shadow DOM, konten dinamis, dan konflik dengan elemen mark.

Playwright dan pengecekan manual

Dengan launchPersistentContext, Playwright bisa memuat unpacked extension dan mengecek marker dari content script.

{
  "type": "module",
  "scripts": {
    "smoke": "node playwright-extension-smoke.mjs"
  },
  "devDependencies": {
    "playwright": "^1.52.0"
  }
}
import { chromium } from "playwright";
import path from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const extensionPath = path.resolve(__dirname);
const userDataDir = path.resolve(__dirname, ".pw-extension-profile");

const context = await chromium.launchPersistentContext(userDataDir, {
  headless: false,
  args: [
    `--disable-extensions-except=${extensionPath}`,
    `--load-extension=${extensionPath}`
  ]
});

const page = await context.newPage();
await page.goto("https://example.com/");
await page.waitForFunction(() => {
  return document.documentElement.dataset.ccMv3Highlighter === "ready";
});

console.log("Extension content script is loaded on https://example.com/");
await page.waitForTimeout(3000);
await context.close();
npm install
npm run smoke

Checklist manual: buka chrome://extensions, aktifkan Developer mode, pilih Load unpacked, buka https://example.com/, pilih teks pada heading, lalu jalankan “Highlight selected text”. Periksa juga console service worker.

Pitfall, packaging, dan catatan hasil

Pitfall utama adalah menambahkan tabs terlalu cepat, menyimpan secret di content script, mengandalkan memory service worker, mengganti DOM dengan innerHTML, dan lupa menghapus <all_urls> setelah debugging.

Sebelum publish, ikuti Prepare your Extension. Siapkan icon, screenshot, privacy explanation, alasan permission, dan deskripsi singkat. Paketkan hanya file extension:

zip -r mv3-highlighter.zip manifest.json service-worker.js content-script.js
Compress-Archive -Path manifest.json,service-worker.js,content-script.js -DestinationPath mv3-highlighter.zip -Force

Monetisasi tidak hanya datang dari code generation. Nilainya ada pada permission review, batas keamanan, checklist testing, dan penjelasan store yang bisa diulang oleh tim. ClaudeCodeLab dapat membantu lewat training dan konsultasi Claude Code untuk desain MV3, prompt review, dan checklist validasi. Untuk belajar mandiri, lihat product library.

Catatan praktik: saat Masa mencoba pola ini, masalah paling berguna bukan sintaks JavaScript, melainkan penjelasan permission. Dengan <all_urls>, demo cepat jalan, tetapi sulit menjelaskan kenapa semua situs harus diakses. Saat dibatasi ke https://example.com/* dan hanya memakai contextMenus serta storage, warning Chrome lebih kecil dan review Claude Code lebih tajam. Pada MV3, targetnya bukan sekadar “berjalan”, tetapi “berjalan dengan permission yang bisa dijelaskan”.

#Claude Code #browser extension #Chrome extension #Manifest V3 #JavaScript
Gratis

PDF gratis: cheatsheet Claude Code

Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.

Kami menjaga datamu dan tidak mengirim spam.

Masa

Tentang penulis

Masa

Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.