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

pixi-inspector-mcp

v0.5.0

Published

Model-agnostic MCP connector for the PixiJS Devtool Pro inspector — exposes the live Pixi/Phaser scene graph and Spine/Sprite controls to any MCP client.

Readme

pixi-inspector-mcp

A model-agnostic MCP connector for the PixiJS Devtool Pro browser extension. It lets any MCP-capable client (Claude Code/Desktop, Cursor, Cline, Windsurf, …) inspect the live PixiJS/Phaser scene graph, drive Spine/Sprite controls, read diagnostics, and run a full Chrome DevTools Protocol layer against a running game — even when the DevTools panel is closed.

A browser extension cannot listen on a port, so this is a small local Node process: the MCP server runs a WebSocket server on 127.0.0.1, and the extension dials in as a client. Each MCP tool call becomes an RPC request the extension forwards to the page (or to chrome.debugger for the cdp.* tools).

Install & connect

The connector runs as a local Node process. Pick one setup path, then add it to your client.

Path A — npx

macOS / Linux / Windows without nvm — nothing to install:

claude mcp add pixi-inspector -- npx -y pixi-inspector-mcp

Path B — global install

Required on Windows + nvm (incl. nvm-windows), where bare npx fails with 'pixi-inspector-mcp' is not recognized as an internal or external command — nvm's symlinked layout breaks npx's staged-bin PATH resolution (this affects any npx package, not just this one). It's also a faster startup.

npm i -g pixi-inspector-mcp
claude mcp add pixi-inspector -- pixi-inspector-mcp

The global bin is pinned to the active node version — after nvm use <other>, re-run npm i -g pixi-inspector-mcp. (Or point the client at an absolute node /abs/path/to/dist/index.js, which is version-agnostic.)

claude mcp add accepts --scope user (all projects), --scope project (shared .mcp.json), or --scope local (default). Verify with claude mcp list.

Other clients (Cursor / Cline / Windsurf / Claude Desktop)

Add the mcpServers block to the client's config file — Cursor ~/.cursor/mcp.json (or .cursor/mcp.json), Cline its MCP settings JSON (MCP Servers → Configure), Windsurf ~/.codeium/windsurf/mcp_config.json, Claude Desktop claude_desktop_config.json (manual — Settings → Developer → Edit Config; an agent cannot edit it). Use the npx command for Path A:

{
  "mcpServers": {
    "pixi-inspector": {
      "command": "npx",
      "args": ["-y", "pixi-inspector-mcp"]
    }
  }
}

…or the bare bin for Path B (after npm i -g):

{
  "mcpServers": {
    "pixi-inspector": {
      "command": "pixi-inspector-mcp"
    }
  }
}

Streamable HTTP (optional)

Run the server yourself, then point the client at the HTTP endpoint. Every HTTP call requires the pairing token as a Bearer header (see Security):

pixi-inspector-mcp --http     # serves POST http://127.0.0.1:8790/mcp
{
  "mcpServers": {
    "pixi-inspector": {
      "url": "http://127.0.0.1:8790/mcp",
      "headers": {
        "Authorization": "Bearer <token from ~/.pixi-inspector-mcp/token>"
      }
    }
  }
}

Claude Code HTTP form:

claude mcp add --transport http pixi-inspector http://127.0.0.1:8790/mcp \
  --header "Authorization: Bearer <token from ~/.pixi-inspector-mcp/token>"

Run directly

pixi-inspector-mcp            # stdio (default)
pixi-inspector-mcp --http     # Streamable HTTP on 127.0.0.1:8790
pixi-inspector-mcp --both     # stdio + HTTP

The extension always connects to the WebSocket server on ws://127.0.0.1:8791.

Single instance & multiple clients

