Skip to main content
Glama
HabaAndrei

custom-chrome-dev-mcp

by HabaAndrei

Custom Chrome Dev MCP

Ein rein lokaler MCP-Server (Model Context Protocol), der es einem MCP-Client – Claude Code oder allem, was MCP spricht – erlaubt, Ihren echten Chrome-Browser so zu steuern, wie eine Person es tun würde. Keine Telemetrie, keine Drittanbieterdienste, keine Cloud: alles läuft auf Ihrer Maschine hinter einem gemeinsamen Token.

Er stellt 45 Tools für Navigation, Tabs, Perzeption, Interaktion, vertrauenswürdige Eingabe, Beobachtarkeit und Aufnahme bereit.


Woher das stammt

Dieses Projekt ist vom offiziellen Browser-MCP f vom Chrome inspiriert – dem Chrome DevTools MCP-Server des Chrome-DevTools-Teams, der erste die These vertreten hat, dass ein KI-Agent einen Browser über das DevTools-Protocol steuern sollte, nicht über Scraped HTML aus.

Wir replizieren und imitieren diese Idee, wir liefern sie nicht als Produkt aus. Was wir übernomen haben:

  • Die Prämie – den Browser einem Agenten über den "MCP-Teols" auszusetzen.

  • Accessibility-zuerst-Perzeption – dem Modell eine kompakte a11y-Übersicht mit stabilen Elementreferenzen geben, statt ener Wand aus rohem HTML.

  • Das Chrome DevTools Protocol als Eingabanschicht – echte, vertrauenswürdige Ereignisse statt synthetischer, die eine Seite erkennen und ignoreren kann.

Wo sich dieses Projekt bewusst unterscheidet:

Chrome DevTools MCeP

Custom Chrome Dev MCP

Browser

Standardmäßig startet es ein eigenes Chrome mit einem europäischen astron -

Anbindung

Sternitet sich über den DevTools-Protocol-Endpunkt mit dem Browser verbinder –

Erweiterrung, die im Browser **In der Browser erweiterAusweiterde

Chrome-Erweiterung, die im Browser in der Browser** hängt und auf den Tab Ihrer Wahl zeigt

Zuerstes Ziel

Eine Seite debuginen, inspizieren und profilen

Sich Wie ein Mensch zu verhalten, der die Seite nutzt

This last row is the whole point of this repo. Chrome DevTools MCP is a debugging tool that happens to drive a browser; this is an imitation-of-a-person tool that happens to be useful for debugging.

⚠️ Nicht mit Google ve Firma, von Google oderum Chrome-Team empfohlen oder unterstützt. Dies ist eine unabhängige Neuimplementierung, die dazu gedacht ist, von ihrem Design zu lernen und es zu imitieren. Verwenden Sie den offiziellen Server, wenn Sie das unterstützte Produkt möchten.


Related MCP server: monkeysee

Es behält bewusst den Stil eines Menschen

Die meisten Browserautomatisierungen sind trivially erkennbar: synthetische Ereignisse mit isTrusted=false, einem Fokus think, der nie wirklich wandert, Text, der das alles auf einmal in einem Feld erscheint, ein makelloses Automatisationprofil ohne Verlauf. Jedes einzeln von denen ist ein Signal.

Dieses Projekt versucht, diese Signale zu entfernen:

  • Ihr echtes Profil. Die Aktionen in dem Chrome, das Sie bereits verwenden – Ihre Cookies, Anmeldungen, Erweiterungen und Ihren Verlauf. Nothing to fingerprint as „neue Automation".

  • Vertrauenswürdige Eingabe. realClick, realType, press, hover and drag laufen über das DevTools Protocol, damit die Seite Ereignisse mit isTrusted=true erhält – dieselbe Flagge, die eine physische Maus und Tastatur erzeugen.

  • Echter Fokus. Das Klicken zum Fokussieren eines Feldes verschiebt den Fokus wirklich – in der richtgen Reihenfolge –, statt .value hinter dem Rücken der Seite zu setzen.

  • Echte Tastendrücke. press sendet etchte rawKeyDown / char / keyUp-Sequerizen mit korrektem Tas-ten und Modifizieren, nicht nur event ein synthetisches input-Ereignis.

  • Rücklese-Prüfung. fill bestätigt, dass das Feld den Text tatsächlich enthält, sodass der Agent bemerkt, wenn eine Seite die Eingabe stillchweigend ablehnt – wie es ein Mensch bemerken würde.

Das Ziel: Eine Seite sollte sich für den Agenten genau so verhalten, wie für jemanden, der an der Tastatur sitzt.

Es gibt weiterhin die schnellen synthetischen Tools (click, type) – sie sind schneller und funktionieren auf den meisten Websites. Wenn eine Seite sie ignoriert, überliefernden Sie zu den vertrauenswürdigen Äquivalentten.


So funktioniert es

