annotation-overlay-mcp
The Annotation Overlay MCP server enables AI agents to read, clear, and control visual annotation feedback from a Chrome extension. With read_annotations, you can retrieve all pending annotations (including type, CSS selectors and fallback chains, comments, position, color, DOM metadata) along with paths to before/after screenshots and the tab URL. clear_annotations resets the annotation queue. Additionally, you can trigger a capture of the current viewport as an after screenshot, and toggle the annotation toolbar visibility to enable or disable annotation mode.
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., "@annotation-overlay-mcp read annotations from the overlay and list all issues found"
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.
Annotation Overlay MCP
DOM-aware visual annotation overlay for web feedback. Draw arrows, boxes, text, freehand strokes, click elements, and annotate text selections — all in one toolbar. Submit structured annotations to Claude Code via MCP.
6 interaction modes. Zero JS dependencies. Chrome Extension + MCP Server.
[Browser Page] ←→ [Chrome Extension] ←→ [MCP Server] ←→ [Claude Code]
overlay.js bridge.js HTTP + stdio read/clear toolsQuick Start
1. Install & Start MCP Server
git clone https://github.com/cc10143/annotation-overlay-mcp.git
cd annotation-overlay-mcp
npm install
npm start
# → HTTP server on port 3847 + MCP stdio transport connected2. Load Chrome Extension
Open
chrome://extensionsEnable Developer mode
Click Load unpacked
Select the
extension/directory
The overlay auto-injects on every page, but the toolbar stays hidden during normal browsing (it's a tool for agents, not a resident UI). To annotate, either press Ctrl+Shift+A, or — in the feedback flow — have the agent call set_annotation_mode(true), which shows the toolbar on the active tab.
3. Configure Claude Code MCP
// ~/.claude/settings.json
{
"mcpServers": {
"annotation-overlay-mcp": {
"command": "node",
"args": ["D:/KaiFa/annotation-overlay/server/index.js"]
}
}
}Or use the CLI:
claude mcp add annotation-overlay-mcp -- node D:/KaiFa/annotation-overlay/server/index.jsRelated MCP server: Browser Feedback MCP
Usage
Annotation Workflow
Agent launches the annotation browser (primary) — Claude Code runs the
annotation-browser-launchskill: Playwright opens chromium with the extension + a persistent profile, navigates to the target page, and shows the toolbar. (Fallback: the agent callsset_annotation_mode(true)on the user's own Chrome.)Annotate — use any of the 6 tools to mark issues
Submit — click Submit to send structured JSON to the MCP server. The extension automatically captures a viewport screenshot with annotations overlaid and uploads it; the server saves it to
~/.annotation-overlay/screenshots/. Submit also hides the toolbar and deactivates the overlay.Agent reads — Claude Code calls
read_annotations→ gets annotations +screenshotPath→ feeds the screenshot to a vision-capable tool → processes feedbackFix & verify — after the fix, the agent refreshes the annotation browser (via CDP reconnect), then compares a fresh screenshot against the annotated
screenshotPathto confirm the fix actually landedNext round — the agent re-activates the overlay and repeats
Tools
Tool | Label | How | DOM Link |
Arrow | ➤ | Click & drag → arrow | Element under arrowhead |
Box | □ | Click & drag → rectangle | Region container + element at box center |
Text | T | Click → type text label | Element under click point |
Freehand | ✎ | Click & drag → freeform drawing | Region container + bbox center element |
Select | + | Hover highlights blue → click to pin numbered badge | Clicked element |
TextSel | [ ] | Select page text → annotate instantly | Containing element |
Drawing annotates immediately — no comment prompt interrupts your flow. To add a comment to an existing annotation, switch to the Select tool and double-click the drawing (or the numbered badge). Only the Text tool still prompts, because its text is the annotation itself.
Keyboard Shortcuts
Key | Action |
| Toggle overlay |
| Switch tool |
| Undo last annotation |
| Redo |
| Edit annotation comment (select tool) |
| Show shortcut panel |
| Cancel drawing / close overlay |
| Confirm comment |
| Newline in comment |
Colors
5 preset colors: red #e94560, blue #4080f0, green #2ecc71, yellow #f1c40f, purple #9b59b6.
Screenshot
On Submit, the extension captures the current viewport with annotations overlaid — the canvas strokes and numbered badges are DOM, so they appear in the shot naturally; only the toolbar is hidden during capture. The screenshot is uploaded to the MCP server and saved to ~/.annotation-overlay/screenshots/anno-<timestamp>.png.
read_annotations returns the absolute screenshotPath in its response. Pass it to a vision-capable tool (e.g. a vision MCP) to see exactly what the user is pointing at — this is how a non-multimodal agent gets visual context for the annotations.
For before/after verification, the agent calls capture_page after the fix reloads the page. The extension captures the current viewport (clean — the overlay is inactive until toggled) and the server exposes it as afterScreenshotPath in read_annotations. Feeding both screenshotPath (annotated "before") and afterScreenshotPath (clean "after") to a vision-capable tool lets the agent confirm a fix landed instead of working blind.
Extension only. Without the extension bridge (standalone script injection), submit proceeds without a screenshot.
MCP Tools
read_annotations
Read all pending annotations. The response also includes screenshotPath — the absolute path to the last annotated viewport screenshot (saved on Submit) — and afterScreenshotPath — the absolute path to the last capture_page screenshot (clean post-fix viewport), both for vision-capable consumption.
Each annotation includes:
{
"id": "uuid",
"type": "circle", // arrow | circle | text | freehand | select | textsel
"comment": "user feedback text", // empty until double-clicked in select mode
"selector": "div.card:nth-child(1) > button.btn-primary",
"fallbackSelectors": [
{ "type": "id", "value": "#submit-btn" },
{ "type": "cssPath", "value": "div.card:nth-child(1) > ..." },
{ "type": "contentHash", "value": "Buy Now-a3f8b2c1" }
],
"tagName": "BUTTON",
"classes": ["btn-primary"],
"elementText": "Buy Now",
"contentHash": "Buy Now-a3f8b2c1",
"region": { // box/freehand only: the container element
"selector": "#card", // covering the drawn area (deepest ancestor
"fallbackSelectors": [ // whose rect covers ≥60% of the drawing)
{ "type": "id", "value": "#card" },
{ "type": "cssPath", "value": "div.card" },
{ "type": "contentHash", "value": "Card A-..." }
],
"tagName": "div",
"classes": ["card"]
},
"position": { "x": 100, "y": 200, "w": 200, "h": 100 },
"color": "#e94560"
}screenshotPath — added to the response when a screenshot was captured on submit, e.g. "C:\Users\you\.annotation-overlay\screenshots\anno-2026-08-12T06-27-48.png". Pass it to a vision-capable tool to see the page with annotations overlaid.
afterScreenshotPath — added to the response after an agent-requested capture_page, e.g. "C:\Users\you\.annotation-overlay\screenshots\anno-2026-08-12T06-30-12.png". This is the clean post-fix viewport; compare it against screenshotPath to verify a fix landed. afterScreenshotTabUrl is the URL of the tab that was captured — confirm it matches the page you expected (the capture is of the active tab). All three are cleared by clear_annotations.
httpOwnedExternally — true when another annotation-overlay instance holds the HTTP port (e.g. a stale npm start, or an orphaned MCP child from an abrupt Claude Code shutdown), with externalHttpVersion naming that instance. read_annotations/clear_annotations still work against the shared store, but capture_page/set_annotation_mode may fail until the stale process is killed (netstat -ano | grep :3847 → taskkill //PID <pid> //F). The server no longer crashes on a port conflict — it keeps the MCP tools alive and reports this flag instead.
position shape varies by type (viewport coordinates):
Type | position |
arrow |
|
circle / select / textsel |
|
text |
|
freehand |
|
region (box/freehand only) is the container element that best represents the drawn area — e.g. the card <div> the user circled — with the same fallback chain as selector. Use it to locate the region when the box center happens to sit on a child element.
capture_page
Capture the current page viewport and save it as the "after" state for before/after verification. The server asks the extension (via its existing ~10s badge poll) to capture the active tab of the focused window; the shot is clean — the extension hides any annotation overlay chrome before capturing. Returns the absolute path to the saved PNG plus the URL of the captured tab, e.g.:
{ "ok": true, "path": "C:\\Users\\you\\.annotation-overlay\\screenshots\\anno-2026-08-12T06-30-12.png", "tabUrl": "https://example.com/" }Verify tabUrl matches the page you expect — capture_page captures the active tab, so if the user switched tabs it would show the wrong page (the server can't tell). May take up to ~15s (extension poll + capture). Requires the extension to be loaded. Pair the result with screenshotPath from read_annotations to confirm a fix landed.
set_annotation_mode
Show or hide the annotation toolbar on the active tab, so the user can give visual feedback. The overlay is injected on every page but the toolbar is hidden by default — this is how the agent turns it on before asking the user to annotate, and off after processing:
{ "ok": true, "enabled": true, "tabUrl": "https://example.com/" }The extension relays the change to the page's overlay via its ~10s badge poll. May take up to ~15s. Requires the extension to be loaded and the target page to be the active tab. The user can also toggle manually with Ctrl+Shift+A.
clear_annotations
Clear all stored annotations. Call after processing feedback.
Claude Code Skills
The repo ships two skills (in skills/) that encode the annotation feedback loop. Install them to ~/.claude/skills/ with:
npm run install-skillsSkill | Purpose |
| Primary flow. The agent launches its own annotation browser (Playwright chromium + extension + persistent profile + fixed CDP port), navigates the target page, and shows the toolbar. The agent natively controls navigation/refresh/screenshot afterward. |
| Wait for the user's submission (background watcher polling the annotation count — the MCP server can't push), then |
Edit the skills in skills/ (the repo is the source of truth), then re-run npm run install-skills to sync.
Selector Fallback Chain
When the agent regenerates the page, CSS selectors may break. Each annotation carries a fallback chain:
id —
#element-id(most stable)cssPath —
div.card:nth-child(1) > button.btn-primarycontentHash —
Buy Now-a3f8b2c1(first 40 chars of text + djb2 hash)
The agent should try each fallback in order when resolving elements after page changes.
Standalone Use (Without Extension)
The overlay can be injected into any page via script tag or browser console:
<script src="annotation-overlay.js"></script>Or via Tandem evaluate / Playwright:
// Tandem
tandem_devtools_evaluate({ function: "..." }) // paste annotation-overlay.js contents
// Playwright
await page.evaluate(fs.readFileSync('annotation-overlay.js', 'utf-8'));Public API:
__annotationOverlay.activate() // show toolbar
__annotationOverlay.deactivate() // hide overlay
__annotationOverlay.serialize() // → JSON string
__annotationOverlay.clear() // remove all annotations
__annotationOverlay.submit() // send to MCP server via direct fetchArchitecture
┌──────────────────────────────────────────────────────┐
│ annotation-overlay.js (src/, ~1600 lines) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ Toolbar │ │ Canvas │ │ DOM Bridge │ │
│ │ 6 tools │ │ 2D ctx │ │ elementFromPoint │ │
│ │ 5 colors│ │ DPR │ │ CSS selector gen │ │
│ │ Submit │ │ undo │ │ contentHash │ │
│ └────┬─────┘ └────┬─────┘ └────────┬─────────┘ │
│ │ │ │ │
│ └─────────────┴───────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Annotation Model│ │
│ │ + Serializer │ │
│ │ + Fallback │ │
│ └────────┬────────┘ │
└─────────────────────────────┼────────────────────────┘
│ postMessage
┌─────────────────────────────┼────────────────────────┐
│ Chrome Extension ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ bridge.js │ │ service- │ │
│ │ (ISOLATED) │ │ worker.js │ │
│ │ inject + │ │ badge + │ │
│ │ relay │ │ relay │ │
│ └──────┬───────┘ └──────┬───────┘ │
└─────────┼──────────────────┼──────────────────────────┘
│ │ HTTP (port 3847)
┌─────────▼──────────────────▼──────────────────────────┐
│ MCP Server (Node.js, single process) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Express API │ │ MCP stdio │ │
│ │ POST/GET/ │ │ read_ │ │
│ │ DELETE ann │ │ clear tools │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ └────────┬─────────┘ │
│ ┌────────▼────────┐ │
│ │ File Store │ │
│ └─────────────────┘ │
└────────────────────────────────────────────────────────┘On Submit the extension captures the annotated viewport; the server saves it to
~/.annotation-overlay/screenshots/andread_annotationssurfaces the path viascreenshotPath.
Comparison
Annotation Overlay MCP | Vibe Annotations | Dongke-X/redline | |
Drawing tools | 4 (arrow/box/text/freehand) | None | Full HTML editor |
Click-to-select | Yes (+ badge) | Yes | Full edit |
Text selection annotation | Yes | No | No |
MCP automation | Yes (stdio) | Yes (SSE/HTTP) | No (file-based) |
Selector fallback | id→cssPath→contentHash | source maps | id→cssPath→contentHash |
License | MIT | PolyForm Shield | Apache 2.0 |
Dependencies | 3 (Express + cors + MCP SDK) | Many (WXT, etc.) | Many (React, etc.) |
Configuration
Env Var | Default | Description |
|
| HTTP server port |
License
MIT — Copyright (c) 2026 gaogao
Available Tools
2 toolsclear_annotationsA
Clear all stored annotations. Call after processing feedback to prepare for the next annotation round.
Inputs: no named parameters.
read_annotationsA
Read all pending visual annotation feedback. Each annotation includes a CSS selector, fallback selector chain (id → cssPath → contentHash), comment, annotation type (arrow/box/text/freehand/select/textsel), color, viewport position, and element metadata. Use this to consume user feedback after they press Submit in the overlay.
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 2 tools
read_annotations and clear_annotations have completely distinct purposes: one retrieves feedback, the other removes it. There is no overlap or ambiguity between the two operations.
Both tools follow the same verb_noun pattern (read_annotations, clear_annotations), with clear and predictable naming. The convention is consistent across the entire server.
With only 2 tools, the server feels minimal. While the read/clear pair covers the core workflow, the count is borderline for a standalone MCP server.
The server covers the essential operations for consuming and resetting annotation feedback. There is no per-annotation update or delete, but the batch read/clear model fits the described usage pattern well.
Maintenance
Related MCP Connectors
Client feedback pinned on live sites, Figma frames, PDFs, images and video, for Claude and agents.
Live browser debugging for AI assistants — DOM, console, network via MCP.
Comment on AI-generated webpages; feedback flows back to your coding agent. Free, MIT, local-first.
Give agents eyes on any web page: structured context, and changes explained in plain language.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables visual annotation on web pages for Claude Code, allowing element selection, comment addition, screenshot capture, and structured UI feedback for code fixes via an MCP server.MIT
- AlicenseAqualityCmaintenanceEnables visual browser feedback collection directly into Claude Code. Users can point at elements in their browser and send annotated feedback that Claude can act on immediately.121MIT
- AlicenseNot gradedqualityDmaintenanceEnables drawing annotations directly on local web pages and sending screenshots, DOM data, and annotation context to your IDE agent for precise code edits.64 npm3MIT
- FlicenseAqualityCmaintenanceEnables annotating SVG paper figures in the browser by picking elements or drawing regions, and feeds those annotations back to Claude via MCP so it can edit the underlying SVG or generation scripts, with automatic page refresh on changes.41-