Skip to main content
Glama

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 example

Ve 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 server

Related MCP server: vibe-annotations

Instalación

npm install -D earmark
import { 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, element.animate(), <video>, <audio>.

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 ← main

Modo de sincronización con el agente (MCP)

claude mcp add earmark -- npx -y earmark-mcp

O, para escribirlo en el .mcp.json del proyecto:

npx earmark-mcp init

Ese 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

earmark_list_annotations

Trabajo pendiente, como markdown (o format: "json"); delimita con session

earmark_watch_annotations

Bloquea hasta que el humano anota algo

earmark_get_annotation

Una anotación con su hilo de respuestas completo

earmark_list_sessions

Qué pestañas del navegador están abiertas y qué rutas fueron anotadas

earmark_get_session

Una pestaña con todas las anotaciones que produjo

earmark_acknowledge

"Lo he leído, estoy en ello" — el pin se vuelve azul

earmark_ask

Haz una pregunta aclaratoria — el pin se vuelve ámbar

earmark_resolve

Marca como hecho con un resumen — el pin se vuelve verde

earmark_dismiss

Rechaza con una razón que el humano ve

earmark_clear

Elimina todo

earmark_status

¿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 → watch

acknowledge 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

openacknowledgedresolved, 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/sessions

Rutas 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/markdown

Ruta

GET /health

vitalidad + contadores

GET /annotations?status=open&session=ID

lista

POST /annotations

crear (lote)

GET /annotations/wait?since=N&timeout=30000

long-poll

PATCH /annotations/:id

actualizar estado

POST /annotations/:id/replies

añadir al hilo

DELETE /annotations/:id · DELETE /annotations

eliminar · limpiar

POST /session

registrar una pestaña / registrar un cambio de ruta

GET /sessions · GET /sessions/:id

pestañas, con contadores y anotaciones

GET /events?session=ID

flujo SSE; también la señal de actividad de la pestaña

GET /markdown

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/earmark

Tambié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 a 0.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 SECRET si 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 test

Siete 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.

F
license - not found
-
quality - not tested
C
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

View all related MCP servers

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,

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/nahar-strativ/Agentic'

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