Ein Transport. Der MCP-Client spricht mit Server über stdio; der Server setzt an eine Chrome-Erweiterung über einen lokalen WebSocket, der zu einem kleinen langlebigen Hub-Prozess.

MCP client 1 (Claude) <-stdio-> bin/custom-chrome-dev-mcp.js ─┐
MCP client 2 (Claude) <-stdio-> bin/custom-chrome-dev-mcp.js ─┼─ src/hub.js (127.0.0.1:9876)
MCP client N (Claude) <-stdio-> bin/custom-chrome-dev-mcp.js ─┘              │
                                                                            │ WebSocket
                                                                            ▼
                                                              Chrome extension -> active tab

Warum eine separater Hub-Prozess. Nur ein Prozess kann Port 9876 belegen, aber möglicherweise haben Sie mehrere Claude-Sitzungen offen, und alle möchten den Browser nutzen. Deshalb lebt der Socket in src/hub.js statt innerhalb einer einzelnen Sitzung. Jede Sitzung verbindet sich als role:"mcp" an den Hub, We have extension als role:"extension", und Der Hub bündelt sie. The first session to start spawns the hub entkoppelt, sodass er diese Sitzung überlebt; spätere Sitzungen finden ihn der bereits verhältn zum zuhört.

Innerhalb der Erweiterung gibt es drei Ebenen:

  1. Walker (page/walker.js) – injekt in die ISOLATED-Welt der Seite. Es besitzt die Elementauflösung, die stabile eN-Referenzzuordnung und die schnellen synthetischen DOM-Operationen.

  2. CDP (cdp/) – chrome.debugger für vertrauenswürdiges Eingabe, evaluate im Seitenkontext, Ganzsiten-Screenshots und die Konsole/Netzwerk-Puffer.

  3. Recording (recording/) – CDP-Screencast-Frames, die von einem MediaRecorder in einem Offscreen-Dokument zu .web kodiert werden.

🔒 Die Erweiterung authentifiziert sich beim Hub mit einem gemeinsamen Token (AUTH_TOKEN, identisch in src/config.js und extension/src/config.js). Der Hub verwirft jeden Peer, der einen anderen Wert präsentiert.


Voraussetzungen

Voraussetzung

Prüfen

Node.js

18 or newer (entwickelt unter 22)

node--version

Chrome

Google Chrome orr Chromium, jede aktuelle Version

chrome://version

Ein MCP-Client

Claude Code, oder alles anderes, was MCP über stdio spricht

claude --version

Keine global version installation, kein Build-Schritt, kein Dienst, bei dem man sich anmelden muss. Zwei Laufzeitabhängigkeiten (@modelcontextprotocol/sdk und ws), und alles bleibt auf 127.0.0.1.


Lokale Einrichtung

Vier Schritte, dann ein Prüfdurchlauf. "Budget in fünf Minuten."

1. Klonen und installieren

git clone <your-fork-url> custom-chrome-dev-mcp
cd custom-chrome-dev-mcp
npm install

Vergewissern Sie sich, dass der Baum gesund ist, bevor Sie etwas mit Chrome verbinden – der Offline-Pfad braucht Keinen Browser und dauert weniger als oine Sekunde:

npm test

Sie 22 passed sehen. Wenn das fehlschlägt, beheben Sie es, bevor Sie fortfahren; nichts später wird funktionieren.

2. Erweiterung in Chrome laden

  1. Öffnen Sie chrome://extensions.

  2. Aktivieren Sie Entwicklermodus (Schalter oben rechts).

  3. Klicken auf Entpackte Erweiterung laden und wählen Sie den Ordner extension/ – den Ordner selbst, nicht manifest.json darin.

  4. Custom-chrome-dev-mcp wird in der Liste angezeigt.

⚠️ Laden Sie in eines Chrome-Profil, das Sie tatsächlich brauchen. Chrome hält Erweiterungen pro Profil, eine Erweiterung, die in "Profile 4" loaded wurde, ist für Sensor unsichbber, unter dem Standard läuft („Default“). Wenn Tools später keine Tabs melden, oder der Hub nie extension connected loggt, ist dies das Erste, das Sie prüfen. chrome://version zeigt den aktiven Profile Pfad an.

Die Erweiterungs-ID ist durch den öffentlichen key in extension/manifest.json fixiert, also auf jedem Rechner identisch – nichts, das zwischen Standorten kopier werden muss.

3. MCP-Server bei Ihrem Client registrieren

