Chrome-Erweiterungen mit Claude Code entwickeln: MV3, Messaging und minimale Berechtigungen
Chrome-MV3-Erweiterung mit Claude Code bauen: manifest, service worker, content script, Berechtigungen und Tests.
Erst die Grenze definieren, dann die Oberfläche
Eine Chrome-Erweiterung sieht klein aus, ist aber technisch ein eigenes Produkt. Sie braucht manifest.json, Manifest V3, service worker, content script, Messaging, Berechtigungen, Storage, Sicherheitsprüfung, Packaging und eine nachvollziehbare Erklärung für den Chrome Web Store. Wenn Sie Claude Code nur bitten, “eine Chrome-Erweiterung zu bauen”, entsteht schnell ein zu großer Entwurf mit <all_urls>, tabs, popup, React, Vite und externen Skripten.
Dieser Artikel bleibt bewusst klein. Wir bauen eine Manifest-V3-Erweiterung, die auf https://example.com/* markierten Text über das Kontextmenü hervorhebt. Sie nutzt reines JavaScript, chrome.storage.local, einen service worker, ein content script und runtime messages. Weite Berechtigungen, Remote-Code und unnötige Abhängigkeiten bleiben draußen. Dadurch lässt sich der Code kopieren, testen und gegenüber Reviewern erklären.
Die Begriffe sollten klar sein. Manifest V3 ist das aktuelle Manifest-Format für Chrome-Erweiterungen. Der service worker ist ein ereignisgesteuerter Hintergrundprozess, der nicht dauerhaft läuft. Das content script wird in Webseiten eingefügt und kann DOM lesen oder ändern. Message passing ist die JSON-Kommunikation zwischen Erweiterungskontexten. Wenn in Agent-Projekten von harness die Rede ist, meint es hier ein “Gerüst”, auf dem der Agent sicher arbeiten kann.
Nutzen Sie Primärquellen: Chrome Extensions Manifest, About extension service workers, Chrome Content scripts, MDN Content scripts, Chrome Declare permissions und MDN permissions. Für bessere Arbeitsanweisungen an Claude Code hilft zusätzlich Claude Code Produktivitätstipps.
Drei sinnvolle Einsatzfälle
Der erste Einsatzfall ist die Prüfung interner Dokumentation. Eine Erweiterung kann auf https://docs.example.com/* begrenzt werden und Produktbegriffe in Release Notes hervorheben. Weil das URL-Muster eng ist, ist die Berechtigung leicht zu erklären.
Der zweite Einsatzfall ist Support-Unterstützung. Ein Support-Team kann in einem Admin-Tool eine Bestellnummer auswählen und per Kontextmenü markieren oder kopieren. Hier können personenbezogene Daten sichtbar sein. Claude Code sollte deshalb eine Tabelle erzeugen: was gelesen, gespeichert, gesendet und ausdrücklich ignoriert wird.
Der dritte Einsatzfall ist Content- und SEO-QA. Ein content script kann auf einer Entwurfsseite description-Länge, h2-Anzahl, interne Links und offizielle externe Links prüfen. Das ergänzt CLI-Prüfungen aus Claude Code CLI-Tool-Entwicklung und Sicherheitsregeln aus Claude Code Security Best Practices.
Prompt für Claude Code
Der Prompt sollte Funktion, Verbote und Verifikation enthalten. Für MV3 müssen Sie betonen, dass der service worker nicht persistent ist und Berechtigungen minimal bleiben.
Erstelle ein Beispiel für eine Chrome-Erweiterung mit Manifest V3.
Anforderungen:
- Ziel-URL auf https://example.com/* begrenzen
- Kontextmenü hinzufügen, das ausgewählten Text hervorhebt
- Reines JavaScript verwenden: manifest.json, service-worker.js, content-script.js
- enabled und color in chrome.storage.local speichern
- Mit dem content script über runtime messages kommunizieren
- Kein <all_urls>, tabs, eval, externe CDN-Skripte oder Remote-Code
- Playwright-smoke-script hinzufügen, das das Laden der Erweiterung prüft
- Berechtigungstabelle auf Basis der Chrome-Dokumentation erstellen
Die wichtigste Regel lautet: Die erste Version klein halten. Wenn Claude Code popup, options page, Icons, Bundler und breite Host-Berechtigungen ergänzt, lassen Sie den Entwurf zuerst reduzieren.
Struktur und Manifest
Diese Struktur kann direkt über “Load unpacked” in chrome://extensions geladen werden.
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"
}
]
}
| Bereich | Einstellung | Grund | Nicht verwendet |
|---|---|---|---|
| API-Berechtigungen | contextMenus, storage | Menü und Einstellungen | tabs, scripting, downloads |
| Seitenzugriff | https://example.com/* | Eine Demo-Domain | <all_urls> |
| Host permissions | Keine | Statisches content script reicht | Breite Host-Rechte |
| Remote-Code | Keiner | Einfachere MV3- und Store-Prüfung | CDN, eval |
Service worker
Der service worker empfängt Ereignisse und sendet eine Nachricht an das content script. Da er zwischen Ereignissen stoppen kann, müssen Einstellungen aus dem Storage gelesen werden.
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;
});
Ein häufiger Fehler ist fehlendes return true, wenn sendResponse asynchron nach einer Promise ausgeführt wird. Prüfen Sie diesen Teil mit Chrome Message passing.
Content script
Das content script ändert das DOM, speichert aber keine Geheimnisse. Es wartet auf eine Nachricht, markiert Text und setzt ein data-*-Flag, damit Playwright die Injektion erkennen kann.
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;
});
Der Highlighter ist bewusst einfach. Er markiert die erste Fundstelle je Textknoten und überspringt Formulare, Skripte, Styles und editierbare Bereiche. Für Produktion müssen Mehrfachtreffer, Shadow DOM, dynamische Inhalte und vorhandene mark-Elemente getestet werden.
Playwright und manuelle Prüfung
Mit Playwrights launchPersistentContext kann eine unpacked extension geladen werden. Der smoke test prüft, ob das content script wirklich injiziert wurde.
{
"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
Manuell prüfen Sie zusätzlich: chrome://extensions öffnen, Developer mode aktivieren, Ordner über Load unpacked laden, https://example.com/ besuchen, Text auswählen und “Highlight selected text” ausführen. Die service-worker-Konsole sollte keine Fehler zeigen.
Fehler, Packaging und Praxisergebnis
Typische Fehler sind zu frühes tabs, Geheimnisse im content script, Vertrauen in service-worker-Speicher, DOM-Ersetzung mit innerHTML und ein vergessenes <all_urls>. Jeder Punkt erhöht Risiko und Erklärungsaufwand.
Vor der Veröffentlichung hilft Prepare your Extension. Bereiten Sie Icons, Screenshots, Datenschutztext, Berechtigungsbegründung und Kurzbeschreibung vor. Packen Sie nur Erweiterungsdateien:
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
Der wirtschaftliche Wert liegt nicht nur im Code, sondern im wiederholbaren Prozess für Berechtigungen, Sicherheit, Tests und Store-Erklärung. ClaudeCodeLab unterstützt Teams über Claude Code Training und Beratung bei MV3-Design, Review-Prompts und Checklisten. Für Selbststudium eignet sich die Produktbibliothek.
Praxisergebnis: Als Masa dieses Muster ausprobierte, war nicht die JavaScript-Syntax das Problem, sondern die Begründung der Berechtigungen. Mit <all_urls> ist die Demo schnell, aber schwer zu rechtfertigen. Mit https://example.com/*, contextMenus und storage wurde die Chrome-Warnung kleiner und die Claude-Code-Prüfung konkreter. Bei MV3 zählt nicht nur “es läuft”, sondern “es läuft mit erklärbaren Berechtigungen”.
Kostenloses PDF: Claude-Code-Cheatsheet
E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.
Wir schützen Ihre Daten und senden keinen Spam.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude Code Workflow von Obsidian zu CLAUDE.md
Obsidian-Arbeitsnotizen in CLAUDE.md-Betriebsnotizen verwandeln und Kontext nicht ständig neu erklären.
Claude Code Revenue CTA Routing: Artikel zu PDF, Gumroad und Beratung führen
Ein Claude-Code-Ablauf, der Leser nach Absicht zu Gratis-PDF, Gumroad oder Beratung führt.
Claude-Code-Team-Handoff-Regeln: Belege, Berechtigungen, Rollback und Umsatzpfade
Ein praktisches Claude-Code-Handoff für Review-Belege, Berechtigungen, Rollback, Gratis-PDF, Gumroad und Beratung.