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

@pyreon/mcp

v0.15.0

Published

MCP server for Pyreon — AI-powered framework assistance

Readme

@pyreon/mcp

Model Context Protocol server for AI-assisted Pyreon development. Gives AI coding assistants direct access to Pyreon's API reference, code validation, React-to-Pyreon migration, and project scanning.

Install

bun add -d @pyreon/mcp

Quick Start

bunx @pyreon/mcp    # starts stdio MCP server

IDE Integration

Claude Code

// .mcp.json (project root)
{
  "mcpServers": {
    "pyreon": {
      "command": "bunx",
      "args": ["@pyreon/mcp"]
    }
  }
}

Cursor

// .cursor/mcp.json
{
  "mcpServers": {
    "pyreon": {
      "command": "bunx",
      "args": ["@pyreon/mcp"]
    }
  }
}

Windsurf

// .windsurf/mcp.json (same format as Cursor)
{
  "mcpServers": {
    "pyreon": {
      "command": "bunx",
      "args": ["@pyreon/mcp"]
    }
  }
}

Tools

get_api — Look up any Pyreon API

get_api({ package: "reactivity", symbol: "signal" })

Returns signature, usage example, common mistakes. Covers all @pyreon/* packages.

Example response:

## @pyreon/reactivity — signal

**Signature:**
signal<T>(initialValue: T): Signal<T>

**Usage:**
const count = signal(0)
count()              // read: 0
count.set(1)         // write
count.update(n => n + 1)  // update

**Common mistakes:**
- Using .value instead of calling the signal: count() not count.value
- Forgetting to call signal() when reading in JSX: {count()} not {count}

validate — Check code for anti-patterns

validate({ code: "import { useState } from 'react'" })

Runs two detectors and returns the merged result, sorted by source line:

  • React / coming-from-React mistakesuseState, useEffect, useMemo, useRef, className, htmlFor, onChange on inputs, .value writes on signals, Array.map() in JSX, memo / forwardRef wrappers, React-package imports.
  • Pyreon / using-Pyreon-wrong mistakes (added 2026-Q2):
    • for-missing-by<For each={...}> without a by prop.
    • for-with-key<For key={...}> (the keying prop is by; JSX reserves key).
    • props-destructured({ foo }: Props) => <JSX /> captures props at setup and loses reactivity. Use props.foo or splitProps(props, [...]).
    • process-dev-gatetypeof process !== 'undefined' && process.env.NODE_ENV !== 'production' is dead code in real Vite browser bundles. Use import.meta.env?.DEV.
    • empty-theme.theme({}) is a no-op chain.
    • raw-add-event-listener / raw-remove-event-listener — use useEventListener from @pyreon/hooks for auto-cleanup.
    • date-math-random-idDate.now() + Math.random() ID schemes collide under rapid operations; use a monotonic counter.
    • on-click-undefinedonClick={undefined} (or any on*={undefined}); omit the prop or gate with a ternary.

Each diagnostic carries { code, message, line, column, current, suggested, fixable }. React diagnostics may report fixable: true (handled by the migrate_react tool). All Pyreon diagnostics report fixable: false — no companion migrate_pyreon tool exists yet, so claiming auto-fix here would mislead consumers wiring UX off the flag. The invariant is locked in packages/core/compiler/src/tests/pyreon-intercept.test.ts under "fixable contract".

migrate_react — Convert React code to Pyreon

migrate_react({
  code: `
    import { useState, useEffect } from "react"

    function Timer() {
      const [seconds, setSeconds] = useState(0)

      useEffect(() => {
        const id = setInterval(() => setSeconds(s => s + 1), 1000)
        return () => clearInterval(id)
      }, [])

      return <div className="timer">{seconds}s</div>
    }
  `
})

Response:

import { signal, effect } from '@pyreon/reactivity'

function Timer() {
  const seconds = signal(0)

  effect(() => {
    const id = setInterval(() => seconds.update((s) => s + 1), 1000)
    return () => clearInterval(id)
  })

  return <div class="timer">{seconds()}s</div>
}

diagnose — Parse error messages

diagnose({ error: "TypeError: count is not a function" })

Response:

**Cause:** Signal accessed without calling it — signals are functions.
**Fix:** Call the signal to read its value: count() instead of count.

// Wrong:
<div>{count}</div>

// Right:
<div>{count()}</div>

get_pattern — Fetch a "how do I do X" pattern

get_pattern({ name: "dev-warnings" })   # full body of the pattern
get_pattern({})                          # list available patterns

Reads docs/patterns/<name>.md from the Pyreon monorepo. Each pattern file answers a "how do I do X the right way" question with a correct example, the rationale, and the anti-pattern to avoid. 8 foundational patterns ship today: dev-warnings, controllable-state, ssr-safe-hooks, signal-writes, keyed-lists, reactive-context, event-listeners, form-fields. A misspelled name returns substring-match suggestions. Complements get_api (symbol reference) and get_anti_patterns (catalogue of mistakes).

get_anti_patterns — Browse the anti-patterns catalog

get_anti_patterns({})                        # full list
get_anti_patterns({ category: "reactivity" }) # single category

Parses .claude/rules/anti-patterns.md into per-category listings. Valid categories: reactivity, jsx, context, architecture, testing, lifecycle, documentation, all. Each entry carries a title, a rationale, and — when applicable — a [detector: <code>] tag pairing it with a validate-tool diagnostic. Use it BEFORE writing new code: the catalog is the surface validate enforces reactively.

get_changelog — Recent release notes for a @pyreon/* package

get_changelog({ package: "query" })                   # latest 5 substantive versions
get_changelog({ package: "query", limit: 10 })        # expand the window
get_changelog({ package: "query", since: "0.12.0" })  # only versions newer than 0.12.0
get_changelog({ package: "query", includeDependencyUpdates: true })
get_changelog({})                                      # list every package + latest version

Parses the CHANGELOG.md file (changesets-populated) for the named package. The short slug auto-prefixes @pyreon/"query" and "@pyreon/query" resolve to the same result. Empty "ceremonial" version bumps (pure dependency bumps with no user-facing body) are filtered by default; when the whole history is ceremonial, the tool surfaces a clear "no substantive changes" message. since accepts any semver-ish string changesets emits ("0.13.0", "1.0.0-alpha.3") — when the floor equals or exceeds the latest substantive version, the tool returns a dedicated "no changes since vX" miss message. Complements get_api (current symbol reference) — changelog answers "what changed" while api-reference answers "what is it now".

get_routes — List project routes

get_routes({})

Scans for createRouter([...]) and const routes = [...] patterns.

get_components — List components with props and signals

get_components({})

Returns component names, file paths, props, and signal usage.

audit_test_environment — Scan tests for mock-vnode patterns

audit_test_environment({})                          # risk-grouped report (minRisk: medium)
audit_test_environment({ minRisk: "high" })         # only the riskiest
audit_test_environment({ minRisk: "low", limit: 3 }) # full coverage, 3 per group

Scans every *.test.ts / *.test.tsx file under the packages tree for mock-vnode patterns — tests that construct { type, props, children } object literals or a custom vnode() helper instead of going through the real h() from @pyreon/core. This class of pattern silently drops rocketstyle / compiler / attrs work from the pipeline and was the cause of PR #197's silent metadata drop.

Classification per file:

  • HIGH — mock patterns present, no real h() calls, and no h import from @pyreon/core. At risk: the file has no pathway to exercise the real pipeline.
  • MEDIUM — mock patterns present AND some real h() usage, but mocks outnumber real calls. Spot-check coverage.
  • LOW — either no mocks, or mock count is dwarfed by real usage.

Each entry reports the literal count, helper count (definitions of vnode / mockVNode / createVNode / VNodeMock / makeVNode), real h(...) call count, and whether h is imported. Use the tool BEFORE modifying a test file to see if strengthening is warranted, or AFTER a framework change to audit for regression.

Pyreon Patterns for AI

These are the key patterns the MCP server teaches AI assistants:

State management

// React                              // Pyreon
const [x, setX] = useState(0)        const x = signal(0)
const val = useMemo(() => a+b, [a,b]) const val = computed(() => a()+b())
useEffect(() => { ... }, [dep])       effect(() => { ... })

JSX differences

// React                              // Pyreon
<div className="box" />               <div class="box" />
<label htmlFor="name" />              <label for="name" />
<input onChange={handler} />          <input onInput={handler} />
{condition && <Child />}              <Show when={condition}><Child /></Show>
{items.map(i => <li key={i.id}>)}    <For each={items} by={i => i.id}>{i => <li>}</For>

Component patterns

// React: hooks, re-renders entire component
function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount((c) => c + 1)}>{count}</button>
}

// Pyreon: signals, fine-grained DOM updates (no re-render)
function Counter() {
  const count = signal(0)
  return <button onClick={() => count.update((c) => c + 1)}>{count()}</button>
}

Refs

// React
const ref = useRef<HTMLDivElement>(null)
<div ref={ref} />

// Pyreon: object ref
const ref = { current: null as HTMLDivElement | null }
<div ref={ref} />

// Pyreon: callback ref
<div ref={(el) => { myElement = el }} />

Context

// React
const ThemeCtx = React.createContext("light")
<ThemeCtx.Provider value="dark"><App /></ThemeCtx.Provider>
const theme = useContext(ThemeCtx)

// Pyreon
const ThemeCtx = createContext<string>("light")
<ThemeCtx.Provider value="dark"><App /></ThemeCtx.Provider>
const theme = useContext(ThemeCtx)  // same API

Lifecycle

// React
useEffect(() => {
  const handler = () => { ... }
  window.addEventListener("resize", handler)
  return () => window.removeEventListener("resize", handler)
}, [])

// Pyreon
onMount(() => {
  const handler = () => { ... }
  window.addEventListener("resize", handler)
  return () => window.removeEventListener("resize", handler)  // cleanup
})

Routing

// React Router
<BrowserRouter>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/user/:id" element={<User />} />
  </Routes>
</BrowserRouter>

// Pyreon Router
const router = createRouter({
  routes: [
    { path: "/", component: Home },
    { path: "/user/:id", component: User },
  ],
})
<RouterProvider router={router}>
  <RouterView />
</RouterProvider>

Lists

// React: key on element
{
  items.map((item) => <li key={item.id}>{item.name}</li>)
}

// Pyreon: by prop on For (not key — JSX extracts key specially)
;<For each={items} by={(item) => item.id}>
  {(item) => <li>{item.name}</li>}
</For>

Lazy loading

// React
const Dashboard = React.lazy(() => import("./Dashboard"))
<Suspense fallback={<Loading />}>
  <Dashboard />
</Suspense>

// Pyreon
const Dashboard = lazy(() => import("./Dashboard"))
<Suspense fallback={<Loading />}>
  <Dashboard />
</Suspense>

Islands (partial hydration)

// Server: define island
import { island } from "@pyreon/server"
const Counter = island(() => import("./Counter"), {
  name: "Counter",
  hydrate: "visible",  // load | idle | visible | media(...) | never
})

// Use in page (renders static HTML, hydrates on client)
<Counter initial={0} />

// Client: register islands
import { hydrateIslands } from "@pyreon/server/client"
hydrateIslands({
  Counter: () => import("./Counter"),
})