npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hyzer-labs/annotate

v0.2.0

Published

Annotate any page in the browser and send the element, screenshot, and your comment to your local coding agent over MCP.

Downloads

395

Readme

hyzer-annotate

Click an element in your browser, type what's wrong with it, and your coding agent gets the comment, the element metadata, and a screenshot — anchored to the thing you actually pointed at.

Works with any agent that speaks MCP. Nothing leaves your machine.

Install from the Chrome Web Store

The connection pill and the annotate composer over a selected heading, with the element's selector filled in

How it works

The MCP server binds a port on 127.0.0.1 (39280-39300) when your agent starts it. The extension scans that range to find running sessions, you connect a tab to one, and each annotation you submit is POSTed to that session's server. The agent drains the queue when you ask it to.

There's one server process per agent session, so the session picker lists one entry per running agent.

Setup

Two halves: the MCP server your agent talks to, and the browser extension. The server comes from npm, the extension from the Chrome Web Store.

The MCP server

For Claude Code, register it once at user scope so it's available in every project you annotate:

claude mcp add hyzer-annotate --scope user -- npx -y @hyzer-labs/annotate

Or per-project, in that project's .mcp.json:

{
  "mcpServers": {
    "hyzer-annotate": {
      "command": "npx",
      "args": ["-y", "@hyzer-labs/annotate"]
    }
  }
}

User scope is usually the better choice. Each agent process binds its own port and the extension discovers whichever ones are running, so registering it globally costs nothing and saves you repeating this per repo.

Restart the agent after adding it.

npx caches by version, so it will keep running whatever it first fetched. Pin with @hyzer-labs/annotate@latest if you'd rather it check each time, or install it globally (npm i -g @hyzer-labs/annotate) and register the hyzer-annotate binary directly.

The extension

Install it from the Chrome Web Store. Works in Chrome and other Chromium browsers — Brave, Edge, Arc.

Or build it from source:

git clone https://github.com/hyzerlabs/annotate.git
cd annotate
npm install
npm run build:extension

Then open brave://extensions (or chrome://extensions), enable Developer mode, click "Load unpacked", and select the extension/ directory.

Optional: a /fb shortcut

Asking your agent to "check annotations" works fine. A slash command is shorter — /fb, for feedback — and this one groups the queue into tasks and gets them agreed before it edits anything. Save it as ~/.claude/commands/fb.md:

---
description: Read the browser annotation queue and turn it into an agreed plan
---

Call `get_annotations` to read the browser annotation queue. It empties the
queue as it reads, so hold on to what comes back — a second call returns
nothing.

For each annotation: the comment is the ask, the selector and element metadata
say where, and the screenshot path is there when you need to see it — read it
if the comment is visual ("this looks wrong", "spacing is off"), skip it if the
comment already tells you what to change.

Then, before editing anything, propose a plan:

- Group the annotations by the area they touch (component, page, module), and
  label each group by type — bugfix, nit, feature, question, unclear.
- Turn each group into one task. Annotations on the same component belong in
  one task, not one per annotation.
- Order them: bugs before nits, and anything the user has to answer first.
- Show the list with a one-line summary per task and the annotations feeding
  it. Ask the user to confirm, drop, or reorder, and to answer anything you
  marked unclear.

Once they confirm, put the agreed tasks on the todo list and work them one at
a time. Finish a task, say what changed in a line or two, then start the next
— so the user can redirect between tasks instead of after all of them. If
their feedback changes the remaining plan, update the todos and say so.

$ARGUMENTS

Then /fb reads the queue and proposes a plan, or /fb just summarize, don't edit to steer it.

Claude Code with /fb typed at the prompt, showing the command's description

Use

  1. Click the extension icon and connect the tab to your agent session.
  2. In the in-page pill, choose either:
    • Annotate (Alt+Shift+A) — click the element you want to talk about, then comment on it.
    • Capture (Alt+Shift+C) — comment on the page as a whole, no element picking.
  3. Submit. Repeat as many times as you like.
  4. Tell your agent to check the annotations, or run /fb if you added the command above. Either way it calls get_annotations, which hands over everything you've queued and empties the queue.

