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

@palbase/kibele-mcp

v0.1.4

Published

MCP server for Kibele — opinionated AI design critic. Analyze screenshots from Claude Code, Cursor, Claude Desktop, etc.

Readme

@palbase/kibele-mcp

MCP server for Kibele — Palaemon's opinionated AI design critic. Brings Kibele into Claude Code, Cursor, Claude Desktop, Cline, Zed — anywhere with MCP support.

Built on top of the Kibele backend's REST API. Sync + async tools, three input modes (file path, URL, drag-dropped base64), desktop notification when async jobs finish.

Why

The Figma plugin owns the designer's moment ("design ready for review"). This MCP brings Kibele into the developer's moment — review your SwiftUI / Compose / React Native screen right inside the IDE, with Hasammeli (the code scout) reading the actual source for code_location precision.

Install

cd kibele-mcp
npm install
npm run build

Run the Kibele backend

This MCP server is a thin client. The backend (/critique/analyze, /critique/flow) must be reachable.

# in the repo root:
npm run start:dev
# → Kibele listening on http://localhost:3000

Wire it up

Claude Code

Add to ~/.config/claude-code/mcp.json (or your project's .mcp.json):

{
  "mcpServers": {
    "kibele": {
      "command": "node",
      "args": ["/absolute/path/to/kibele/kibele-mcp/dist/index.js"],
      "env": {
        "KIBELE_URL": "http://localhost:3000"
      }
    }
  }
}

Cursor

Add to .cursor/mcp.json in your project:

{
  "mcpServers": {
    "kibele": {
      "command": "node",
      "args": ["/absolute/path/to/kibele/kibele-mcp/dist/index.js"],
      "env": { "KIBELE_URL": "http://localhost:3000" }
    }
  }
}

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "kibele": {
      "command": "node",
      "args": ["/absolute/path/to/kibele/kibele-mcp/dist/index.js"],
      "env": { "KIBELE_URL": "http://localhost:3000" }
    }
  }
}

API key (optional)

If the backend has KIBELE_API_KEY set, pass the same value to the MCP server:

"env": {
  "KIBELE_URL": "http://localhost:3000",
  "KIBELE_API_KEY": "your-key-here"
}

Tools

| Tool | Mode | Latency | Use | |---|---|---|---| | kibele_analyze | sync | 100-260s | Single screen, blocks conversation | | kibele_flow | sync | 200-400s | 2-12 screen flow, blocks conversation | | kibele_start | async | <1s | Fire-and-forget. Returns runId. Notifies on done. | | kibele_result | sync | <100ms | Poll a runId for status / result | | kibele_list_runs | sync | <100ms | All runs in this MCP session | | kibele_set_context | sync | <10ms | Required once per session. Sets app category + short description. | | kibele_get_context | sync | <10ms | Read current session context | | kibele_clear_context | sync | <10ms | Forget session context (use when switching projects) | | kibele_set_profile | sync | <500ms | Create / update a persistent backend profile | | kibele_health | sync | <500ms | Backend reachability check |

Session-scoped product context (required)

Before Kibele will critique anything, she needs to know what app she is looking at. The first kibele_analyze / kibele_flow / kibele_start call in a fresh session refuses with KBL_NO_PRODUCT_CONTEXT and a structured message telling the assistant to ask the user for:

  1. Category — fintech / dating / music / health / productivity / etc.
  2. Description — one or two sentences: what the app does, who it is for, what makes it different.

The assistant then calls kibele_set_context({ category, description }) and retries. The context lives in the MCP server process — every subsequent critique in the same session reuses it without re-asking. Restart Claude Code → fresh process → re-asked (this is intentional).

// First-time interaction, paraphrased:
//
//   user: "kibele baksın" + screenshot
//   assistant calls kibele_analyze({ image: ... })
//   tool returns: KBL_NO_PRODUCT_CONTEXT — ask for category + description
//   assistant: "Önce şunu sormam gerek: bu uygulama hangi kategori? Ne yapıyor, kim kullanıyor?"
//   user: "Fintech. Avrupalı çiftler için ortak banka — fatura ve ortak hedefleri bölüştürürler."
//   assistant calls kibele_set_context({ category: "fintech", description: "..." })
//   assistant retries kibele_analyze({ image: ... })
//   → critique runs
//
// Every later kibele_analyze in this session: no questions asked.