The extension dials into one WebSocket server, so only one pixi-inspector-mcp process can own the bridge per machine (it always binds 8791). This shapes how you wire it up:

  • One client (Claude Code or Cursor or Claude Desktop) over stdio — just works; that client launches the server and the extension connects to it.

  • Several clients at once — do not add a stdio launch to each (the 2nd+ process can't bind 8791 and exits, so that client shows the server failed to start / a generic -32000). Instead run one shared server and point every client at the HTTP URL:

    pixi-inspector-mcp --both   # stdio for one client + HTTP for the rest

    then give the other clients the Streamable HTTP config above.

Multiple browsers → one endpoint each

That single server is a hub. Every Chrome profile attaches under its own label and is driven at its own endpoint, so two browsers never fight over the socket:

http://127.0.0.1:8790/mcp          → the single attached browser
http://127.0.0.1:8790/mcp/dev17    → that browser only
http://127.0.0.1:8790/mcp/staging  → that browser only
  1. In each browser, open the PixiJS Pro panel (or the popup) and set "Name this browser". The endpoint it was actually assigned is printed right under the field — use that, not what you typed: labels are slugified (Dev 17dev-17) and a label already claimed by another browser gets -2 appended rather than stolen.
  2. Point each client at that endpoint.

The bare /mcp keeps working unchanged while one browser is attached; with two it returns AMBIGUOUS_INSTANCE listing the labels instead of silently guessing. A label nobody claims returns UNKNOWN_INSTANCE. A stdio client has no URL to put a label in — pass --instance <label>.

Call status to see it all: instances[] (every attached browser + its endpoint), instance (the one this endpoint drives), and endpointBound (whether this endpoint resolves to exactly one browser).

Tab ids are unique only within a browser. Never carry a tabId from one endpoint to another — the other browser doesn't have that tab and rejects it. Requires extension 1.1.0+; an older one attaches as the single legacy browser, and two of those still evict each other (status then reports evictions.flapping with the fix).

Multiple agents → multiple tabs

Within a browser, targeting is allowlist-based, not focus-based: a tab is reachable only once the user ticks "Use this tab for MCP" on it, so status never drifts as you switch windows.

  1. Call browser.targets → every tab with a live agent as { tabId, url, title, engine, active, lastSeen, enabled } (engine is best-effort and may be null until the app initialises).
  2. Pass the chosen tabId to any pixi.* / spine.* / sprite.* / browser.* / cdp.* tool. Omit it and the single enabled tab serves every agent; enable several and an untargeted call returns AMBIGUOUS, so each agent passes its own tabId. A tab that is not enabled returns NOT_ENABLED — the allowlist is a safety guard, so an agent can never touch a tab you didn't opt in (your email, say).

Run one shared server (--both) and let each agent address its own tab — no need to keep a tab focused. This is cooperative addressing (not enforced isolation): two agents driving cdp.* on the same tab still conflict (CDP_ATTACH_FAILED); different tabs are independent.

For browser-level separation (parallel CI, one agent per environment), give each agent its own Chrome profile and address it by label — …/mcp/<label> — against the same shared server (see Multiple browsers above). Separate server processes on distinct --ws-port / --http-port are only needed if you also want separate pairing tokens and separate CDP debuggers.

Seeing which tab is driven. Every pixi.* / browser.* / cdp.* result carries a ↳ target: tab <id> · <title> · <url> line, so the CLI/chat shows which tab each call hit. status reports activeTabId / activeTabUrl (the default target when no tabId is passed). In the browser, the targeted tab flashes a small corner badge with the method name (toggle it off via the panel/popup "In-page activity badge" checkbox).

Troubleshooting: server won't start / -32000 / "disconnected"

-32000 (or failed to start) in the client's MCP list almost always means the server process exited at launch, not an extension fault. Most common cause: the WS port 8791 (or HTTP 8790) is already in use by another pixi-inspector-mcp instance — a second client's stdio copy, or a server you started earlier and left running. The server logs the exact reason to stderr (Cannot start: WebSocket port 8791 is already in use…). Fixes:

  • Use the already-running instance (it owns the bridge), or stop it, or
  • Run a single shared server (--both) and point extra clients at the HTTP URL, or
  • Give this instance its own ports with --ws-port / --http-port.

Remember: the MCP-list status only reflects whether the server started — for the extension state, call the status tool.

Flags / env

| Flag | Env | Default | | --------------------------------- | -------------------------- | ----------- | | --stdio / --http / --both | PIXI_MCP_TRANSPORT | stdio | | --ws-port <n> | PIXI_MCP_WS_PORT | 8791 | | --http-port <n> | PIXI_MCP_HTTP_PORT | 8790 | | --host <h> | PIXI_MCP_HOST | 127.0.0.1 | | --timeout <ms> | PIXI_MCP_TIMEOUT_MS | 15000 | | --allow-origin <o> (repeatable) | PIXI_MCP_ALLOWED_ORIGINS | (none) | | (auto-pairing on/off) | PIXI_MCP_AUTO_PAIR | on |

Two caveats:

  1. Reload — a newly added server is usually picked up only after the client restarts/reloads, not live in the same session.
  2. Status meaning — the client's MCP-list status reflects MCP-server reachability (did the server start + handshake), NOT whether the browser extension is connected. For the extension state, call the status tool.

Extension setup

The extension is not on the Chrome Web Store — load it unpacked. This is a one-time human GUI action; an agent cannot do it and must ask the user.

  1. Clone this repo, then build the extension:
    pnpm install && pnpm run build
  2. Open chrome://extensions, enable Developer mode, click Load unpacked, and select apps/chrome-extension/build.
  3. Open the extension popup and enable the MCP connector (it is off by default — when off, the extension opens no socket and logs nothing).
  4. Start the server (pixi-inspector-mcp …, after npm i -g). The extension auto-connects.
  5. Keep the inspected game tab active/focused so the connector targets it.

Diagnose at any point with the status tool — it needs no extension and reports whether the connector is connected/paired (plus the remedy if not). When the extension is connected it also returns engineDetected (true/false, or null when unknown) — a quick read of whether a Pixi/Phaser engine is found on the page.

Engine detection

The connector finds your app two ways:

  • Explicit global (preferred, deterministic). Expose one canonical global so the inspector has a context-independent handle. app/game/stage/renderer are objects you already create — pick the one group that fits, one line is enough:

    // Most apps — expose the Application / Game (the inspector reads
    // app.stage / app.renderer from it). Pick the one for your engine:
    globalThis.__PIXI_APP_NEW__ = app; // PixiJS — your new PIXI.Application()
    globalThis.__PHASER_GAME__ = game; // Phaser — your new Phaser.Game()
    
    // No Application? Custom renderer + root container — expose both pieces:
    globalThis.__PIXI_STAGE__ = stage; // your root Container
    globalThis.__PIXI_RENDERER__ = renderer; // your PIXI.Renderer
    
    // Already using the official PixiJS devtools? Auto-recognised, no extra line:
    globalThis.__PIXI_DEVTOOLS__ = { app };
    globalThis.__PIXI_APP__ = app;
  • Auto-detection (fallback, always on). With no global, the extension still self-detects: it scans candidate canvases + window for an app, and as a last resort patches the render engine (resolving on the next frame). This works with the DevTools panel closed and needs no project-code change — but it is best-effort, so the explicit global above is the more reliable option.

A setup agent should offer the global first and fall back to auto-detection if you decline. Check status.engineDetected to see if detection already works.

Pairing

The owner-only ext.* tools (extension self-debug) require a paired connection. Two ways, no shared secret is ever embedded:

  • Auto-pairing (default, Trust-On-First-Use). The first time the real extension connects, the server trusts its chrome-extension://<id> origin and remembers it in ~/.pixi-inspector-mcp/paired.json. This is safe because the gate is the WebSocket handshake Origin: a malicious web page reaching ws://127.0.0.1 carries a web origin and can never auto-pair — only extension origins can. No user action needed; the popup shows paired (auto).
  • Manual token. Set PIXI_MCP_AUTO_PAIR=off to require a token instead. The server prints a token on first run (also at ~/.pixi-inspector-mcp/token); provide it via chrome.storage.local.pixiMcpToken (or use the HTTP transport, which reads the token file). The popup then shows paired (token).

pixi.*, spine.*, sprite.*, browser.*, and cdp.* are not gated by pairing — only ext.* is.

Security

  • Binds to 127.0.0.1 only. The HTTP transport additionally enforces a Host-header allow-list (DNS-rebinding protection), an Origin allow-list, and requires Authorization: Bearer <token> for every call.
  • Owner-only ext.* (manifest/storage/logs/reload/targets) is default-deny: any ext.* call on an unpaired connection returns UNAUTHORIZED before any side effect. The token is compared in constant time and is never logged; it is redacted out of ext.storage.get.
  • Opt-in connector. The extension does nothing until you enable it in the popup.

Tools (61)

| Group | Count | Examples | | --------------- | ----- | ------------------------------------------------------------------------------ | | status | 1 | connector/pairing state — needs no extension, never blocks | | pixi.tree.* | 5 | getTree, export, getNode, select, highlight | | pixi.node.* | 2 | getProperties, setProperty | | pixi.app.* | 3 | stats, textures, renderStats | | spine.* | 10 | list, getInfo, setAnimation, setSkin, changeSkeleton | | sprite.* | 1 | getInfo | | browser.* | 5 | targets, reload, navigate, screenshot, eval | | cdp.* | 27 | network.*, console.*, performance.trace, input.*, dom.*, emulate.* | | ext.* (owner) | 7 | info, storage.get/set/remove, logs, targets, reload |

The cdp.* group drives the tab through chrome.debugger (Chrome DevTools Protocol): bounded scene/DOM snapshots, passive network + console capture, time-boxed performance traces (summary on the wire, full trace to a temp file), synthetic input, DOM mutations, and reversible device/CPU/network emulation.

See PROGRESS.md for the frozen WebSocket wire contract and per-phase details.

License

MIT — © 2026 Andrii Karpus. See the bundled LICENSE file. This package is the MCP server for PixiJS Devtool Pro, a modified version of pixi-inspector (© 2015 Bob Fanger, MIT).