earmark
earmark
Haz clic en un elemento de tu aplicación en ejecución, di qué debería cambiar, y tu agente de codificación recibe el selector CSS, el archivo fuente y la línea, la ruta del componente, los estilos calculados y la geometría de la caja — en lugar de "el botón de la derecha se ve mal".
Funciona en cualquier framework. No se requiere paso de compilación para la superposición.
┌─ browser ──────────────┐ ┌─ broker ────────┐ ┌─ agent ─────────┐
│ click → annotate │ POST │ store + SSE │ MCP │ list / watch │
│ pins, panel, markdown │───────▶│ long-poll │◀──────▶│ ask / resolve │
│ │◀───────│ .earmark/*.json │ │ dismiss │
└────────────────────────┘ SSE └─────────────────┘ └─────────────────┘Pruébalo en 30 segundos
npm install && npm run exampleVe a http://127.0.0.1:5173/examples/vanilla/, haz clic en la flecha de la barra de herramientas (abajo a la derecha) o pulsa alt+a, y luego haz clic en cualquier elemento de la página.
La página de aterrizaje y la guía completa se sirven junto con ella en http://127.0.0.1:5173/site/ — el código fuente está en site/index.html, un único archivo autocontenido sin dependencias.
Para la sincronización en vivo con el agente, ejecuta el broker en una segunda terminal:
npm run serverRelated MCP server: vibe-annotations
Instalación
npm install -D earmarkimport { createEarmark } from 'earmark';
if (import.meta.env.DEV) {
createEarmark();
}Sin bundler:
<script type="module" src="/node_modules/earmark/src/index.js" data-earmark-auto></script>Opciones
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) => {},
});El endpoint usa por defecto el broker local y se degrada silenciosamente cuando no hay nada escuchando — la superposición sigue funcionando, solo que el punto de sincronización se pone gris.
Uso
Herramienta | Qué hace |
➤ | Haz clic en un elemento. Mayús-clic para añadir más, y luego haz clic para terminar. |
T | Selecciona texto — la cadena exacta es lo más fácil de buscar que puedes darle a un agente. |
⛶ | Arrastra una región. Informa de cada elemento dentro, o marca un área vacía. |
❊ | Congela todo lo que se mueve — animaciones CSS, |
☰ | Panel: revisa, elimina, responde al agente, copia markdown. |
⌘↵ guarda una anotación, esc cancela, alt+a alterna la selección. Cada anotación se puede marcar como alta, normal o baja prioridad; high se ordena primero para el agente.
Modo copiar y pegar
Haz clic en Copiar markdown en el panel y pégalo en tu agente:
## 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 ← mainModo de sincronización con el agente (MCP)
claude mcp add earmark -- npx -y earmark-mcpO, para escribirlo en el .mcp.json del proyecto:
npx earmark-mcp initEse mismo proceso ejecuta el servidor MCP y el broker con el que habla el navegador. Cuando algo no funcione, pregúntale por qué:
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)Cada comprobación que falla imprime el comando que la arregla, y doctor sale con código no cero para que CI pueda usarlo.
Herramientas
Herramienta | Propósito |
| Trabajo pendiente, como markdown (o |
| Bloquea hasta que el humano anota algo |
| Una anotación con su hilo de respuestas completo |
| Qué pestañas del navegador están abiertas y qué rutas fueron anotadas |
| Una pestaña con todas las anotaciones que produjo |
| "Lo he leído, estoy en ello" — el pin se vuelve azul |
| Haz una pregunta aclaratoria — el pin se vuelve ámbar |
| Marca como hecho con un resumen — el pin se vuelve verde |
| Rechaza con una razón que el humano ve |
| Elimina todo |
| ¿Está conectada la superposición? ¿Qué endpoint debería usar? |
El bucle de corrección que esto permite:
watch → acknowledge → read the source path → edit the file → resolve → watchacknowledge importa en cualquier cosa lenta: sin él, un agente a mitad de una refactorización parece exactamente un agente que te ignora. El pin azul significa que lo ha asumido, el verde que realmente está hecho.
Cuando la retroalimentación es ambigua, usa ask en lugar de adivinar. La pregunta aparece en el pin; la respuesta del humano despierta el siguiente watch.
Estados
open → acknowledged → resolved, con needs-input cuando el agente espera a un humano y dismissed cuando lo rechaza. Los pines tienen código de colores: naranja, azul, verde, ámbar, gris.
Sesiones
Una sesión es una pestaña del navegador, no una carga de página — el id vive en sessionStorage, por lo que sobrevive a las recargas. Las anotaciones llevan su propio page.url, de modo que una sesión que recorrió tres rutas le da al agente un grupo con tres elementos enrutados de forma distinta.
La navegación SPA también se rastrea: pushState, replaceState, popstate y hashchange actualizan la lista de rutas de la sesión. Una pestaña cuenta como conectada exactamente mientras su flujo SSE esté abierto.
curl http://127.0.0.1:7331/sessionsRutas de archivos fuente
Los selectores le dicen al agente qué buscar. Las rutas de archivo fuente le indican exactamente dónde mirar, que es la diferencia entre una edición y tres búsquedas grep.
React 19 eliminó el campo de fibra _debugSource en tiempo de ejecución, por lo que esto se hace en tiempo de compilación:
// vite.config.js
import earmark from 'vite-plugin-earmark';
export default {
plugins: [react(), earmark()],
};Cada elemento JSX intrínseco recibe data-earmark-src="src/Card.tsx:42:7" durante vite dev. El plugin también inyecta la superposición, por lo que createEarmark() en el código de tu aplicación se vuelve opcional.
earmark({
inject: false, // do not auto-mount the overlay
endpoint: '…', // passed through to createEarmark
applyInBuild: true, // also stamp production builds (off by default)
})Sin el plugin todo sigue funcionando: obtienes selectores, nombres de componentes y texto, solo que no file:line. También puedes añadir data-earmark-src a mano.
HTML y CSS plano — sin paso de compilación
Un sitio estático no tiene compilación que sellar, así que earmark resuelve el origen en el momento de la anotación:
HTML — el documento se vuelve a obtener y se analiza con seguimiento de posición, y luego se recorre en el origen la ruta de índice de hijo del elemento. Cada paso se comprueba contra el nombre de etiqueta en vivo, así que una página renderizada por un framework (donde el HTML servido es solo un caparazón) no informa nada en lugar de inventar una línea.
CSS — cada regla que coincide con el elemento, mapeada de vuelta al archivo y la línea que la declara. Esta funciona en todas partes, con o sin 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);El agente ahora sabe que el padding que tiene que cambiar vive en la línea 49 de la regla genérica button, no en .primary. Los bloques <style> en línea se compensan dentro de su documento anfitrión; las hojas de estilo externas informan de su propia ruta; las hojas de estilo de origen cruzado se omiten porque su contenido es ilegible.
Broker independiente
npx earmark-server --port 7331
curl http://127.0.0.1:7331/markdownRuta | |
| vitalidad + contadores |
| lista |
| crear (lote) |
| long-poll |
| actualizar estado |
| añadir al hilo |
| eliminar · limpiar |
| registrar una pestaña / registrar un cambio de ruta |
| pestañas, con contadores y anotaciones |
| flujo SSE; también la señal de actividad de la pestaña |
| el documento dirigido al agente |
Banderas: --host --store --file --no-persist --webhook --token --quiet.
Almacenamiento
--store json (por defecto) escribe un .earmark/annotations.json legible con un debounce de 250 ms. --store sqlite escribe cada cambio inmediatamente en .earmark/annotations.db a través de node:sqlite, de modo que un fallo pierde como máximo la sentencia en vuelo — sin dependencia, Node 22.5+, y si no está disponible, usa JSON como alternativa. --store memory no conserva nada.
Webhooks
npx earmark-server --webhook https://hooks.example/earmarkTambién EARMARK_WEBHOOK_URL y EARMARK_WEBHOOKS (separados por comas). Cada evento de anotación se envía por POST con una cabecera x-earmark-event. La entrega es de disparar y olvidar con un tiempo de espera de 5 s y un reintento, de modo que un endpoint muerto no puede detener el bucle de anotaciones.
Seguridad
Esta es una herramienta de desarrollo.
El broker se vincula solo a
127.0.0.1. No lo vincules a0.0.0.0.CORS está abierto por diseño: tu servidor de desarrollo está en un origen arbitrario.
Cualquier página abierta en tu navegador puede alcanzar un puerto de bucle local. Pasa
--token SECRETsi eso importa en tu máquina.Los webhooks envían contenido de anotaciones fuera de tu máquina — URLs de página, texto de elementos y lo que hayas escrito. Solo configura endpoints que controlas.
La resolución de origen vuelve a obtener tu propia página y hojas de estilo desde el mismo origen. No se envía nada a ningún lugar.
No lo ejecutes en un host compartido o público.
Pruebas
npm testSiete suites, 88 pruebas: comportamiento del almacenamiento y HTTP, la superficie MCP controlada por un cliente stdio real, el cliente de sincronización de la superposición, ambos backends de persistencia, la entrega de webhooks, la CLI init/doctor y los resolvedores de origen.
No compatible
Solo navegadores de escritorio. Sin iframes, sin internals de canvas/WebGL, sin capturas de pantalla. Consulta plan.md para la lista abierta completa y el razonamiento detrás de cada decisión de diseño.
Licencia
MIT. Implementación de sala limpia — no derivada del código fuente de ninguna otra herramienta.
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