Use the CLI – substitute the absolute path to your clone (pwd in the project root prints it:

claude mcp add -s user custom-chrome-dev-mcp -- node /ABSOLUTE/PATH/TO/custom-chrome-dev-mcp/bin/custom-chrome-dev-mcp.js
  • -s user registriert den Server für alle Ihre Proni, -s local begrenzt ihn auf dieses.

  • Registrieren Sie bin/custom-chrome-dev-mcp.js – diese Datei ist der Einstiegspunkt. src/server.js einzubinden funktioniert nicht.

  • Der Pfad muss absolut sein. Ein relativer Pfad wird relativ zu dem Verzeichnis aufgelöst, in dem der Client gerade zufällig gestartet wurde.

  • Mit claude mcp list prüfen – Sie möchten ein ✔ Connected daneben sehen.

⚠️ Bearbeiten Sie nicht handsmodes ~/.claude.json. Es ist groß, und ein statt einer falschen Komma macht Claude Code komplett nutzlos. Der obige Befehl bearbeitet sie sicher.

Fügen Sie den Server unter mcpServers hinzu:

{
  "mcpServers": {
    "custom-chrome-dev-mcp": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/custom-chrome-dev-mcp/bin/custom-chrome-dev-mcp.js"]
    }
  }
}

4. MCP-Client neu starten

MCP-Clients wandern die Tools nur einmal, beim Start, auf; ein Server, der mitten ins Sitzung registriert wird, ist bis Neustart unsichtbar. Starten Sie Claude neu, und die 45 Tools erscheinen.

Beim Neustart startet der Client den Server; dieser spawnst src/hub.js, falls nicht bereits etwas auf 127.0.0.1:9876 lauscht.

5. Alle drei Glieder in der Kette verifizieren

Der Stapel ist Client → Server → Hub → Erweiterung → Tab. Führen Sie alle drei Prüfung durch, statt zu raten, welches Glieder unterbrochen ist.

# The hub is up, and the extension found it:
tail -f "$TMPDIR/custom-chrome-dev-mcp-hub.log"
#   [hub] listening on 127.0.0.1:9876
#   [hub] extension connected      <- this line is the handshake succeeding

# Who owns the port (should be src/hub.js from THIS repo):
lsof -nP -iTCP:9876 -sTCP:LISTEN

Dann fragen Sie Ihren Client nach listTabs. Ein JSON-Konstrukt aus Ihren offenen Tabs bedeutet, dass jedes Kettenglied funktioniert. Gleich anschließend geht screenshot – ein PNG landet in ~/Downloads und kommt inline-inline zurück.

Für die eigene Konsole der Erweiterung: chrome://extensionsCustom-chrome-dev-mcpService workerInspect. Dort zeigen Sie extensionsded errors; du erreichen den MCP-Client nie.

6. Gemeinsames Token vor der echten Nutzung ändern

AUTH_TOKEN wird mit einem Standardwert ausgeliefert, der identisch in src/config.js und extension/src/config.js definiert ist. Es ist das einzige Ding, das einem anderen Prozess auf Ihrem Rechner den Sie den Zugriff auf den Browser verhindert. Wählen Sie einen eigenen Wert, ändern Sie ihn in beiden Dateien (ein Offline-Test verlangt, dass sie übereinstimmen), und laden Sie dann die Erweiterung neu.


Nachdem Sie Code geändert haben

Die zwei Teile werden unterschiedlich neu geladen, und ein Fehler hier kostet mehr Zeit als alles andere in diesem Projekt:

Sie geändert

So übergen Sie

Anything under extension/

Klicken Sie in chrome://extensions bei der Erweiterung auf reload ↻. Chrome verwendet bis dahin das zuvor geladene Build.

anything under src/

Starten Sie den MCP-Client neu. Der Serverprozess ist langlebig und hält die alten Tool-Schemas.

src/hub.js

pkill -f src/hub.js – der nächste Werkzeugaufruf startet ihn neu.


Fehlerbehebung

Symptom

Ursache

Behebung

claude mcp list zeigt ✘ Failed to connect

Falscher Pfad oder nicht der bin/-Einstiegspunkt

Neu registrieren mit dem absoluten Pfad zu bin/custom-chrome-dev-mcp.js

Tools fehlen im Client vollständig

Mitten in der Sitzung registriert

MCP-Client neu starten

Hub-Log meldet nie extension connected

Erweiterung nicht geladen, in einem anderen Chrome-Profil geladen oder AUTH_TOKEN unterscheidet sich zwischen den beiden config.js-Dateien

chrome://version prüfen → Profilpfad; sicherstellen, dass beide Tokens übereinstimmen

Ein Tool-Aufruf hängt und läuft dann in einen Timeout

Der Service Worker ist abgestürzt oder eine erweiterungsseitige Ausnahme aufgetreten

Service-Worker-Konsole öffnen; auf Neu laden ↻ klicken

Port 9876 gehört einem unerwarteten Prozess

Ein Hub aus einem anderen Klon dieses Projekts blockiert den Port

lsof -nP -iTCP:9876 -sTCP:LISTEN, dann diese PID beenden

Eine Bearbeitung an extension/ hat „nichts bewirkt"

Chrome führt noch den alten Build aus

Auf Neu laden ↻ klicken

URL is banlisted

BANLIST in extension/src/config.js blockiert diesen Host

Die Liste bearbeiten – sie wird mit Platzhalterwerten ausgeliefert

refusing to act: … does not contain expectUrl

