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.
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"
}
]
}
| Area | Setting | Alasan | Tidak dipakai |
|---|---|---|---|
| API permissions | contextMenus, storage | Menu dan setting | tabs, scripting, downloads |
| Akses halaman | https://example.com/* | Satu domain demo | <all_urls> |
| Host permissions | Tidak ada | Static content script cukup | Host permission luas |
| Remote code | Tidak ada | Review MV3 lebih sederhana | CDN, 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”.
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.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Workflow Obsidian ke CLAUDE.md untuk Claude Code
Ubah catatan kerja Obsidian menjadi operating note CLAUDE.md agar konteks tidak dijelaskan ulang.
Claude Code Revenue CTA Routing: dari artikel ke PDF, Gumroad, dan konsultasi
Workflow Claude Code untuk mengarahkan pembaca ke PDF gratis, Gumroad, atau konsultasi sesuai intent.
Aturan handoff tim Claude Code: bukti review, permission, rollback, dan jalur revenue
Format handoff Claude Code untuk tim: bukti, permission rule, rollback, PDF gratis, Gumroad, dan konsultasi.