Use when the user mentions "annotator", "ai-annotator", "vite-plugin-ai-annotator", "browser feedback", "select elements in browser", "capture screenshot from browser", "inject CSS/JS", or wants to act on UI feedback that arrived as a `<channel source="ai-annotator">` event. Provides REST API + channel event reference for the live browser session.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-annotator-plugin:annotatorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
`vite-plugin-ai-annotator` injects an annotation toolbar into the browser. The user picks UI elements, attaches comments, then either copies a prompt to clipboard or — when the channel plugin is enabled — pushes a `<channel source="ai-annotator">` event straight into your running Claude Code session.
vite-plugin-ai-annotator injects an annotation toolbar into the browser. The user picks UI elements, attaches comments, then either copies a prompt to clipboard or — when the channel plugin is enabled — pushes a <channel source="ai-annotator"> event straight into your running Claude Code session.
You interact with the live browser session through a REST API on http://localhost:7318 (default). The MCP annotator_* tools that earlier versions exposed are gone — use the REST API.
┌─────────────────┐ Socket.IO ┌──────────────────┐
│ Browser Page │◄─────────────────►│ ws-server │
│ (toolbar UI) │ │ (Express, :7318)│
└─────────────────┘ └────────┬─────────┘
│ REST /api/*
│
│ Socket.IO room "channels"
│
┌────────▼─────────────┐
│ channel.ts (this │
│ plugin's MCP server)│
└────────┬─────────────┘
│ stdio (claude/channel)
┌────────▼─────────┐
│ Claude Code │
│ session │
└──────────────────┘
When the user clicks a "Send to Claude" button in the toolbar, the channel server pushes:
<channel source="ai-annotator" session_id="<uuid>" page_url="<url>" count="<N>">
User submitted <N> feedback item(s) on <url> ("<title>"). Fetch GET http://localhost:7318/api/sessions/<session_id>/feedback?fields=xpath,attributes for details, apply the changes the comments describe, then DELETE the same endpoint. Use notify_user(session_id="<session_id>", ...) to report progress.
</channel>
When you see this event:
GET /api/sessions/<session_id>/feedback?fields=xpath,attributes (add styles,children if you need them)componentData.componentLocation — file path with line number — open the file at that locationcomment describesnotify_user to surface progress in the browser toastDELETE /api/sessions/<session_id>/feedback after the change ships, so the same items don't fire next timeAll endpoints under /api/. Default base URL: http://localhost:7318 (override with the AI_ANNOTATOR_PORT or INSPECTOR_PORT env var).
| Method | Endpoint | Body / Query | Returns |
|---|---|---|---|
GET | /api/sessions | — | Array of BrowserSession |
GET | /api/sessions/:id/page-context | — | {url, title, selectionCount, isInspecting} |
POST | /api/sessions/:id/select | {mode?: 'inspect'|'selector', selector?: string, selectorType?: 'css'|'xpath'} | {success, count, error?} |
GET | /api/sessions/:id/feedback | ?fields=xpath,attributes,styles,children | Array of ElementData |
DELETE | /api/sessions/:id/feedback | — | {success: true} |
POST | /api/sessions/:id/screenshot | {type?: 'viewport'|'element', selector?, quality?} | {success, filePath} (WebP saved to tmpdir) |
POST | /api/sessions/:id/inject-css | {css: string} | {success, error?} |
POST | /api/sessions/:id/inject-js | {code: string} | {success, result?, error?} |
GET | /api/sessions/:id/console | ?clear=true | Array of {type, args, timestamp} |
Session ID: required in path. Use GET /api/sessions to discover. The channel event hands you session_id directly via meta.
notify_user reply toolTwo-way channel exposes one MCP tool:
notify_user(session_id: string, message: string, status?: 'info' | 'progress' | 'done' | 'error')
The toolbar shows a toast prefixed by status icon (✓ done, ✗ error, … progress). Call it to acknowledge receipt, report progress on long fixes, or signal completion. Pass session_id from the inbound channel tag verbatim.
interface ElementData {
index: number
tagName: string
cssSelector: string
textContent: string
selectedText?: string // when user highlighted specific text
comment?: string // user's annotation — the actionable instruction
componentData?: {
componentLocation: string // "src/Foo.vue:42" — open this
componentName?: string
framework?: 'vue' | 'react' | 'angular' | 'svelte' | 'vanilla'
}
// Returned only when requested via ?fields=...
xpath?: string
attributes?: Record<string, string>
computedStyles?: { width, height, fontSize, fontFamily, color?, backgroundColor?, display?, position? }
children?: ElementData[]
}
session_id, page_url, count from tag attributesGET /api/sessions/<session_id>/feedback?fields=xpath,attributes → list of ElementDatacomponentData.componentLocation, apply change driven by commentnotify_user(session_id, "Applied 3 changes", status="done")DELETE /api/sessions/<session_id>/feedbackGET /api/sessions → list sessionsGET /api/sessions/<id>/feedback?fields=xpath,attributes → detailsDELETE /api/sessions/<id>/feedbackPOST /api/sessions/<id>/select with {mode: 'selector', selector: '.btn-primary'} (CSS) or {mode: 'selector', selector: '//button', selectorType: 'xpath'}GET /api/sessions/<id>/feedback?fields=styles,attributes to inspectPOST /api/sessions/<id>/inject-css with {css: '.foo { color: red }'} to testPOST /api/sessions/<id>/screenshot with {type: 'element', selector: '.foo'} to verify visuallysession_id from the channel meta when calling REST. If multiple browsers connected and you call without it, you get an error listing available IDs.componentData.componentLocation ends with :line — use it directly in Read tool (Read("src/Foo.vue", offset: 42)) instead of grep.?clear=true after read to avoid duplicates next call.$TMPDIR/ai-annotator-screenshots/screenshot-<ts>.webp.vite serve, never during vite build — no production overhead.If the user is asking how to install:
bun add -d vite-plugin-ai-annotator
// vite.config.ts
import { defineConfig } from 'vite'
import annotator from 'vite-plugin-ai-annotator'
export default defineConfig({
plugins: [annotator({ port: 7318 })],
})
Then for the channel push experience:
/plugin marketplace add nguyenvanduocit/claude-annotator-plugin
/plugin install claude-annotator-plugin@claude-annotator-plugin
# Restart Claude Code with the channel flag (research preview)
claude --dangerously-load-development-channels plugin:claude-annotator-plugin@claude-annotator-plugin
Channels require Claude Code v2.1.80+ and Anthropic auth (claude.ai or Console API key). Not available on Bedrock / Vertex / Foundry.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub nguyenvanduocit/claude-annotator-plugin --plugin claude-annotator-plugin