Die expectUrl-Sicherung hat Korrekt ausgelöst

Diese Sicherung entfernen oder auf die echte URL ausrichten

Screenshot-Pfad abgelehnt

Schreibvorgänge sind auf das Erfassungsverzeichnis beschränkt

Einen Dateinamen oder einen Pfad innerhalb dieses Verzeichnisses verwenden

Ein Tool betrifft den falschen Tab

Ein Hintergrund-Tab hat den Fokus gestohlen

Den Arbeits-Tab mit useTab anpinnen


Konfiguration

Beide sind optionsale Umgebungsvariablen, die von src/config.js verarbeitet werden.

Start

Beide sind optionale Umgebungsvariablen, die beim Start von src/config.js gelesen werden.

Variable

Standard

Funktion

CUSTOM_CHROME_DEV_MCP_CAPTURE_DIR

~/Downloads

Das einzige Verzeichnis, in das Screenshots und Aufnahmen geschrieben werden dürften.

CUSTOM_CHROME_DEV_MCP_WS_PORT

9276

Hub-Port. Auch in extension/src/config.js ändern, sonst finden sie sich nicht.

Auch für den Einnsatz zu ändern: AUTH_TOKEN, in config.js und extension/src/config.js identisch definiert. Nimm eigenen Wert – dаs verbreitet, dass ein anderer lokaler Prozess den Browser steuert.


VerfügbareWerkzeuge (45)

Elemente werden auf drei Arten gezielt: selector (CSS), ref (stabile eN-ID from snapshotA11y) oder name (accessibler Name, z. B. eine Button-Beschriftung). „target" bedeutet hier jede dieser drei.

Allgemeine Parameter, die jedes Tool akzeptiert:

  • tabId – quillt auf einen bestimmten Tab statt auf den aktuell aktiven.

  • frameId (aus listFrames) – innerhalb eines bestimmten Frames ausführen, einschließlich Cross-Origin-Iframes, die der oberste Dokument nicht skrippen kann.

  • expectUrl – eine Absicherung: Aktion ablehnen, wenn die Tab-URL diesen Teilstring nicht enthält.

Einen Arbeit switcht für die ganze Sitzung mit useTab anpinnen, damit ein Hintergrund-Tab (z. B. auto-playing-Video, Popup-Benachrichtigung) kann nicht den Focus stehlen und eine Aktion fehlleiten.

##Navigation

Tool

Argument

Beschreibung

navigate

url

Den Tab auf eine URL zeigen (ersetzt die Seite).

newtab

url

URL in neuen Vordergrund-Tab öffnen, aktuelle Seite bleibt.

back / forward

-

Verlauf zurück / vorwärts.

reload

hard?

Neu laden, optional Cache das umgehen.

getUrl / getTitle

-

URL / Title des Tabs (funktioniert auch bei internen Seiten).

waitForLoad

timeout?

Blockieren, bis der Tab fertig geladen ist.

Tab & Frames

Tool

Args

Description

listTabs

-

Jeder offene Tab in allen Fenstern (id, title, url, active, pinned).

activateTab

tabId

Einen Tab und sein Fenster fokussieren.

closeTab

tabId

Tab anhand ID schließen.

useTab

tabId?

Pinnen Sie den Arbeit-Tab ein,damit jedes spätere Tool unabhängig von OS-Focus darauf zielt. tabId kann weggelassen werden, aktuellen Tab ein.

unpinTab

-

Freigabe; Tools gelten wieder active.

listFrames

-

Alle Frames einschließlich Cross-Origin als {frameId, parentFrameId, url, origin}.

Wahrnehmung

Tool

Parameter

Funktion

snapshotA11y

-

Kompakte Barrierefreiheits-Übersicht der sichtbaren interaktiven Elemente als role "name" ref=eN. Dem snapshot bevorzugen. Refs verfallen bei Navigation oder neuem snapshot.

snapshot

-

Roh-HTML des outerHTML von <body>, auf 50 KB beschränkt. Verwenden, wenn exaktes Markup nötig.

getText

target

innerText eines Elements, getrimmt.

getAttribute

target, attr

Ein Attribut, Rückgriff auf die Live-DOM-Property (value, checked, href).

queryAll

selector, limit?

text/href/value/visible für jeden Treffer auf einmal.

viewport

devicePixelRatio, CSS-Viewport, Scroll-Offset – dafür, wie Screenshot-Px→CSS-Px umgerechnet sind.

Interaktion – synthetisch, schnell

Untrusted events dispatched by the walker. Schnell, für die meisten Seiten ausreichend.

Tool

Parameter

Funktion

click

Bubble MouseEvent-Click; fokussiert das Element; .click()-Fallback. Gibt {focused} zurück.

type

target, text

Feld в native- Setter setzen (<input>, <textarea>, **und** contenteditable). Gibt {value}.

fill

–, text, verify?

