ui-annotate
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ui-annotatecheck the UI annotations on this page and fix the important ones"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ui-annotate
Point at the UI, tell your coding agent what is wrong. A browser extension for Chrome and
Firefox: click an element of your running dev app, write a note, and Claude Code, Codex or Cursor
receives the selector, DOM context, component name and the exact source file:line. The agent
answers and resolves right on the page.
English · Русский

Why
Describing UI problems in a chat is slow and lossy: "the grey button under the price, no, the other one". Screenshots carry no code location, so the agent greps for text and guesses. ui-annotate attaches what the agent needs to every note: which element, which component, which line of which file. Nothing gets installed into your app: the extension reads framework internals of the dev build.
Related MCP server: earmark
Features
Element, text, multi-select and area annotations with intent (fix, change, question, approve) and severity.
Source location without app changes for Vue, React 18/19, Svelte 4/5, Angular and Solid; exact template lines with common build plugins (see Source location).
Two-way thread on the page: the agent acknowledges, asks questions, resolves with a summary; you reply in the overlay.
Agent integrations: an MCP server (stdio and Streamable HTTP), hooks that put open annotations into the agent's context and keep it from stopping while work is open, a Claude Code plugin with a live channel.
Automatic mode: every new annotation of a site goes to a background
claude,codexorcursor-agentrun in the project folder, limited to reading and editing.Project root detection: the daemon finds the repository behind
localhost:PORTby itself, so paths are relative and annotations reach only the agent working in that repository.Local only: one daemon on
127.0.0.1, SQLite storage, token pairing done automatically.
How it works
flowchart LR
subgraph Browser
page["dev app page"] -- "framework internals<br/>(MAIN world)" --> overlay["overlay<br/>(content script)"]
overlay --> bg["extension background"]
end
bg -- "HTTP + token" --> daemon["ui-annotate daemon<br/>127.0.0.1:4848, SQLite"]
daemon -- "MCP tools, hooks, channel" --> agents["Claude Code · Codex · Cursor"]
daemon -. "automatic mode" .-> runs["background agent run<br/>in the project root"]
agents -- "acknowledge · reply · resolve" --> daemon
daemon -- "live updates (SSE)" --> bgQuick start
Want your agent to set it up? Give it docs/agents/install.md.
Requirements: Node.js ≥ 22.13 (the daemon uses node:sqlite), pnpm 9, Chrome ≥ 116 or Firefox ≥ 128.
1. Build
git clone https://github.com/AnarchyGhost/ui-annotate.git
cd ui-annotate
pnpm install
pnpm build # extension → apps/extension/.output/{chrome-mv3,firefox-mv3}2. Load the extension
Chrome:
chrome://extensions→ Developer mode → Load unpacked →apps/extension/.output/chrome-mv3.Firefox:
about:debugging#/runtime/this-firefox→ Load Temporary Add-on →apps/extension/.output/firefox-mv3/manifest.json. A temporary add-on is removed when Firefox restarts; Firefox Developer Edition, Nightly and forks such as Zen keep an unsigned build installed withxpinstall.signatures.required = false.
3. Connect your agent (any combination)
# Claude Code: MCP server, skill, hooks and the channel in one plugin
claude plugin marketplace add "$PWD"
claude plugin install ui-annotate@ui-annotate-local
# Codex: MCP server in ~/.codex/config.toml, hooks in ~/.codex/hooks.json
pnpm exec ui-annotate init codex --write # then approve the hooks once in Codex: /hooks
# Cursor: .cursor/mcp.json and .cursor/hooks.json of the project you run it in
cd /path/to/your/app && /path/to/ui-annotate/node_modules/.bin/ui-annotate init cursor --writeinit writes the absolute path of the Node.js that runs it, so run it with Node ≥ 22.13 first in
PATH. The daemon starts by itself with the first MCP connection; pnpm exec ui-annotate serve runs
it by hand.
4. Annotate. Open your dev app, click the extension icon (or press Ctrl/Cmd+Shift+F), turn on
annotation mode (Ctrl/Cmd+Shift+1), click an element and type the note. Enter saves.
5. Let the agent work. Ask it to "handle the UI feedback", or simply keep working: the hooks show open annotations in its context. Replies and resolutions appear on the page live.