Switching projects mid-session: call kibele_clear_context (or just kibele_set_context with new values).

Image input — three forms

Each tool that takes an image accepts ONE of:

// 1) Local file (most common in IDE workflow)
{ "image": { "image_path": "./screenshots/login.png" } }

// 2) Remote URL (Dribbble, Figma export, etc.)
{ "image": { "image_url": "https://example.com/screen.png" } }

// 3) Base64 (when user drag/drops into chat)
{ "image": { "image_base64": "iVBORw0KGgo...", "mime": "image/png" } }
// or with full data URL prefix:
{ "image": { "image_base64": "data:image/png;base64,iVBORw0KGgo..." } }

Workflow examples

iOS dev — review LoginView

In Claude Code:

"Simulator'da LoginView açık, bir screenshot aldım ~/Desktop/login.png. Kibele baksın, bekleyebilirim."

Claude will call:

kibele_analyze({
  image: { image_path: "/Users/you/Desktop/login.png" },
  source_code: "<contents of LoginView.swift>",
  product_profile_id: "my-fintech-app"
})

Async — keep coding while Kibele deliberates

"Kibele bu ekrana baksın ama beklemeyelim, ben başka şeye geçiyorum"

kibele_start({
  mode: "single",
  image: { image_path: "./mock.png" },
  label: "OTP screen v3"
})
// → { run_id: "a3f2b1c8", expected_seconds: "100-260" }

Continue working. macOS notification fires when done. Then:

"Kibele ne dedi? runId a3f2b1c8"

kibele_result({ run_id: "a3f2b1c8" })
// → { status: "done", result: { structural: [...], editorial: [...] } }

Drag-drop screenshot

When you drag a screenshot into Claude Code chat, the image is attached to the message. Claude can extract the base64 and pass it directly:

kibele_analyze({ image: { image_base64: "<extracted from dragged image>" } })

Flow critique

kibele_flow({
  steps: [
    { image: { image_path: "./01-login.png" }, label: "Login" },
    { image: { image_path: "./02-otp.png" }, label: "OTP" },
    { image: { image_path: "./03-kyc.png" }, label: "KYC" },
    { image: { image_path: "./04-home.png" }, label: "Home" }
  ],
  flow_type: "auth"
})

Async behaviour notes

kibele_start returns a runId in <1s, then runs the actual critique in the background of the MCP process. When it settles:

  1. Job state updates in-memory (queryable via kibele_result).
  2. JSON written to ~/.kibele-mcp/runs/<runId>.json (survives MCP restart for that one job).
  3. macOS desktop notification fires (osascript); other platforms append to ~/.kibele-mcp/notifications.log.

The MCP protocol itself has no native async — the assistant is responsible for not auto-polling kibele_result in a loop. The tool descriptions say so explicitly. If a client misbehaves, the worst case is conversation blocks like the sync version would have.

In-memory job state is per-MCP-session. If you restart Claude Code mid-job, the runId is lost from the in-memory map but the persisted file at ~/.kibele-mcp/runs/<runId>.json still gets written when the in-flight HTTP call finishes (provided the MCP process survives — this depends on client lifecycle).

Environment variables

| Var | Default | Purpose | |---|---|---| | KIBELE_URL | http://localhost:3000 | Backend base URL | | KIBELE_API_KEY | — | Sent as x-kibele-key header if set |

Limits

  • 8 images max per single critique
  • 12 steps max in a flow
  • 20MB per image (PNG / JPEG / WebP / GIF)

These mirror the backend's enforcement.

Troubleshooting

kibele_health returns ok: false → Backend not running, or KIBELE_URL wrong. Run npm run start:dev in the repo root and check lsof -iTCP:3000 -sTCP:LISTEN.

Unauthorized → Backend has KIBELE_API_KEY set, MCP doesn't. Add KIBELE_API_KEY to the env block in your client config.

No notification on done → Check macOS System Settings → Notifications → Script Editor / osascript permission. Or tail -f ~/.kibele-mcp/notifications.log.