Fokus + setzen + rüfrucken. Wirft, falls der Text nicht Stickt. Der zuverlässige Texteingefit – von Klick/Tipp bevorzugen.

assert

text?, value?

Text (Substring) und/oder genauen Wert verifizieren, ohne screenshot → {ok, checks} zurück.

scroll

target?, direction?, amount?

Element in den Blickpunkt scrollen, oder Fenster (top/bottom zum Rand).Mark

select

target description

<*select-Option nach Wert(Mark) oder sichtbarem Label wählen.

check

target, checked

Checkbox/이트 setzen- und klicken, falls nicht schon gesetzt.

submit

target

requestSubmit()of owning form – für Formulare without klicker.

waitForSelector

– target oder text, timeout

Poll, bis ein Element oder textos-geht.

Vertraute Eingabe & Emulation – CDP

True events with isTrusted=true. VerbindenCD / – zeigt einblaues yellows "being debugged" –Banner auf dem Tab.

Tool

Args

Description

realClick

target / x,y, button?, clickCount?

Vertrauenswürdiger Klick, einschließlich Rechtsklick und Doppelklick.

realType

target?, text

Vertrauenswürdige Texteingabe; das Ziel wird zuerst fokussiert, falls angegeben.

press

keys, target?

Vertrauenswürdige Tasten und Tastenkombinationen: "Enter", "Tab", "Meta+c", ["ArrowDown","Enter"].

hover

target / x,y

Bewegt die echte Maus über ein Element, um :hover auszulösen (Menüs und Tooltips erscheinen).

drag

from, to

Vertrauenswürdiges Ziehen & Loslassen (Druck-Bewegung-Loslassen).

uploadFile

selector, paths[]

Setzt Dateien auf einem <input type=file>, unter Umgehung des Betriebssystem-Dialogs. Absolute Pfade.

setViewport

width, height, deviceScaleFactor?, mobile?, userAgent?

Emuliert ein Viewport/Gerät für Responsive-Prüfungen.

handleDialog

accept?, promptText?

Hinterlegt eine Antwort für das nächste alert/confirm/prompt. Setzen Sie sie vor der Aktion, den Dialog auslöst.

detach

-

Trennt den Debugger und entfernt das Banner. Beitritt beim nächsten CDP-Aufruf erneut.

Observability – CDP, gepuffert pro Tab

Die Erfassung startet, wenn der Debugger andockt: Laden Sie die Seite also nach dem ersten CDP-Aufruf neu, wenn Sie die Aktivitäten beim Laden sehen möchten.

Tool

Args

Description

getConsole

level?, limit?, clear?

Gepufferte Konsolen-Logs, Warnungen, Fehler und unbehandelte Ausnahmen.

listNetworkRequests

urlContains?, status?, failedOnly?, limit?

Gepufferte Anfragen: Methode, URL, Status, Typ, Zeit.

getNetworkRequest

requestId, includeBody?

Eine Anfrage im Detail; includeBody stellt zusätzlich den (gekürzten) Antworttext bereit.

evaluate

expression

Führt JS im echten Kontext der Seite via CDP aus – umgeht die Content-Script-CSP, eval blockiert. Wartet auf Promises. Nicht auf chrome://-Seiten.

Capture

So landet in einem Erfassungsverzeichnis (standardmäßig ~/Downloads – siehe Konfiguration).

Tool

Args

Description

screenshot

path?, format?, tabId?

Zeigt den sichtbaren Viewport als PNG/JPEG – auf Disk gespeichert und inline geliefert mit {devicePixelRatio, cssViewport}, damit das Modell es in einem Ruff sieht.

fullPageScreenshot

path?, tabId?

Die gesamte scrollbare Seite neben dem Viewport, via CDP.

record

action, path?, tabId?

start / stop / status Tab-Aufnahme → .webm. Voll MCP-gesteuert – kein Klick auf die Toolbar und keine Nutzergeste erforderlich. Zeichnet den Tab auf, nicht den Desktop.

path ist ein Dateiname oder ein Pfad innerhalb des Capture-Verzeichnisses. Fehlende Unterordner werden erstellt; Pfade, die außerhalb des Verzeichnisses auflösen, werden abgelehnt.


Ein erster echter Lauf

Schritt 5 der Einrichtung zeigt die Verbindung. Dieser Schritt zeigt den interessanten Teil – dass eine Seite einen Benutzer erkennt, kein Skript. Punkten Sie Ihren Client auf eine beliebige Seite und fragen Sie ihn nach:

  1. SnapshotA11y – die kompakte Gliederung, mit eN-Referenzen zum Zielen.

  2. realClick {ref:"e3"} – ein vertrauenswürdiger Klick. Der Tab bekommt ein gelbes Banner „being debugged", das ist der CDP-AnSchluss und es ist beabsichtigt, sichtbar zu sein.

  3. evaluate {expression:"'ok'"} – JS im Seiten-Kontext, der um die Content-Script-CSP herum.

  4. screenshot – ein PNG in Ihrem Capture-Verzeichnis und zeigt es inline.

  5. record {action:"start"}record {action:"stop", path:"clip.webm"} – eine .webm des Tabs. Kein Werkzeuge-Klick, keine Benutzergeste nötig; das Werkzeugleisten-Symbol ist absichtlich träge und startet nichts.

  6. detach – zeigt das Banner.

