earmark
earmark
Klicke in deiner laufenden App auf ein Element, sag, was sich ändern soll, und dein Coding-Agent erhält den CSS-Selektor, die Quelldatei und Zeile, den Komponentenpfad, die berechneten Stile und die Box-Geometrie – statt „der Button rechts sieht falsch aus“.
Funktioniert mit jedem Framework. Für das Overlay ist kein Build-Schritt erforderlich.
┌─ browser ──────────────┐ ┌─ broker ────────┐ ┌─ agent ─────────┐
│ click → annotate │ POST │ store + SSE │ MCP │ list / watch │
│ pins, panel, markdown │───────▶│ long-poll │◀──────▶│ ask / resolve │
│ │◀───────│ .earmark/*.json │ │ dismiss │
└────────────────────────┘ SSE └─────────────────┘ └─────────────────┘In 30 Sekunden ausprobieren
npm install && npm run exampleÖffne http://127.0.0.1:5173/examples/vanilla/, klicke auf den Pfeil in der Symbolleiste (unten rechts) oder drücke alt+a, und klicke dann auf irgendetwas auf der Seite.
Die Landingpage und die vollständige Anleitung werden unter http://127.0.0.1:5173/site/ mit ausgeliefert – der Quellcode in site/index.html, eine einzige in sich geschlossene Datei ohne Abhängigkeiten.
Für die Live-Agent-Synchronisierung führe den Broker in einem zweiten Terminal aus:
npm run serverRelated MCP server: vibe-annotations
Installation
npm install -D earmarkimport { createEarmark } from 'earmark';
if (import.meta.env.DEV) {
createEarmark();
}Ohne Bundler:
<script type="module" src="/node_modules/earmark/src/index.js" data-earmark-auto></script>Optionen
createEarmark({
endpoint: 'http://127.0.0.1:7331', // or false for copy-paste only
hotkey: 'alt+a',
theme: 'auto', // 'auto' | 'light' | 'dark'
persist: true, // keep annotations across reloads
onAnnotate: (annotation) => {},
});Standardmäßig zeigt der Endpunkt auf den lokalen Broker und fällt stillschweigend ab, wenn nichts zuhört – das Overlay funktioniert weiterhin, nur der Sync-Punkt wird grau.
Verwendung
Tool | Was es tut |
➤ | Klicke auf ein Element. Shift-Klick, um weitere hinzuzufügen, dann Klick zum Abschließen. |
T | Text auswählen – der exakte String ist das am besten grepbare, was du einem Agenten geben kannst. |
⛶ | Ziehe einen Bereich auf. Meldet jedes enthaltene Element oder markiert einen leeren Bereich. |
❊ | Friert alles Bewegende ein – CSS-Animationen, |
☰ | Panel: prüfen, löschen, dem Agenten antworten, Markdown kopieren. |
⌘↵ speichert eine Annotation, esc bricht ab, alt+a schaltet das Auswählen um. Jede Annotation kann als hoch, normal oder niedrig priorisiert markiert werden; high wird für den Agenten zuerst sortiert.
Copy-Paste-Modus
Klicke im Panel auf Copy markdown und füge es in deinen Agenten ein:
## UI feedback — 1 annotation
- **Page:** http://localhost:5173/dashboard
- **Viewport:** 1440×900 @2x, dark mode
- **Framework:** react
### 1. Export button padding is too tight — needs 10px 16px
- **Element:** `<button>` <ExportButton>
- **Selector:** `[data-testid="export-btn"]`
- **Source:** `src/components/Card.tsx:42:7`
- **Component path:** App › Dashboard › Card › ExportButton
- **Text:** "Export"
- **Box:** 66×37 at (194, 376)
- **Computed:** padding: 7px 13px; border-radius: 8px; font-size: 13px
- **Ancestors:** div.row ← section.card ← mainAgenten-Synchronisierungsmodus (MCP)
claude mcp add earmark -- npx -y earmark-mcpOder um es in die .mcp.json des Projekts zu schreiben:
npx earmark-mcp initDieser eine Prozess führt den MCP-Server und den Broker aus, mit dem der Browser spricht. Wenn etwas nicht funktioniert, frag ihn, warum:
npx earmark-mcp doctor✓ Node version: v24.12.0
✓ sqlite backend: available
✓ MCP registration: earmark is registered in .mcp.json
✓ Broker: responding on http://127.0.0.1:7331 — 1 annotations, 2 sessions
✓ Browser overlay: http://localhost:5173/ (1 annotations)Jede fehlgeschlagene Prüfung gibt den Befehl aus, der sie behebt, und doctor wird mit einem Non-Zero-Exit beendet, damit CI es verwenden kann.
Tools
Tool | Zweck |
| Offene Arbeit als Markdown (oder |
| Blockiert, bis der Mensch etwas annotiert |
| Eine Annotation mit ihrem vollständigen Antwortthread |
| Welche Browser-Tabs offen sind und welche Routen annotiert wurden |
| Ein Tab mit jeder Annotation, die er erzeugt hat |
| „Ich habe es gelesen, ich bin dran“ – der Pin wird blau |
| Stelle eine klärende Frage – der Pin wird bernsteinfarben |
| Als erledigt markieren mit einer Zusammenfassung – der Pin wird grün |
| Ablehnen mit einem Grund, den der Mensch sieht |
| Alles löschen |
| Ist das Overlay verbunden? Welchen Endpunkt soll es verwenden? |
Die damit ermöglichte Korrekturschleife:
watch → acknowledge → read the source path → edit the file → resolve → watchacknowledge ist bei allem Langsamen wichtig: Ohne es sieht ein Agent, der mitten in einem Refactoring steckt, genauso aus wie ein Agent, der dich ignoriert hat. Blauer Pin bedeutet aufgenommen, grün bedeutet tatsächlich erledigt.
Wenn das Feedback mehrdeutig ist, frage mit ask, anstatt zu raten. Die Frage erscheint auf dem Pin; die Antwort des Menschen weckt den nächsten watch.
Status
open → acknowledged → resolved, mit needs-input, wenn der Agent auf einen Menschen wartet, und dismissed, wenn er ablehnt. Pins sind farbcodiert: orange, blau, grün, bernstein, grau.
Sessions
Eine Session ist ein Browser-Tab, kein Seitenladevorgang – die ID lebt in sessionStorage, übersteht also Neuladungen. Annotationen tragen ihre eigene page.url, sodass eine Session, die über drei Routen gewandert ist, einem Agenten eine Gruppe mit drei unterschiedlich gerouteten Elementen liefert.
Auch SPA-Navigation wird verfolgt: pushState, replaceState, popstate und hashchange aktualisieren alle die Routenliste der Session. Ein Tab gilt genau so lange als verbunden, wie sein SSE-Stream offen ist.
curl http://127.0.0.1:7331/sessionsQuelldateipfade
Selektoren sagen einem Agenten, wonach er mit grep suchen soll. Quellpfade sagen ihm genau, wo er suchen muss – das ist der Unterschied zwischen einer Änderung und drei Greps.
React 19 hat das Laufzeit-Feld _debugSource der Fiber entfernt, daher geschieht dies zur Build-Zeit:
// vite.config.js
import earmark from 'vite-plugin-earmark';
export default {
plugins: [react(), earmark()],
};Jedes intrinsische JSX-Element erhält während vite dev data-earmark-src="src/Card.tsx:42:7". Das Plugin injiziert auch das Overlay, sodass createEarmark() in deinem Anwendungscode optional wird.
earmark({
inject: false, // do not auto-mount the overlay
endpoint: '…', // passed through to createEarmark
applyInBuild: true, // also stamp production builds (off by default)
})Ohne das Plugin funktioniert weiterhin alles – du erhältst Selektoren, Komponentennamen und Text, nur kein file:line. Du kannst data-earmark-src auch von Hand hinzufügen.
Einfaches HTML und CSS – kein Build-Schritt
Eine statische Website hat keinen Build, den man stempeln könnte, daher löst earmark die Quelle stattdessen zur Annotationszeit auf:
HTML – das Dokument wird erneut abgerufen und mit Positionsverfolgung geparst, dann wird der Child-Index-Pfad des Elements im Quelltext abgegangen. Jeder Schritt wird gegen den Live-Tag-Namen geprüft, sodass eine framework-gerenderte Seite (bei der das ausgelieferte HTML nur eine Hülle ist) nichts meldet, anstatt eine Zeile zu erfinden.
CSS – jede Regel, die auf das Element passt, wird auf die Datei und Zeile zurückgeführt, die sie deklariert. Diese Funktion funktioniert überall, mit oder ohne Framework.
- **Source:** `index.html:101:11` _(resolved from the served HTML)_
- **CSS rules that style it:**
- `button` → `index.html (inline <style>):49`
- padding: 7px 13px; border-radius: 8px; border: 1px solid var(--line);
- `button.primary` → `index.html (inline <style>):59`
- background: var(--accent); color: rgb(255, 255, 255);Der Agent weiß jetzt, dass das Padding, das er ändern muss, in Zeile 49 der generischen button-Regel liegt, nicht in .primary. Inline-<style>-Blöcke werden in ihr Host-Dokument versetzt; externe Stylesheets melden ihren eigenen Pfad; Cross-Origin-Stylesheets werden übersprungen, weil ihre Inhalte nicht lesbar sind.
Eigenständiger Broker
npx earmark-server --port 7331
curl http://127.0.0.1:7331/markdownRoute | |
| Lebendigkeit + Zähler |
| Liste |
| erstellen (Stapel) |
| Long-Poll |
| Status aktualisieren |
| an den Thread anhängen |
| entfernen · leeren |
| einen Tab registrieren / eine Routenänderung aufzeichnen |
| Tabs mit Zählern und Annotationen |
| SSE-Stream; auch das Lebendigkeitssignal des Tabs |
| das agentenorientierte Dokument |
Flags: --host --store --file --no-persist --webhook --token --quiet.
Storage
--store json (Standard) schreibt bei einem 250-ms-Debounce eine lesbare .earmark/annotations.json. --store sqlite schreibt jede Änderung sofort über node:sqlite in .earmark/annotations.db, sodass ein Absturz höchstens die gerade laufende Anweisung verliert – keine Abhängigkeit, Node 22.5+, und es fällt auf JSON zurück, wenn nicht verfügbar. --store memory behält nichts.
Webhooks
npx earmark-server --webhook https://hooks.example/earmarkAußerdem EARMARK_WEBHOOK_URL und EARMARK_WEBHOOKS (durch Kommas getrennt). Jedes Annotation-Ereignis wird mit einem x-earmark-event-Header per POST gesendet. Die Zustellung ist Fire-and-Forget mit einem 5-s-Timeout und einem erneuten Versuch, sodass ein toter Endpunkt die Annotation-Schleife nicht aufhalten kann.
Sicherheit
Dies ist ein Entwicklungswerkzeug.
Der Broker bindet nur an
127.0.0.1. Binde ihn nicht an0.0.0.0.CORS ist absichtlich offen – dein Dev-Server befindet sich auf einem beliebigen Origin.
Jede in deinem Browser geöffnete Seite kann einen Loopback-Port erreichen. Übergib
--token SECRET, falls das auf deinem Rechner eine Rolle spielt.Webhooks senden Annotationsinhalte von deinem Rechner – Seiten-URLs, Elementtext und alles, was du getippt hast. Konfiguriere nur Endpunkte, die du kontrollierst.
Die Quellauflösung ruft deine eigene Seite und Stylesheets erneut vom selben Origin ab. Es wird nichts irgendwohin gesendet.
Führe es nicht auf einem gemeinsamen oder öffentlichen Host aus.
Tests
npm testSieben Suiten, 88 Tests: Store- und HTTP-Verhalten, die MCP-Oberfläche, gesteuert von einem echten Stdio-Client, der Sync-Client des Overlays, beide Persistenz-Backends, Webhook-Zustellung, die init/doctor-CLI und die Quellauflöser.
Nicht unterstützt
Nur Desktop-Browser. Keine Iframes, keine Canvas/WebGL-Interna, keine Screenshots. Siehe plan.md für die vollständige offene Liste und die Begründung hinter jeder Designentscheidung.
Lizenz
MIT. Clean-Room-Implementierung – nicht aus dem Quellcode eines anderen Werkzeugs abgeleitet.
This server cannot be installed
Maintenance
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
- Alicense-qualityDmaintenanceMCP server for visual feedback, video direction, and QA assertions on web pages, enabling AI agents to read, reply, and resolve annotations in real time.4MIT
- Flicense-qualityAmaintenanceMCP server that exposes web page annotations to AI coding agents, enabling automated implementation of visual feedback and design tweaks.1132
- Alicense-qualityCmaintenanceA MCP server that enables AI coding agents to consume structured UI feedback via click-to-annotate, supporting issue types and severity levels.11MIT
- AlicenseAqualityBmaintenanceMCP server that opens a browser and adds a comment/pencil overlay to every page, letting you annotate live sites and send the marks to your coding agent.8MIT
Related MCP Connectors
MCP server for AgentDocs (agentdocs.eu): read, search, write, comment on & share Markdown docs.
A paid remote MCP for AI agent browser approval MCP, built to return verdicts, receipts, usage logs,
A paid remote MCP for AI agent browser DevTools MCP, built to return verdicts, receipts, usage logs,
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/nahar-strativ/Agentic'
If you have feedback or need assistance with the MCP directory API, please join our Discord server