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

@fusedashlabs/ui9000-mcp

v5.5.5

Published

MCP server that creates charts from your data — bar, line, pie, maps, KPI, network, and more.

Readme

UI9000-MCP

MCP server for creating charts from data. Attach a CSV or pass values in chat, choose a chart type (bar, line, pie, map, KPI, network, and more), and the chart renders in the MCP Apps view — Cursor, Claude Desktop, or any host that supports MCP Apps.

npm: @fusedashlabs/ui9000-mcp
Server key in Cursor / Claude: UI9000-MCP

Add to Cursor, Claude Desktop, or ChatGPT: docs/add-to-chatgpt-cursor-claude.md


Install in Cursor or Claude

Hosted

Streamable HTTP — no local Node process:

{
  "mcpServers": {
    "UI9000-MCP": {
      "url": "https://mcp.ui9000.com/mcp"
    }
  }
}

Restart Cursor after saving ~/.cursor/mcp.json (or this repo's .cursor/mcp.json).

npx (local stdio, hosted data-links)

Node.js 22+. The CLI speaks MCP over stdio. Chart payloads are POSTed to https://mcp.ui9000.com/v1/data-links and widgets GET the signed URL — no mcp.json env, no client token. No clone required.

Cursor

Add to ~/.cursor/mcp.json (or project .cursor/mcp.json):

{
  "mcpServers": {
    "UI9000-MCP": {
      "command": "npx",
      "args": ["-y", "@fusedashlabs/ui9000-mcp"]
    }
  }
}

Restart Cursor. All chart types are registered. Optional: "env": { "MCP_BASE_URL": "http://127.0.0.1:8088" } to save/fetch chart JSON on a local MCP HTTP server instead of https://mcp.ui9000.com.

Claude Desktop

Edit claude_desktop_config.json:

{
  "mcpServers": {
    "UI9000-MCP": {
      "command": "npx",
      "args": ["-y", "@fusedashlabs/ui9000-mcp"]
    }
  }
}

Optional env:

| Variable | Default (npx CLI) | Meaning | | --- | --- | --- | | MCP_BASE_URL | https://mcp.ui9000.com | Hosted save/get. local starts loopback HTTP. A http://127.0.0.1:… URL assumes pnpm mcp:serve is already up | | MCP_UI_RESOURCE_MIME | mcp-app | Claude / Cursor MCP Apps MIME | | PORT | 8088 | Local data-link port when MCP_BASE_URL=local | | STORAGE_DIR | ~/.mcp-ui/data | Local payload files (loopback only) | | MCP_DATA_LINK_SECRET | dev default locally | Host signing secret; not sent by npx |

Attach a CSV in chat, then ask for a chart (see chart-test-kit/prompts.md in the workspace).


Requirements

  • Node.js 22+
  • pnpm (via corepack or global install)
  • Chart widgets: npm @fusedashlabs/widgets (aliased as @ui9000/widgets at build time)

Quick Start (<10 min)

  1. Install:

    pnpm install
    pnpm build:mcp-app
  2. Environment:

    cp env.local .env   # or env.prod / .env.example
  3. HTTP/SSE (signed data-links + local HTTP):

    pnpm run mcp:serve

    Default port from .env (often 8088 or 5173). Expect: MCP Server running over SSE on port …

  4. Stdio (Claude Desktop / Cursor local MCP):

    pnpm run mcp:serve:stdio

    Stdio alone does not serve /v1/data-links. Keep pnpm mcp:serve running (or point MCP_BASE_URL at a deployed HTTP instance) whenever charts need signed payloads. Data-link files live under mcp-ui/.data (package root), not process.cwd(), so Cursor/Claude stdio and mcp:serve share the same store.

Install in Claude Desktop (from a clone)

Prefer npx (see Install in Cursor or Claude (npm)). Clone + stdio is for local development.

Edit claude_desktop_config.json (absolute paths; prefer tsx over pnpm to avoid Corepack version checks):

{
  "mcpServers": {
    "UI9000-MCP": {
      "command": "/absolute/path/to/mcp-ui/node_modules/.bin/tsx",
      "args": ["/absolute/path/to/mcp-ui/src/mcp-stdio.ts"],
      "env": {
        "MCP_BASE_URL": "http://localhost:8088"
      }
    }
  }
}

Or install the .mcpb bundle (see MCPB pack) via Claude’s extension install dialog.

Install in Cursor (from a clone)

Prefer npx (see Install in Cursor or Claude (npm)). Clone + tsx is for local development.

Add to ~/.cursor/mcp.json (or project .cursor/mcp.json). Prefer invoking tsx directly (avoids Corepack/pnpm version mismatch when Cursor spawns the process):

{
  "mcpServers": {
    "UI9000-MCP": {
      "command": "/absolute/path/to/mcp-ui/node_modules/.bin/tsx",
      "args": ["/absolute/path/to/mcp-ui/src/mcp-stdio.ts"],
      "env": {
        "MCP_BASE_URL": "http://localhost:5173"
      }
    }
  }
}

Run pnpm install once so node_modules/.bin/tsx exists. Keep pnpm mcp:serve running for signed data-links.

Match MCP_BASE_URL / PORT to your running mcp:serve. Cursor may show tool results as links/text; full MCP Apps iframe rendering varies by host (see Known limitations).

Prompt examples (copy-paste)

| # | Prompt | Expected tool / chart | |---|--------|------------------------| | 1 | Create a line chart of monthly sales: Jan 10, Feb 20, Mar 15. Title "Sales trend". | generate_simple_single_series_chart → lineChart (inline @ui9000/widgets) | | 2 | Pie chart of share: Alpha 40, Beta 35, Gamma 25. Title "Market share". | generate_pie_chart → pie (@ui9000/widgets) | | 3 | Load this CSV as a dataset then chart revenue by region with a bar chart: (paste small CSV or use load_dataset then chart with datasetId) | load_dataset → generate_simple_single_series_chart / bar |

Screenshot

Add a PNG of a rendered chart in Claude (MCP Apps) at docs/screenshots/claude-chart.png when available. Until then, verify locally: tool result _meta.dataUrl in the MCP App View.

Transport support

| Transport | Command / endpoint | FuseDash | Claude Desktop | Cursor | |-----------|--------------------|----------|----------------|--------| | Streamable HTTP | POST /mcp via mcp:serve | Yes | Supported | Possible via URL config | | SSE | GET /sse via mcp:serve | Yes | Supported | Possible via URL config | | Stdio | pnpm mcp:serve:stdio | No | Primary local path | Primary local path |

P13 verification: automated IT covers list-tools / read ui://ui9000/chart / call chart tool on in-memory, Streamable HTTP, SSE, and stdio (src/mcp/transport.integration.test.ts). Claude Desktop visual check: install via stdio config above (or .mcpb), confirm MCP App View loads; for HTTP use local mcp:serve URL if the host allows remote MCP.

Chart tools advertise _meta.ui.resourceUri: ui://ui9000/chart. CSP connectDomains is MCP_BASE_URL plus Mapbox / PMTiles origins (frameDomains is empty — widgets fetch JSON, no nested charts app). Maps join against GET /geojson/{country,state,county,province,city}.json (centroids) and paint choropleth fills from GET /pmtiles/*.pmtiles (CORS proxy; centroids cannot fill). Country/state/province/county collections are also inlined on the signed data-link so the join does not depend on a second fetch.

Render: @ui9000/widgets in the MCP App View. Unsupported chartType values show the View empty state (no apps/charts iframe).

Rebuild the View after editing src/mcp-app/ or bumping @fusedashlabs/widgets:

pnpm install
pnpm build:mcp-app

MCPB pack (P28)

pnpm pack:mcpb

Produces dist/mcp-ui.mcpb from manifest.json (stdio entry via pnpm + this directory). Requires Node 22 + pnpm on the install host. User config prompts for MCP_BASE_URL.

GitHub release (P29)

Push a tag mcp-v* or v* (or run Release mcpb workflow dispatch) to:

  1. Pack dist/mcp-ui.mcpb and attach it to a GitHub Release
  2. Trigger the existing ci.yml tag path that deploys HTTP/SSE to staging (mcp-v*)

Remote clients: use staging MCP_BASE_URL → GET /sse or POST /mcp, plus /v1/data-links for chart payloads.

Known limitations

| Topic | Detail | |-------|--------| | Map charts | Inline <ui9000-map-chart> — token + /geojson + /pmtiles (CORS proxy) | | Host UI | Claude MCP Apps render ui://; Cursor/VS Code may only show tool text/dataUrl | | Stdio vs data-links | Chart tools need HTTP MCP_BASE_URL reachable for signed payloads | | Widget coverage | Unsupported types show empty state in the MCP App View — no charts iframe | | SSE “reconnect” | GET /sse?sessionId=… returns 400 — clients must open a new SSE session |

Host verification (P31)

Same stdio / HTTP / .mcpb artifacts for every host — no per-host code forks.

| Host | Install path | Tools / data-links | MCP Apps UI (ui://) | Status | |------|--------------|--------------------|----------------------|--------| | Claude Desktop | Stdio or .mcpb | Yes (with HTTP MCP_BASE_URL) | Yes (primary) | Verified path (P13 + IT) | | Cursor | Stdio (~/.cursor/mcp.json) | Yes | Partial / host-dependent | Smoke locally | | VS Code Insiders (MCP) | Stdio or HTTP URL | Expected yes | Depends on MCP Apps support in extension | Not fully verified — re-test after release | | ChatGPT (MCP Apps) | Remote HTTP when host allows | Expected yes | Depends on host CSP / Apps support | Not fully verified — use same HTTP artifact | | Goose | Stdio or HTTP | Expected yes | Host-dependent | Not fully verified — no Goose-specific code |

If a host needs a code branch, fix handshake/CSP in P1 instead of forking.

Production secret check (P04)

# Offline: ensure env.prod (or ENV_FILE) is not the known-dev default — never prints the secret
pnpm check:data-link-secret
ENV_FILE=env.prod pnpm check:data-link-secret

HTTP/stdio entrypoints refuse to start when NODE_ENV/MCP_ENV/ENV_FILE look production-like and MCP_DATA_LINK_SECRET is missing, too short, or a known default (dev-secret-key-for-testing, secret, …).

Known limitations

  1. Build the image:

    docker build --build-arg ENV_FILE=env.prod -t mcp-ui .

    The build process will extract the PORT value from your env.prod file and display it in the logs.

  2. Run the container:

    docker run -p 5173:5173 mcp-ui

    Replace 5173 with the actual PORT value from your env.prod file (shown during build).

    With persistent storage:

    # Windows PowerShell
    docker run -p 5173:5173 -v ${PWD}/.data:/app/.data mcp-ui
       
    # macOS/Linux
    docker run -p 5173:5173 -v $(pwd)/.data:/app/.data mcp-ui

    Run in background:

    docker run -d -p 5173:5173 --name mcp-ui-container mcp-ui

Environment Variables

Create a .env file in the project root with the following variables:

| Variable | Description | Default | |----------|-------------|---------| | PORT | Port for the Express MCP server | 8088 | | MCP_BASE_URL | Base URL for signed data links. npx CLI defaults to https://mcp.ui9000.com | http://localhost:8088 (mcp:serve) | | MCP_DATA_LINK_SECRET | Secret key for signing data links | dev-secret-key-for-testing | | CORS_ALLOWED_ORIGINS | Comma-separated list of allowed origins or * | * | | STORAGE_DIR | Directory for storing chart payloads (relative to project root) | .data | | DATA_LINK_TTL_HOURS | Time-to-live for data links in hours | 24 | | MAX_PAYLOAD_SIZE_MB | Maximum payload size in megabytes for data links | 1.5 | | MCP_UI_RESOURCE_MIME | MCP Apps MIME (mcp-app) | mcp-app (npx) |

Example .env file:

PORT=5173
STORAGE_DIR=.data
MCP_BASE_URL=http://localhost:5173
MCP_DATA_LINK_SECRET=your-secret-key-here
DATA_LINK_TTL_HOURS=24
MAX_PAYLOAD_SIZE_MB=1.5
CORS_ALLOWED_ORIGINS=https://charts.example.com,https://mcp.example.com

API Endpoints

Data Links

Create a data link:

POST /v1/data-links
Content-Type: application/json

{
  "config": { ... },
  "data": { ... },
  "meta": {
    "source": "mcp",
    "expiresInHours": 24
  }
}

Response:

{
  "id": "abc123",
  "dataUrl": "http://localhost:5173/v1/data-links/abc123?sig=...&exp=..."
}

Retrieve a data link:

GET /v1/data-links/:id?sig=...&exp=...

Response:

{
  "id": "abc123",
  "config": { ... },
  "data": { ... },
  "createdAt": "2024-01-01T00:00:00Z",
  "ttlSeconds": 86400,
  "meta": { ... }
}

Health Checks

  • GET /health - Detailed health status with stats
  • GET /health/ready - Readiness probe
  • GET /health/live - Liveness probe

MCP Tools (SSE)

The MCP server exposes tools via Server-Sent Events (SSE):

  • SSE Endpoint: GET /sse and POST /sse/message
  • Alias: GET /mcp and POST /mcp/message

Available Chart Tools

All tools are defined in src/mcp/registerTools.ts and include:

  • Basic Charts: Bar, Line, Lollipop, Area (simple, grouped, stacked, cumulative)
  • Advanced Charts: Sankey, Treemap, Histogram, Bubble, Scatterplot, Boxplot, Punchcard
  • Specialized: SparkLine, SparkArea, KPIs, Map (with multiple layer types)
  • ML/Stats: KS Plot, ROC Curve, Gini Impurity & Entropy, Violin, Partial Dependence, Bias-Variance Tradeoff
  • Q-Q Plot: Scatterplot with reference line option

All tools return an externalUrl UI resource that embeds the charting application with the signed data link.


Storage

Chart payloads are stored as JSON files in the STORAGE_DIR directory (default: .data/):

  • Each data link is saved as {id}.json
  • Files are automatically cleaned up when expired (TTL check on access)
  • Storage directory is created automatically if it doesn't exist

Location: {STORAGE_DIR}/{id}.json relative to the project root (or container working directory).


Development

Scripts

  • pnpm run mcp:serve - Run the MCP Node.js server (Express + SSE)
  • pnpm run dev - Run the React Router UI in development mode
  • pnpm run build - Build the React Router application
  • pnpm run typecheck - Type check the codebase
  • pnpm test - Run tests
  • pnpm run lint - Lint code
  • pnpm run lint:fix - Fix linting issues

Project Structure

src/
  ├── mcp-node.ts          # Express server + MCP initialization
  ├── mcp/
  │   └── registerTools.ts # MCP tool definitions
  ├── api/
  │   ├── healthCheck.ts   # Health check handlers
  │   └── handleDataLink.ts # Data link API handlers
  └── utils/
      ├── dataLinkStore.ts # Filesystem-based TTL storage
      ├── dataLinkHandlers.ts # CORS, validation, rate limiting
      └── createChartData.ts # Helper for chart data links

Docker Details

Build Process

The Dockerfile:

  1. Extracts the PORT value from the specified ENV_FILE during build
  2. Displays the extracted port in build logs
  3. Copies the environment file as .env for runtime use
  4. Runs the MCP server with pnpm run mcp:serve

Build Arguments

  • ENV_FILE - Environment file to use (default: env.prod)

Example Build & Run

# Build with custom env file
docker build --build-arg ENV_FILE=env.prod -t mcp-ui .

# Check the PORT value in build logs (look for "PORT extracted from env.prod: XXXX")

# Run with the extracted port
docker run -p 5173:5173 mcp-ui

# Run with persistent storage
docker run -p 5173:5173 -v ${PWD}/.data:/app/.data mcp-ui

# Run in background
docker run -d -p 5173:5173 --name mcp-ui-container mcp-ui

# View logs
docker logs mcp-ui-container

# Stop container
docker stop mcp-ui-container

Security Notes

  • Use a strong MCP_DATA_LINK_SECRET in production
  • Restrict CORS_ALLOWED_ORIGINS to specific domains instead of *
  • Adjust DATA_LINK_TTL_HOURS based on your data retention requirements
  • Rate limiting can be enabled in src/utils/dataLinkHandlers.ts

FAQ

Q: Where are data files stored?
A: In the STORAGE_DIR directory (default: .data/) relative to the project root. Files are named {id}.json.

Q: How are charts rendered?
A: MCP Apps View (ui://ui9000/chart) plus _meta.dataUrl. There is no apps/charts iframe.

Q: How do I enforce strict CORS?
A: Set CORS_ALLOWED_ORIGINS to a comma-separated list of allowed domains: CORS_ALLOWED_ORIGINS=https://example.com,https://other.com

Q: What port does the server use?
A: The port is read from the PORT environment variable (in .env file). Default is 8088.

Q: How do I check if the server is running?
A: Visit http://localhost:<PORT>/health or use the health check endpoints.


Publish to npm

From this repo (must be logged in to an npm user/org that can publish @fusedashlabs/*):

pnpm install
pnpm build:npm
npm publish --access public

CI: push a v* / mcp-v* tag or run the Publish npm workflow. Set GitHub secret NPM_TOKEN.

After publish, Cursor/Claude use npx -y @fusedashlabs/ui9000-mcp with server key UI9000-MCP (see top of this README).


License

Apache-2.0