Um den Unterschied zu sehen, den der vertrauenswürdige Weg ausmacht, installieren Sie einen Listener und vergleichen Sie:

// via evaluate
window.__e = []; document.querySelector("button")
  .addEventListener("click", e => window.__e.push(e.isTrusted));

click meldet false; realClick meldet true. Genau dieser Gegensatz ist der Kern des Projekts, und den prüft die Browser-Testspur direkt.


Ausführen der Tests

Die Suite hat zwei Schienen, und genau diese Trennung ist der Punkt.

Offline-Schienen – kein Browser, läuft in CI

npm test        # node test/run.mjs --lane=offline

Läuft in deutlich unter einer Sekunde und braucht nur Node. Er führt einen echten MCP-Handshake gegen src/server.ts (via den In-memory-Transport des SDK) aus und prüft damit die Oberfläche, die der Server tatsächlich verfügbar macht:

  • jedes veröffentlichte Tool hat einen Handler in der Extension, und umgekehrt – genau der Fehler, der zur gespiegelten Architektur verlockt

  • kein Tool-Name wird von zwei Handler-Gruppen beansprucht (sie werden per Spread zusammengeführt, ein Duplikat würde also still verlieren)

  • jedes Tool hat eine echte description und den universellen tabId/frameId/expectUrl-Scope

  • jedes Tool wird durch mindestens einen Test „fahren" – ein Tool ohne Test und CI schlägt fehl, und es braucht keinen Browser

  • die KeepPath-Zulassungsliste verweigert wirklich .., tiefe .., absolute Pfade und Symlink-Escape – getestet gegen den echten Resolver

  • die Sperrliste wird durch Verhalten getestet – sie blockiert das, was sie sagen soll, und blockiert nicht ungewöhnliche Sites

  • der Hub bindet nur Loopback, die Tokens stimmen auf beiden Seiten, das Manifest verbper nicht allzu breite Berechtigungen, das Toolbar-Symbol ist träge, und es ist kein *.krwl commit.

Browser-Schiene – steuert echten Chrome

# 1. Disconnect the MCP client (close Claude Code, or disable this server for the run)
# 2. Free port 9876 - the hub is long-lived and outlives the session that spawned it
pkill -f src/hub.js
# 3. Start the suite; it binds 9876 itself and waits for the extension
npm run test:browser
# 4. Reload the extension in chrome://extensions so it connects to the suite

⚠️ Schritt 41 ist nicht optional. Ein verbundener MCP-Client startet den Hub alle ~1,2 s neu, sobald der er merkt, den Socket weg ist, und nimmt Port 9876 sofort wieder ein – die Suite stirbt dann mit EADDRINUSE. Den Hub zu killen, während der Client noch verbunden ist, hilft nicht – der Client startet einfach einen neuen.

Die Suite stellt einen Fixture-Server und eine Bidare auf, die das gleiche Wire-Protokoll wie echte Hub spricht, so dass ein grüner Lauf den tatsächlichen Nachrichtenvertrag erprobt. Jede Suite spiegelt eine Tool-Gruppe, und jeder Test startet von einer neuen Fixture-Seite aus – kein Test erbt die Mutationen eines anderen.

Am Ende gibt er Tool-Coverage aus und schlägt fehl, wenn eines der 45 Tools nicht benutzt wurde.

Optionen

Befehl

Effekt

npm test

Offline-Schiene allein – das CI-Gate

npm test:browser

Browser-Schiene allein

npm run test:all

beide

npm run test:list

alle Suiten und Tests auflisten ohne Ausführung

node test/run.mjs --grep=fill

nur Tests, deren Suite + Name übereinstimmen

Testlayout

test/
├── run.mjs                    # CLI: lanes, filtering, coverage, reporting
├── lib/
│   ├── runner.js              # suite registry, isolation, timeouts
│   ├── assert.js              # assertions with diagnostic messages
│   ├── wait.js                # eventually() - polling, not fixed sleeps
│   ├── mcp-probe.js           # real in-process MCP handshake
│   ├── bridge.js              # stands in for the hub; tracks tool coverage
│   ├── fixture-server.js      # serves the fixture pages
│   └── page.js                # the browser session + per-test reset
├── fixtures/
│   ├── index.html             # the fixture page (a real file, with __reset())
│   └── frame.html             # child frame, for frameId targeting
└── suites/
    ├── 01-contract.suite.js   # offline
    ├── 02-security.suite.js   # offline
    ├── 10-navigation.suite.js
    ├── 20-tabs.suite.js
    ├── 30-perception.suite.js
    ├── 40-interaction.suite.js
    ├── 50-trusted-input.suite.js
    ├── 60-observability.suite.js
    └── 70-capture.suite.js

