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

@kovaldev/core-mcp

v0.1.0

Published

Koval Core as an MCP server — decide a design system with an agent, then emit global.css and theme-rules.md

Readme

@kovaldev/core-mcp

Koval Core as an MCP server — decide a design system with an agent, then emit global.css and theme-rules.md.

{
  "mcpServers": {
    "kovalCore": { "command": "npx", "args": ["-y", "@kovaldev/core-mcp"] }
  }
}

What it is for

Starting a site for a client and settling its design system in conversation: palette, type, shape language, motion. The builder at core.koval.dev/theme is where a person goes to understand the approach and tune by hand; this is the same system with an agent driving.

The division of labour

The framework supplies the vocabulary and the constraints. The agent supplies the judgement. Serif for a law firm, quiet motion for a clinic — that call is the model's, and a curated list of "approved themes" would only get in the way of it. What the server refuses to do is let a choice land outside the vocabulary, or let six colours be picked by hand and hoped over.

Neither half works alone. An agent with no framework invents tokens from nothing; a framework with no agent is a form to fill in.

So the tools do not answer what should this site look like. They answer what are the choices, what does each one commit you to, is this particular answer valid — and then turn a decided config into files.

Tools

| Tool | What it does | |---|---| | describe_system | The framework itself: every axis, its options, and what each commits you to. Call this first — it is the vocabulary the other tools expect | | propose_theme | Reads a plain-language brief into candidates for every axis, ranked by claimed signal and carrying the when / avoid that justify each. Not a theme — see below | | generate_palette | Derives six brand roles from one anchor by hue geometry, repairs the ink ladder against real contrast obligations, and reports what it moved and why. Omit the anchor and one is chosen for the mood | | search_fonts | Narrows the 2,096 Fontsource families to a shortlist, by name, category, or variable-only | | search_icon_sets | The 193 monotone Iconify collections. Coloured sets are never offered — they cannot inherit currentColor, so a theme would never reach them | | validate_theme | Accepts or refuses a config, naming the offending field | | emit_files | Turns a decided config into global.css and theme-rules.md, plus the full install line and the Astro wiring |

emit_files returns text and does not touch the filesystem. The agent already has file tools and knows where the project root is; a server writing paths it inferred is a worse failure mode than one that hands back a string.

The score is not a verdict

propose_theme ranks options by how many of the brief's signals each one claims. That is an ordering for reading, not an answer — and the difference matters.

Give it "law firm, established and trustworthy, but modern" and radius comes back with sharp and subtle tied. The tie is the useful part: subtle's criterion reads "the right answer when the brief pulls in two directions — professional but not cold", which is precisely this brief, and it is not the option a scoring system would have picked for you.

So every candidate arrives with its when and avoid. Read them, choose, and be able to say why in those terms. A brief the vocabulary cannot read comes back with every option at zero and says so, rather than ranking them arbitrarily and looking confident.

emit_files then reports coherence notes — combinations that are individually defensible and jointly odd. Hairline borders with elevation draws every card edge twice; a lift on hover with no transition snaps rather than lifts. Not validation, since a designer may want either on purpose, but they are the mistakes that look fine one axis at a time.

Propose and validate, not whitelist

Font and icon choice is not confined to a curated list. The catalogues split the problem where it wants to be split:

  • Mechanically checkable, so the framework does it. Does this family exist, is it variable, which package carries it. Is this icon set monotone, what grid, what licence.
  • Aesthetic, so you do it. A serif for a law firm; a thin stroke against sharp corners. Iconify's own style vocabulary is four tags across 236 collections and will never tell you that Phosphor has rounded terminals — a model knows that, and a whitelist would only get in the way.

So propose anything from the whole catalogue; the resolver checks it and refuses with a reason. emit_files derives the package name and the CSS stack from the catalogue entry, including the Variable suffix a variable family registers under, which is the one part of the mapping you would get wrong by hand.

Two things it will not do

Invent colours. generate_palette computes five roles from one anchor and repairs them against contrast obligations — text-dark/55 on the ground has to clear 4.5:1, and if the envelope and hue you asked for cannot manage it, the ladder moves and the audit says so. Six hand-picked hex values carry none of those guarantees, and they are not recoverable afterwards.

Constrain layout. There is no Container, Stack or Grid primitive to compose with; page structure stays stock Tailwind utilities in the markup. The conventions fix the vocabulary, not the composition.

Output

Two files, and only one of them is reading material.

  • theme-rules.md (~2,700 tokens) goes in the project root, pointed at from AGENTS.md so it is in context every session. This is what the agent reads.
  • global.css replaces the project's stylesheet. It is a build input — Tailwind compiles it. No agent should ever load it into context.

emit_files also returns setup: the whole npm i line, the astro.config.mjs snippet, and the root layout. The stylesheet opens with @import "tailwindcss" and @plugin "@tailwindcss/typography", so a project missing either fails to build — and the error names the plugin rather than the file that asked for it. Listing every package is what makes the output a foundation you paste rather than a file you then debug.

Fonts arrive through the Astro Fonts API, not as Fontsource packages. setup.astroConfig carries a fonts: [...] entry per family and setup.layout the <Font /> tags that go in the <head>; Astro fetches the faces at build time, self-hosts them, and generates a metric-matched fallback. So there is nothing to npm i for type, and nothing to remember to import. A family is asked for only at the weights the scale uses (400–700) and only with italics if it has them.

theme-rules.md carries the same block, which is the point: the file that names a family is the file that says how to get it.

Development

npm run start              # run from source
npm run build              # bundle to dist/ for publishing
node --test ../mcp/test/*  # from the repo root: npm test

The server is deliberately thin: every generator it calls lives in src/lib/*.mjs in the main repo and is pure ESM with zero Node imports, because the same code runs in the browser inside /theme. The bundle carries those modules rather than depending on a published version of them — there is no release the server could drift from, because it is built from the tree it lives in. mcp/test/server.test.mjs asserts the emitted files match the builder's byte for byte.