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

@skyelight/mcp

v0.4.1

Published

MCP server for Skyelight — read feedback items from your project as an agent

Readme

@skyelight/mcp

An MCP server that lets a coding agent read the feedback people left on your running app — the thread, the page, and the element they were pointing at — and report back on it when the work is done.

Works with anything that speaks MCP over stdio: Claude Code, Cursor, Grok, Codex.

Setup

Get a personal access token. In the Skyelight web app, go to your account settings → API Keys → create one. It is shown once, and you can give it an expiry.

The short way

npx @skyelight/mcp init

Finds your coding agent, registers the remote server with it, and leaves the sign-in to OAuth — nothing is pasted and no key is stored on disk. It shows what it will change and waits; --yes skips that once you have read it.

It knows Claude Code, Cursor, Grok, Codex and Windsurf. Name one with --client cursor when more than one is installed, and point at another deployment with SKYELIGHT_URL=https://…. The deployment tells the installer its own MCP URL and OAuth client over /mcp/install-config, so this package holds no environment-specific constants.

The token is yours, not a service account's: anything the agent writes is attributed to you, marked with the tool it came through. That is deliberate — see Who a write belongs to.

Then add the server to your client:

{
  "mcpServers": {
    "skyelight": {
      "command": "npx",
      "args": ["-y", "@skyelight/mcp"],
      "env": {
        "SKYELIGHT_API_URL": "https://<deployment>.convex.site",
        "SKYELIGHT_API_TOKEN": "sky_..."
      }
    }
  }
}

Claude Code users can skip the file:

claude mcp add skyelight \
  --env SKYELIGHT_API_URL=https://<deployment>.convex.site \
  --env SKYELIGHT_API_TOKEN=sky_... \
  -- npx -y @skyelight/mcp

The API host is the Convex deployment with .convex.cloud swapped for .convex.site — Convex serves HTTP actions from the latter. The web app shows the right one when you create the token.

Credentials

Checked in order, so an existing setup keeps working:

  1. SKYELIGHT_API_TOKEN / SKYELIGHT_API_URL in the environment
  2. .env.local, then .env, in the working directory
  3. ~/.skyelight/credentials{"apiUrl": "...", "token": "sky_..."}

Binding a repo to a project

If you can reach several projects, drop a .skyelight.json at the repo root:

{ "projectId": "j57abc...", "projectName": "Checkout rebuild" }

Tools then default to that project and nobody has to pass an id. Without it, list_items will ask you to call list_projects first rather than guess.

Tools

| Tool | What it is for | | ----------------- | ----------------------------------------------------------------------- | | list_workspaces | Which workspaces this credential can reach, and who you are. | | list_projects | The projects inside them, with the ids the other tools take. | | list_items | What is outstanding. Filter by page, type, status, assignee. | | search_items | Find items whose thread mentions some text, replies included. | | get_item | Everything needed to work one item: thread, page, anchor, code, images. | | post_update | Report back on the thread the feedback came from. | | create_item | Raise a new item — an audit finding, something you noticed. | | set_status | Move an item to open, deferred or resolved. |

list_items leads with a summary — totals, breakdown by type and by page — so an agent can tell you the shape of the work before pulling any of it. Rows are stubs; get_item is where the thread and the anchor live.

"What is assigned to me." list_workspaces returns your own user id, and list_items takes assignee: "me", so the most ordinary question anyone asks an agent needs no id looked up by hand.

Merged duplicates do not appear twice. When somebody merges five reports of one problem in the web app, the duplicates leave the list and the item they were merged into says 5 reports. A list that shows all five reads as five bugs.

What the agent sees

Alpha: 21 items total — 15 open, 3 deferred, 3 resolved, 19 unassigned.
By type: bug (7), idea (4), feedback (10).
Busiest pages: /checkout (8), /settings (4).

2 matches:
- The pay button does nothing on the second click
  bug · open · /checkout · unassigned · 2 replies · 5 reports — id i1

Deliberately prose rather than JSON. A tool that returns a bare array invites a model to read the array out; this one invites it to summarise.

A row's line is the one-sentence summary Skyelight's classifier wrote, where there is one, rather than the first 140 characters of whatever somebody typed — four truncated paragraphs are not something you can choose between.

One call reads the 2,000 most recent items in a project. Past that the result says so, in words, rather than presenting a slice as the total.

The pictures

get_item returns the screenshot taken when the pin was left, and any images people attached to the thread, as image content blocks — things a model can actually look at, not links it cannot open.

Screenshot of the page when this was pinned: attached below.
2 images attached to this thread: also below.

Capped at four per call. Anything too large to inline comes back as its link with the reason, so a picture never disappears silently.

What somebody else already did

get_item also names the Linear issue a thread was handed to, and who it was delegated to, so an agent does not start work a Linear-hosted agent is finishing. Reactions come back counted by emoji — a thread with one comment and nine thumbs up is not a thread with one comment.

Where the code is

If the app was built with @skyelight/build, get_item ends with the line that changes what an agent does next:

Written by components/Card.tsx, line 88, in build a1b2c3d on branch feat/checkout.

The file saves a grep. The commit and branch are how an agent tells "still broken" from "already fixed, this pin is stale" — and, on a preview deployment, which branch the code it wants is actually on.

Who a write belongs to

Every write is a person's. A token carries your access, so a reply the agent posts is yours, with the tool it came through shown as a mark beside the timestamp rather than as the author.

There is no service-account mode. Synthetic agent identities existed and were removed: a workspace should not grow a member for every tool somebody plugs in, and a reply nobody is accountable for is worse than one attributed plainly.

Workspace API keys (sk_live_...) used to be a second way in, for scripts and CI. They are gone — a credential that outlives whoever minted it is one nobody is accountable for, and the write tools already refused them for exactly that reason. A personal token is the only credential the API accepts.

Permissions

The server enforces nothing; the API does. Whatever your account can do in the web app, the agent can do through your token, and nothing more:

  • Owner, admin, collaborator and reviewer all read and write — a reviewer can reply on a thread and raise one, which is what a client seat is for.
  • A reviewer can only set_status on threads they opened themselves. Every other role can move anyone's. It is the rule the web app already applies: closing someone else's report is a decision about their report.
  • A project-bound credential answers 404 outside its project rather than reporting that something exists but is forbidden.
  • Workspaces your plan or seat does not cover are listed and marked rather than hidden, with the reason, so a client can see the workspace they know exists and be told which gate closed instead of being told it is missing.