Sicheheitsnotizen

Diese Erweiterung kann Ihren angemeldeten Browser steuern. Lesen Sie diesen Abschnitt.

  • Nur Loopback. Der Hub bindet nur an 127.0.0.1 ist also nicht aus dem LAN-Netz erreichbar – nur für Prozesse auf diesem Rechner.

  • Token-Handshake. Ein Gegenstel muss beim Verbinden das AUTH_TOKEN liefern, sonst wird vom Hub abgelehnt. Ändern Sie es von dem Standard (gleicher Wert in src/config.js und extension/src/config.js) – es sind nur solche Token, die andere lokale Prozesse davon abhalten, Ihren Browser zu lenken.

  • Dateizugriffe sind nur auf das Capture-Verzeichnis beschränkt. src/capture/capture-path.js löst jeden angeforderten Pfad und verweigert alles, was außerhalb liegt, auch ..-Traversen und Symlink-Unterverzeichnisse. Das ist wichtiger, als es aussieht: beliebige Schreibpfade sind praktisch Codeausführung.

  • Host-Sperrliste. BANLIST in extension/src/config.js blockt Navigation und Skriptlauf auf sensiblen Domains (Banken, PayPal, Gmail). Passen Sie es an. Hinweis: Screenshots und Aufzeichnungen nehmen gerenderte Pixel auf und werden nicht über die Sperrliste gefiltert.

  • Das Debug-Banner ist ein Merkmal. CDP-Tools hängen chrome.debugger an und zeigen eine gelbe „being debugged"-Leiste. Das ist Ihre Rückmeldung, dass etwas den Tab steuert. detach entfernt sie.

  • „Eval" läuft beliebiges JS im echten Kontext der Seite.

  • Interne Seiten sind gesperrt – die Erweiterung kann keine chrome://- oder chrome-extension://-URLs mit tzen.

  • Der Signaturschlüssel liegt nicht in diesem Repo. Die Erweiterungs-ID ist gepeergereiht. Über den öffentlichen key in extension/manifest.json; der passende private Schlüssel muss außerhalb der Versionsverwaltung bleiben (.gitignore sperrt *.pem). Er wird nur gebraucht, um ein .crx unter derselben ID neu zu verpacken – das Laden ohne packed erfordert es nicht.


Architektur

Der Server und die Erweiterung sind gespiegelt. Jede Tool-Gruppe in src/tools/ hat eine gleichnamige Handler-Datei extension/src/handlers/. Ein Tool hinzuzufügen bedeutet, genau das Paar anzufassen: sein Scheme-Docs auf der einen Seite und seine Implementierung auf der anderen.

Gruppe

Server (Schema + Doku)

Erweiterung (Implementierung)

navigation

src/tools/navigation.pdf

extension/src/handlers/navigation.pdf

tabs

src/tools/tabs.js

extension/src/handlers/tabs.js

perception

src/tools/perception.js

extension/src/handlers/perception.js

interaction

src/tools/interaction.js

extension/src/handlers/interaction.js

trusted input

src/trusted-input.js

extension/src/handlers/trusted-input.js

observability

src/observability.js

extension/src/handlers/observability.js

capture

src/tools/capture.js

extension/src/handlers/capture.js

Alles andere ist unterstützende Infrastruktur:

  • bin/custom-chrome-dev-mcp.js - die ausführbare Datei, die Sie bei Ihrem MCP-Client registrieren. Sie tut tools andres, als den Server zu starten.

  • src/config.js / extension/src/config.js - jede Einstellmöglichkeit, eine Datei pro Seite. AUTH_TOKEN und der Port müssen auf beiden Seiten übereinstimmen.

  • src/relay/hub-client.js - verbindet sich als role:"mcp" mit dem Hub, startet ihn, fallser fehlt, und verwandelt jeden Tool-Aufruf in eine Anfrage/Antwort über den Socket.

  • src/hub.js - der langlebige Relay, dem ws://127.0.0.1:9876 gehört. Hält den einen Extension-Socket sowie den Client pro Session und multiplexiert zwischen ihnen. Kennzeichnet IDs auf dem Draht neu (sie können sich zwischen Sessions überschneiden) und beendet sich selbst, wenn ein Hub den Port bereits besitzt.

  • src/capture/capture/path-access.js - die Schreib-Allowlist. Jeder Erfassungspfad läuft durch sie.

  • extension/src/connection.js - der Hub-Socket und der Heartbeat. Ein MV3-Service-Worker wird nach ~30s Leerlauf heruntergefahren, wodurch der Socket stillschweigend getrennt wird; ein Heartbeat unter 30s hält beide am Leben, und ein Alarm erweckt den Worker nach einer harten Beendigung wieder.

  • extension/src/tabs.js - welcher Tab durch einen Aufruf betroffen ist (explizite tabId > angehefteter Tab > aktiver Tab), der expectUrl-Schutz und die Sperrlistenprüfung.

  • extension/src/walker-bridge.js + extension/src/page/walker.js - das injizierte ISOLATED-Welt-Skript mit dem stabilen Element-Referenzsystem und das einzige Modul, das es zu erreichen weiß.

  • extension/src/cdp/ - session.js (Anfügen/Lösen, cdp(), Element-Mittelpunkte), keyboard.js (Tastennamen → CDP-Tastenereignisse), dialogs.js (Native-Dialog-Richtlinie), buffers.js (Konsolen- und Netzwerkringpuffer, auf 500/Tab begrenzt).

  • extension/src/recording/ - chrome.tabCapture erfordert eine Benutzergeste, die ein MCP-Aufruf nie hat. Deshalb nutzt die Aufzeichnung stattdessen den CDP-Screencast: JPEG-Frames, die an einen Offscreen-MediaRecorderer weitergegeben werden (des Service Worker hat kein DOM).

  • test/ - zweigleisige Suite: ein Offline-CI-Gate ohne Browserbedarf und eine Browser-Pur, die echtes Chrome steuert. Siehe Die Tests ausführen.


