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-mcpread 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: claude-annotate
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden. It discloses the destructive nature ('Clear all') and adds workflow context, but does not mention irreversibility, scope (e.g., global vs workspace), or permissions. For a zero-parameter tool this is adequate but not thorough.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with the action stated first. No filler. Every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (0 params, no output schema), the description sufficiently covers its purpose and usage timing. It could mention that clearing is irreversible or affects all annotations, but those are not necessary for basic invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the schema is trivially 100% covered. The description adds no parameter-specific details, but none are needed; baseline of 4 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Clear') and resource ('all stored annotations'), making the action unmistakable. It also distinguishes itself from the sibling tool 'read_annotations' by explicitly focusing on clearing rather than reading.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly states when to use the tool ('Call after processing feedback to prepare for the next annotation round'). While it doesn't discuss alternatives or when not to use it, the context is clear and the single sibling tool is distinct enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must disclose behavioral traits on its own. It describes the data contents in detail, but uses the ambiguous phrase 'consume user feedback' without clarifying whether reading is destructive or marks annotations as no longer pending. Since 'clear_annotations' exists, reading is likely non-destructive, but the description fails to confirm this, creating uncertainty about side effects.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no wasted words. The first sentence is dense, listing all the fields in a compact manner, while the second gives the usage timing. It could be structured with a list for readability, but it remains appropriately concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description thoroughly enumerates the return structure (CSS selector, fallback chain, comment, type, color, viewport position, metadata), which provides strong coverage. However, it leaves the lifecycle of 'pending' annotations ambiguous and doesn't clarify whether reading has side effects. Given the low tool complexity, this omission prevents a higher score.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, making schema coverage 100%. The description adds context about what the read operation returns, which is the relevant semantic contribution when no parameters exist. Baseline for 0 params is 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Read all pending visual annotation feedback' – a specific verb+resource pair. It then enumerates the annotation fields (selector, fallback chain, comment, type, color, viewport position, element metadata), clearly distinguishing it from the sibling 'clear_annotations' which focuses on removal.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The final sentence provides an explicit usage trigger: 'Use this to consume user feedback after they press Submit in the overlay.' It does not explicitly state when not to use it, but with only one sibling (clear_annotations) the contrast is implied, and the timing criterion is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v2.0.0- First observed
clear_annotations - First observed
read_annotations
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
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.
UX-review and product-feedback MCP: annotated screenshots and reviews your AI agent can act on.
Turn any webpage into a structured action manifest — clickable, fillable, submittable elements.
Related MCP Servers
- FlicenseNot gradedqualityAmaintenanceMCP server that exposes web page annotations to AI coding agents, enabling automated implementation of visual feedback and design tweaks.0157-
- 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
- FlicenseAqualityAmaintenanceEnables 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.121-
- FlicenseNot gradedqualityAmaintenanceEnables UI feedback loop by clicking elements, leaving comments, and letting AI coding agents (via MCP) resolve annotations interactively.2-