earmark
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 examplehttp://127.0.0.1:5173/examples/vanilla/ を開き、ツールバー(右下)の矢印をクリックするか alt+a を押して、ページ上の任意のものをクリックします。
ランディングページと完全なガイドは、http://127.0.0.1:5173/site/ で一緒に提供されます。ソースは site/index.html にあり、依存関係のない単一の自己完結型ファイルです。
ライブエージェント同期を行うには、2つ目のターミナルでブローカーを実行します:
npm run serverRelated MCP server: vibe-annotations
インストール
npm install -D earmarkimport { 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アニメーション、 |
☰ | パネル: 確認、削除、エージェントへの回答、マークダウンのコピー。 |
⌘↵ で注釈を保存、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で利用できます。
ツール
ツール | 目的 |
| 未処理の作業をマークダウンで返します( |
| 人間が何かを注釈するまでブロックします |
| 完全な返信スレッドを含む1つの注釈 |
| 開いているブラウザタブと、注釈が付けられたルート |
| 生成されたすべての注釈を含む1つのタブ |
| 「確認しました、対応中です」 — ピンが青に変わります |
| 明確化の質問をする — ピンが琥珀色に変わります |
| 要約付きで完了とマーク — ピンが緑に変わります |
| 人間が見る理由を添えて拒否 |
| すべて削除 |
| オーバーレイは接続されていますか? どのエンドポイントを使うべきですか? |
これによって可能になる修正ループ:
watch → acknowledge → read the source path → edit the file → resolve → watchacknowledge は時間のかかる処理で重要です。これがないと、リファクタリングの途中のエージェントは、あなたを無視したエージェントとまったく同じに見えます。青いピンは対応中を意味し、緑は実際に完了を意味します。
フィードバックがあいまいな場合は、推測する代わりに 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ルート | |
| 死活監視 + 件数 |
| 一覧 |
| 作成(バッチ) |
| ロングポーリング |
| ステータス更新 |
| スレッドに追記 |
| 削除 · クリア |
| タブを登録 / ルート変更を記録 |
| タブと、件数と注釈 |
| SSEストリーム。タブの生存シグナルも兼ねる |
| エージェント向けドキュメント |
フラグ: --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 test7つのスイート、88のテスト: ストアとHTTPの動作、実際のstdioクライアントで駆動するMCPサーフェス、オーバーレイの同期クライアント、両方の永続化バックエンド、ウェブフック配信、init/doctor CLI、そしてソースリゾルバ。
非対応
デスクトップブラウザのみ対応。iframe、canvas/WebGLの内部、スクリーンショットには対応していません。未対応の全一覧と各設計判断の理由については plan.md を参照してください。
ライセンス
MIT。クリーンルーム実装 — 他のツールのソースから派生したものではありません。
This server cannot be deployed
Maintenance
Related MCP Connectors
Agent-ready feedback for AI-built apps — read and close UI feedback reports over MCP.
MCP Server for an Agent Task Marketplace
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
An MCP server that automatically collects feedback on your MCP server.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP server for visual feedback, video direction, and QA assertions on web pages, enabling AI agents to read, reply, and resolve annotations in real time.6MIT
- FlicenseNot gradedqualityAmaintenanceMCP server that exposes web page annotations to AI coding agents, enabling automated implementation of visual feedback and design tweaks.1 npm190-
- AlicenseNot gradedqualityDmaintenanceA MCP server that enables AI coding agents to consume structured UI feedback via click-to-annotate, supporting issue types and severity levels.8 npmMIT
- AlicenseAqualityDmaintenanceMCP 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.82MIT