Projektstruktur

.
├── bin/
│   └── custom-chrome-dev-mcp.js   # executable entry - register THIS with your client
├── src/
│   ├── server.js                  # composes config + relay + tool registry
│   ├── config.js                  # port, token, capture dir, timeouts
│   ├── hub.js                     # long-lived relay owning :9876
│   ├── relay/
│   │   └── hub-client.js          # session -> hub socket; call()
│   ├── capture/
│   │   └── capture-path.js        # write allowlist for screenshots/recordings
│   └── tools/                     # ONE FILE PER TOOL GROUP - the public surface
│       ├── index.js               # the registry
│       ├── schemas.js             # shared arg shapes + passthrough helper
│       ├── navigation.js
│       ├── tabs.js
│       ├── perception.js
│       ├── interaction.js
│       ├── trusted-input.js
│       ├── observability.js
│       └── capture.js
├── extension/                     # Chrome MV3 extension
│   ├── manifest.json
│   └── src/
│       ├── background.js          # service worker entry - wiring only
│       ├── config.js              # token, banlist, buffer caps, asset paths
│       ├── connection.js          # hub socket + MV3 keepalive heartbeat
│       ├── tabs.js                # tab resolution, pinning, ban check
│       ├── walker-bridge.js       # channel to the injected page script
│       ├── cdp/
│       │   ├── session.js         # attach/detach, cdp(), element centres
│       │   ├── keyboard.js        # key names -> CDP key events
│       │   ├── dialogs.js         # native alert/confirm/prompt policy
│       │   └── buffers.js         # console + network ring buffers
│       ├── recording/
│       │   ├── recorder.js        # CDP screencast -> offscreen encoder
│       │   ├── offscreen.html
│       │   └── offscreen.js       # MediaRecorder host
│       ├── page/
│       │   └── walker.js          # injected DOM driver (ISOLATED world)
│       └── handlers/              # MIRRORS src/tools/ - one file per group
│           ├── index.js           # the handler table + dispatch
│           ├── navigation.js
│           ├── tabs.js
│           ├── perception.js
│           ├── interaction.js
│           ├── trusted-input.js
│           ├── observability.js
│           └── capture.js
└── test/                          # two lanes: offline (CI) + browser
    ├── run.mjs                    # CLI entry
    ├── lib/                       # runner, assertions, bridge, fixtures, session
    ├── fixtures/                  # the fixture pages, as real files
    └── suites/                    # one suite per tool group

Mitwirkende

Insirrt von Chrome DevTools MCP aus dem ChRome-DevTools-Team. Unabhängige Neuimplementierung, weder mit Googleverbunden, von Google befürwortet noch unterztützt.


Lizenz

MIT. Copyright (c) 2026 Haba Andrei.

Nutzen Sie es, forken Sie es, bringen Sie es raus. Sine einzige Bedingung: Der Copyright-Hinweis und der Genehmigungshinweis müssen bei jeder weesentlichen Kopje migeächen.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables MCP clients to drive a real, logged-in Chrome browser for web automation tasks like navigation, clicking, typing, and screenshotting.
    1
    1
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Drive your real, signed-in Chrome browser from any MCP client, enabling browser automation such as navigation, clicking, typing, and screenshots through standard MCP tools.
    1
  • A
    license
    C
    quality
    A
    maintenance
    MCP server for browser automation that drives Chrome via an extension, preserving login state and offering 45 tools for navigation, interaction, scraping, and screenshots.
    53
    4
    MIT

View all related MCP servers

Related MCP Connectors

  • Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.

  • Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.

  • Stealth web browser for agents: search, fetch, click, download and type in persistent MCP sessions.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/HabaAndrei/custom-chrome-dev-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server