Skip to main content
Glama

earmark

実行中のアプリで要素をクリックし、変更したい内容を伝えると、コーディングエージェントはCSSセレクタ、ソースファイルと行、コンポーネントパス、計算済みスタイル、ボックスジオメトリを取得します — 「右側のボタンがおかしい」ではなく。

どのフレームワークでも動作します。オーバーレイにビルドステップは不要です。

┌─ browser ──────────────┐        ┌─ broker ────────┐        ┌─ agent ─────────┐
│ click → annotate       │ POST   │ store + SSE     │  MCP   │ list / watch    │
│ pins, panel, markdown  │───────▶│ long-poll       │◀──────▶│ ask / resolve   │
│                        │◀───────│ .earmark/*.json │        │ dismiss         │
└────────────────────────┘  SSE   └─────────────────┘        └─────────────────┘

30秒で試す

npm install && npm run example

http://127.0.0.1:5173/examples/vanilla/ を開き、ツールバー(右下)の矢印をクリックするか alt+a を押して、ページ上の任意のものをクリックします。

ランディングページと完全なガイドは、http://127.0.0.1:5173/site/ で一緒に提供されます。ソースは site/index.html にあり、依存関係のない単一の自己完結型ファイルです。

ライブエージェント同期を行うには、2つ目のターミナルでブローカーを実行します:

npm run server

Related MCP server: vibe-annotations

インストール

npm install -D earmark
import { createEarmark } from 'earmark';

if (import.meta.env.DEV) {
  createEarmark();
}

バンドラー不要:

<script type="module" src="/node_modules/earmark/src/index.js" data-earmark-auto></script>

オプション

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) => {},
});

エンドポイントはデフォルトでローカルブローカーを指し、何もリッスンしていない場合は静かに機能低下します。オーバーレイは動作を続け、同期ドットがグレーになるだけです。


使い方

ツール

機能

➤

要素をクリックします。Shift+クリックで追加し、クリックで確定します。

T

テキストを選択 — 正確な文字列は、エージェントに渡せる最もgrepしやすいものです。

⛶

領域をドラッグします。内部のすべての要素を報告するか、空の領域にフラグを立てます。

❊

動いているすべてを停止 — CSSアニメーション、element.animate()、<video>、<audio>。

☰

パネル: 確認、削除、エージェントへの回答、マークダウンのコピー。

⌘↵ で注釈を保存、esc でキャンセル、alt+a でピッキングを切り替えます。各注釈は高、通常、低の優先度に設定でき、high はエージェント用に最初に並べられます。


コピー&ペーストモード

パネルの マークダウンをコピー をクリックして、エージェントに貼り付けます:

## 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

エージェント同期モード(MCP)

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

または、プロジェクトの .mcp.json に書き込む場合:

npx earmark-mcp init

その1つのプロセスが、MCPサーバーとブラウザが通信するブローカーを実行します。何かが機能していないときは、その理由を尋ねてください:

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)

失敗した各チェックは修正コマンドを出力し、doctor は非ゼロで終了するため、CIで利用できます。

ツール

ツール

目的

earmark_list_annotations

未処理の作業をマークダウンで返します(format: "json" も可)。session でスコープを指定します。

earmark_watch_annotations

人間が何かを注釈するまでブロックします

earmark_get_annotation

完全な返信スレッドを含む1つの注釈

earmark_list_sessions

開いているブラウザタブと、注釈が付けられたルート

earmark_get_session

生成されたすべての注釈を含む1つのタブ

earmark_acknowledge

「確認しました、対応中です」 — ピンが青に変わります

earmark_ask

明確化の質問をする — ピンが琥珀色に変わります

earmark_resolve

要約付きで完了とマーク — ピンが緑に変わります

earmark_dismiss

人間が見る理由を添えて拒否

earmark_clear

すべて削除

earmark_status

オーバーレイは接続されていますか? どのエンドポイントを使うべきですか?

これによって可能になる修正ループ:

watch → acknowledge → read the source path → edit the file → resolve → watch

acknowledge は時間のかかる処理で重要です。これがないと、リファクタリングの途中のエージェントは、あなたを無視したエージェントとまったく同じに見えます。青いピンは対応中を意味し、緑は実際に完了を意味します。

フィードバックがあいまいな場合は、推測する代わりに ask を使います。質問はピンに表示され、人間の回答が次の watch を起動します。

ステータス

open → acknowledged → resolved の流れで、エージェントが人間を待っている場合は needs-input、拒否した場合は dismissed になります。ピンは色分けされます: オレンジ、青、緑、琥珀、グレー。

セッション

セッションは1つのブラウザタブであり、1回のページロードではありません。IDは sessionStorage に保持されるため、リロード後も存続します。注釈はそれぞれ独自の page.url を持つため、3つのルートをまたいだセッションは、エージェントに3つの異なるルートを持つ1つのグループを提供します。

SPAナビゲーションも追跡されます: pushState、replaceState、popstate、hashchange はすべてセッションのルートリストを更新します。タブは、SSEストリームが開いている間だけ接続済みと見なされます。

curl http://127.0.0.1:7331/sessions

ソースファイルパス

セレクタはエージェントに何をgrepすべきかを伝えます。ソースパスは正確にどこを見るべきかを伝えます。これが1回の編集と3回のgrepの違いです。

React 19はランタイムの _debugSource ファイバーフィールドを削除したため、これはビルド時に行われます:

// vite.config.js
import earmark from 'vite-plugin-earmark';

export default {
  plugins: [react(), earmark()],
};

vite dev 中、すべての組み込みJSX要素は data-earmark-src="src/Card.tsx:42:7" を取得します。プラグインはオーバーレイも注入するため、アプリコード内の createEarmark() はオプションになります。

earmark({
  inject: false,        // do not auto-mount the overlay
  endpoint: '…',        // passed through to createEarmark
  applyInBuild: true,   // also stamp production builds (off by default)
})

プラグインがなくてもすべて動作します。セレクタ、コンポーネント名、テキストは取得できますが、file:line だけは取得できません。data-earmark-src を手動で追加することもできます。

プレーンなHTMLとCSS — ビルドステップ不要

静的サイトにはスタンプするビルドがないため、earmarkは代わりに注釈時にソースを解決します:

  • HTML — ドキュメントを再取得して位置追跡付きで解析し、ソース内で要素の子インデックスパスをたどります。各ステップはライブのタグ名と照合されるため、フレームワークでレンダリングされたページ(提供されたHTMLが単なるシェルである場合)は、行をでっち上げるのではなく何も報告しません。

  • CSS — 要素に一致するすべてのルールを、それを宣言しているファイルと行にマッピングします。これはフレームワークの有無にかかわらずどこでも機能します。

- **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);

エージェントは、変更する必要があるパディングが汎用の button ルールの49行目にあり、.primary にはないことを知ります。インライン <style> ブロックはホストドキュメント内のオフセットとして扱われます。外部スタイルシートは自身のパスを報告します。クロスオリジンのスタイルシートは、その内容を読み取れないためスキップされます。


スタンドアロンブローカー

npx earmark-server --port 7331
curl http://127.0.0.1:7331/markdown

ルート

GET /health

死活監視 + 件数

GET /annotations?status=open&session=ID

一覧

POST /annotations

作成(バッチ)

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

ロングポーリング

PATCH /annotations/:id

ステータス更新

POST /annotations/:id/replies

スレッドに追記

DELETE /annotations/:id · DELETE /annotations

削除 · クリア

POST /session

タブを登録 / ルート変更を記録

GET /sessions · GET /sessions/:id

タブと、件数と注釈

GET /events?session=ID

SSEストリーム。タブの生存シグナルも兼ねる

GET /markdown

エージェント向けドキュメント

フラグ: --host --store --file --no-persist --webhook --token --quiet。

ストレージ

--store json(デフォルト)は、250msのデバウンスで読み取り可能な .earmark/annotations.json を書き込みます。--store sqlite は、node:sqlite を介して各変更を即座に .earmark/annotations.db に書き込むため、クラッシュしても実行中のステートメントだけが失われます。依存関係なし、Node 22.5+ で、利用できない場合はJSONにフォールバックします。--store memory は何も保持しません。

ウェブフック

npx earmark-server --webhook https://hooks.example/earmark

さらに EARMARK_WEBHOOK_URL と EARMARK_WEBHOOKS(カンマ区切り)があります。すべての注釈イベントは x-earmark-event ヘッダー付きでPOSTされます。配信は5秒のタイムアウトと1回のリトライでfire-and-forget方式のため、停止したエンドポイントによって注釈ループが止まることはありません。


セキュリティ

これは開発ツールです。

  • ブローカーは 127.0.0.1 のみにバインドします。0.0.0.0 にバインドしないでください。

  • CORSは設計上オープンです。開発サーバーは任意のオリジン上にあります。

  • ブラウザで開いている任意のページがループバックポートに到達できます。お使いのマシンでそれが問題になる場合は --token SECRET を渡してください。

  • ウェブフックは注釈の内容をマシンの外部に送信します — ページURL、要素テキスト、入力した内容など。自分が管理するエンドポイントだけを設定してください。

  • ソース解決は、同じオリジンから自分のページとスタイルシートを再取得します。どこにも送信されません。

  • 共有ホストや公開ホストで実行しないでください。


テスト

npm test

7つのスイート、88のテスト: ストアとHTTPの動作、実際のstdioクライアントで駆動するMCPサーフェス、オーバーレイの同期クライアント、両方の永続化バックエンド、ウェブフック配信、init/doctor CLI、そしてソースリゾルバ。


非対応

デスクトップブラウザのみ対応。iframe、canvas/WebGLの内部、スクリーンショットには対応していません。未対応の全一覧と各設計判断の理由については plan.md を参照してください。


ライセンス

MIT。クリーンルーム実装 — 他のツールのソースから派生したものではありません。

Related MCP Connectors

Related MCP Servers