What the agent receives
get_pending returns a markdown block per annotation plus the same data as JSON. The first line is
the location the agent opens first:
`src/components/PlanCard.vue:17:7` `<PlanCard>` — #1 fix/important [pending] id=a_vOPD0BMk
> Upgrade is the main action here: make it primary, like Invite
- element: `button "Upgrade plan"`
selector: `[data-testid="upgrade"]`
path: `main.content > div.grid > section.card > div.actions > button[data-testid=upgrade]`
text: "Upgrade plan"
parents: App (src/App.vue)
- page: http://127.0.0.1:5180/ (1280×800)The agent works through annotations with MCP tools and the thread shows up in the overlay:

MCP tools
Tool | Parameters | Result |
| — | pages with open and total counts |
|
| ties the page to the agent's repository (or the given absolute path) |
|
| open (pending or acknowledged) annotations; all pages without |
|
| one annotation with its thread |
|
| status |
|
| agent message in the thread |
|
| status |
|
| status |
|
| batch of new annotations or |
The same tools are served over Streamable HTTP at http://127.0.0.1:4848/mcp with the token as
Authorization: Bearer (pnpm exec ui-annotate token). watch_annotations keeps a cursor per MCP
session, so annotations created while the agent was busy come with the next call.
Hooks and the channel
Claude Code and Codex:
SessionStartandUserPromptSubmitadd a digest of open annotations (≤ 10 000 characters) and stay silent when there are none;Stopblocks the end of a turn while annotations of this project are open, once (stop_hook_active).Cursor: the digest comes from
sessionStart(beforeSubmitPromptcannot add context);stopsends a follow-up message at most twice.Claude Code channel (research preview, interactive sessions):
claude --dangerously-load-development-channels plugin:ui-annotate@ui-annotate-localdelivers each new annotation as a<channel source="...ui-annotate...">message without typing anything.
The daemon is shared by all projects. Hooks and the channel show annotations of pages whose project root contains the agent's working directory; pages without a root appear only as a count and never block.
Automatic mode
Popup → This site → Automatic mode hands every new annotation of the site to a background agent run, with no agent session open. Pick the agent and a delay: the run starts after that many quiet seconds, so notes left in a row go into one run.
The daemon starts the CLI in the page's project root and reports in the thread: "working on it", then resolved or dismissed with the agent's summary, a question as a reply, or the failure reason (the annotation stays open). One run per project at a time.
Agent | Command | Limits |
claude |
| read and edit tools only; Bash, web and MCP tools are denied |
codex |
| Codex has no switch for its shell tool: commands run in the sandbox, writes only inside the project, no network |
cursor |
| edits applied; shell commands run in Cursor's sandbox |
The CLIs are looked up in the daemon's PATH; UI_ANNOTATE_CLAUDE_BIN, UI_ANNOTATE_CODEX_BIN and
UI_ANNOTATE_CURSOR_BIN override them. A run stops after 15 minutes. The agent edits your code with
no review step, so turn the mode on for projects under version control.
Project root
The project root of a page makes source paths relative and scopes its annotations to agent sessions in that folder. It is found without setup, in this order of precedence:
the Project root field of the popup (manual override);
link_page(url)called by an agent: the repository it runs in (the Claude Code skill calls it after starting a dev server);the dev server behind a localhost page: the process listening on the port (
/procon Linux,lsofon macOS), its working directory, lifted to the git top level;absolute source paths in the first annotation, lifted to the git top level and accepted only with a project marker (
package.json,angular.json, …).
The home directory, the file system root and processes outside home (proxies, containers) never become a root. The popup shows what was detected and how.
Using the overlay
Action | Default shortcut |
Show / hide everything on the page |
|
Annotation mode on/off |
|
Markers on/off |
|
Resolved and dismissed annotations on/off |
|
Rebind them in the browser: Change shortcuts in the popup opens the shortcut settings.
In annotation mode | How |
Annotate an element | click it |
Several elements | Ctrl/Cmd+click each, then Enter |
Text | select it with the mouse |
Area | drag on a non-text area |
Save / newline / cancel | Enter / Shift+Enter / Esc |
Markers follow their element and fall back to the stored position with a dashed border when it is
gone. Load on every visit in the popup injects the overlay at page load; origins other than
localhost and 127.0.0.1 ask for the host permission first.
Source location
Verified on the dev servers in fixtures/ by apps/extension/e2e/resolve.spec.ts:
Framework | Component | File | Line |
Vue 3 |
|
| template line with vite-plugin-vue-inspector |
React 18 (@vitejs/plugin-react 4) | owner fiber |
| JSX line, corrected through the module source map |
React 19 (@vitejs/plugin-react 6) | owner fiber |
| JSX line |
Svelte 4 / 5 | file name |
| template line |
Angular 22 | component class |
| line of the class declaration |
Solid | — | with solid-devtools | JSX line |
Build-plugin attributes take priority: data-insp-path (code-inspector-plugin), vite-plugin-vue-inspector,
data-locatorjs, data-source-loc. Without any source the annotation still carries the DOM context
and a reason: production-build, no-framework or no-line.
Security model
The daemon listens on 127.0.0.1 only and checks every request:
Hostis127.0.0.1:<port>orlocalhost:<port>(DNS rebinding).Origin, when present, is the extension origin: a web page cannot call the API even with the token. WithoutOrigin,Sec-Fetch-Sitemust be absent,noneorsame-origin.Authorization: Bearer <token>(other local processes and extensions).
POST /pair hands the token only to the extension: the fixed Chrome ID, or the first Firefox install
that pairs (pnpm exec ui-annotate unpair resets it). Overlay inputs accept only trusted user events,
so page scripts cannot write annotations. Page-derived text reaches the agent flattened and marked as
data, and source paths outside the project root are dropped.
Demo
examples/demo is the Vue app of the screenshots. pnpm screenshots (after
pnpm build) starts it with a test daemon, drives the extension in Chrome for Testing and writes
docs/images/*.png.
Development
See AGENTS.md: layout, commands, conventions and how to verify a change. It is written for coding agents and works as a contributor guide too.
Status
Early stage. Not published to the Chrome Web Store, addons.mozilla.org or npm yet: install from
source as above. Tested on Linux with Chrome for Testing 151–156, Google Chrome 155,
Firefox 158 and Zen 1.23; the macOS lsof path of project root detection is untested.
License
This server cannot be deployed
Maintenance
Related MCP Connectors
Read, reply to and resolve website feedback threads from your coding agent.
Serves your design system and coding standards to coding agents, so they stop guessing.
Comment on AI-generated webpages; feedback flows back to your coding agent. Free, MIT, local-first.
Your team's design feedback from Figma, Jira and Notion: ask, search, read, reply, mark done.
Related MCP Servers
- AlicenseAqualityAmaintenanceTurns product feedback pinned to a live UI into an actionable backlog for Claude Code — list comments, open one with its target element's HTML, computed styles and screenshot, and update its status.21955,739,560 npm1MIT
- AlicenseNot gradedqualityBmaintenanceMCP server that lets coding agents receive UI feedback from a browser overlay—clicked elements, selectors, source paths, and computed styles—and manage it with watch, ask, acknowledge, resolve, and dismiss tools.MIT
- AlicenseNot gradedqualityAmaintenanceEnables coding agents to pull structured UI feedback captured in the browser — including element selectors, bounding boxes, computed styles, screenshots, and annotations — and to mark issues as fixed.MIT
- AlicenseNot gradedqualityBmaintenanceEnables coding agents and human reviewers to share a single live view of code, letting the agent open and annotate specific lines while the human marks ranges to ask about, all without any write access to the underlying files.1MIT