There's no "send" step to remember. Every annotation POSTs the moment you submit it, and the agent collects the whole queue in one call.

The rest of the pill: click Connected: name to switch the tab to a different agent session, the wrench for settings, and the × to disconnect. It also disconnects on its own when you close the tab, navigate to a different origin, or stop the agent.

The pill is draggable — drop it wherever it's out of your way and it stays there across navigation.

Screenshots

Every annotation carries one unless you clear Include screenshot in the composer. Annotate crops to the element you picked, with a little padding for context. Capture stitches the whole scrollable page together from viewport captures. Either way the image is written to a temp directory and handed to the agent as a file path, so it only loads the image when it needs to.

The pill and the settings popup hide themselves during capture so they stay out of the shot — but only when they'd actually be in frame, and never when the thing you're annotating is one of them.

Two things worth knowing about full-page capture:

  • captureVisibleTab is rate-limited to 2 calls/sec, so a tall page takes a beat, and stitching stops at 12 viewport-heights. The agent is told when a shot was truncated.
  • Fixed and sticky elements are hidden after the first band. Otherwise a sticky header gets stamped into every strip. A sticky sidebar will leave a gap where it was.

Images are swept once they're more than six hours old, rather than when the queue drains. The agent reads those paths after get_annotations returns, so deleting on drain would hand it dead paths. Anything still queued is never swept, however old it gets.

Settings

The wrench in the pill opens a popup beside it, holding three things:

  • Keep queue across restarts — see below.
  • Annotation outline — the colour of the box that follows your cursor while picking an element. Left alone it follows the light/dark theme; Default puts it back after you've picked a colour.
  • Keyboard shortcuts — the current bindings for annotate and capture. Chrome owns extension shortcuts, so Change shortcuts hands you to chrome://extensions/shortcuts, the only place they can be rebound. If another extension already claimed Alt+Shift+A or Alt+Shift+C, yours will show as Not set until you assign one there.

The outline colour is stored in the browser. The queue setting belongs to the agent's server, so it follows the session rather than the browser.

Keeping the queue across restarts

The queue lives in memory, so restarting your agent discards anything you haven't asked it to read yet. Tick Keep queue across restarts in settings to write it to the runtime directory instead. The setting is stored there too, since a toggle that reset on restart would forget at exactly the wrong moment.

It's off by default. It applies to every project rather than per-repo, being a preference about your machine rather than about any one codebase, and the saved copy is discarded as soon as the agent drains the queue.

Set HYZER_ANNOTATE_DIR to move the runtime directory somewhere else.

Development

npm run check   # typecheck, tests, extension build

npm test runs four suites:

  • geometry.mjs — capture and placement arithmetic: crop clamping, zoom scaling, band planning, stitch height, and the composer's flip-above-when-there's-no-room-below.
  • token-scope.mjs — runs the :root to :host rewrite over the real installed @hyzer-labs/ui stylesheet, so an upgrade that adds a :root selector fails here instead of silently shipping unstyled overlays.
  • smoke.mjs — boots the server and exercises the HTTP contract the extension depends on: discovery, claiming, screenshot decoding, queueing, input rejection. Then drains over MCP to check every annotation shape formats correctly.
  • persistence.mjs — restarts the server for real, which is the only way to prove the queue survives one.

The server tests run against HYZER_ANNOTATE_DIR. Settings and the saved queue are shared by every server for a user, so without an override the suite would rewrite your real state.

Packaging

npm run icons              # regenerate icons/*.png from icon.svg (macOS only)
npm run package:extension  # build and zip into release/ for the Chrome Web Store

The extension version comes from package.json and is stamped into the manifest at build time. extension-src/manifest.json has no version field at all, so there's no second copy to forget on a release.

Icons are committed under icons/ rather than generated during the build. Rasterizing needs sips, which is macOS-only, so generating them at build time would quietly ship an iconless extension on Linux and in CI. npm run build:extension fails outright if they're missing.

License

GPL-3.0-only. This is a modified derivative of opencode-chrome-annotation by Benjamin Shafii. See NOTICE for what changed.