mcp-dashboards 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,6 +5,8 @@
5
5
  ### Your AI can talk about data. Now it can show it.
6
6
 
7
7
  [![npm](https://img.shields.io/npm/v/mcp-dashboards)](https://www.npmjs.com/package/mcp-dashboards)
8
+ [![Glama Quality](https://img.shields.io/badge/Glama_Quality-A-brightgreen)](https://glama.ai/mcp/servers/@KyuRish/mcp-dashboards)
9
+ [![Glama Security](https://img.shields.io/badge/Glama_Security-A-brightgreen)](https://glama.ai/mcp/servers/@KyuRish/mcp-dashboards)
8
10
  [![License: FSL-1.1-MIT](https://img.shields.io/badge/License-FSL--1.1--MIT-blue.svg)](LICENSE)
9
11
  [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-support-yellow?logo=buy-me-a-coffee&logoColor=white)](https://buymeacoffee.com/kyuish)
10
12
  [![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub-ea4aaa?logo=github)](https://github.com/sponsors/KyuRish)
@@ -13,8 +15,8 @@
13
15
  <img src="assets/demo.gif" width="400" alt="MCP Dashboards demo" />
14
16
  <br>
15
17
  <a href="assets/dashboard-example.png"><img src="assets/dashboard-example.png" width="115" height="220" alt="Infrastructure Command Center dashboard" /></a>&nbsp;
16
- <a href="assets/chart-catalog.png"><img src="assets/chart-catalog.png" width="115" height="220" alt="Chart Catalog - 45+ chart types" /></a>&nbsp;
17
- <a href="assets/theme-catalog.png"><img src="assets/theme-catalog.png" width="115" height="220" alt="Theme Catalog - 20 themes" /></a>
18
+ <a href="assets/chart-catalog.png"><img src="assets/chart-catalog.png" width="115" height="220" alt="Chart Catalog - 31 chart tools" /></a>&nbsp;
19
+ <a href="assets/theme-catalog.png"><img src="assets/theme-catalog.png" width="115" height="220" alt="Theme Catalog - 21 themes" /></a>
18
20
  <br><sub>Click any thumbnail to see full size</sub>
19
21
  </p>
20
22
 
@@ -24,7 +26,7 @@ We use AI for everything - analysis, reports, strategy. But when it comes to act
24
26
 
25
27
  ## The solution
26
28
 
27
- MCP Dashboards renders interactive charts, dashboards, and KPI widgets directly inside your AI conversation. 45+ chart types, 20 themes, live polling, PNG/PPT/A4 export - all from a single MCP server. No browser tabs, no copy-paste, no context switching.
29
+ MCP Dashboards renders interactive charts, dashboards, and KPI widgets directly inside your AI conversation. 31 chart tools covering 44+ chart subtypes (bar has stacked/drilldown, hero has 11 variants, etc.), 21 themes, 4 visual discovery catalogs, live polling, PNG/PPT/A4 export - all from a single MCP server. No browser tabs, no copy-paste, no context switching.
28
30
 
29
31
  ## Quick Start
30
32
 
@@ -56,9 +58,11 @@ claude mcp add dashboard -- npx -y mcp-dashboards --stdio
56
58
 
57
59
  ```bash
58
60
  npx mcp-dashboards
59
- # Server starts on http://localhost:3001/mcp
61
+ # Server starts on http://127.0.0.1:3001/mcp
60
62
  ```
61
63
 
64
+ Bound to localhost by default. See [Configuration](#configuration) if you need to expose it on your network or allow browser access from a non-localhost origin.
65
+
62
66
  ### Supported clients
63
67
 
64
68
  Works in any MCP Apps-compatible client: **Claude Desktop**, **Claude Web**, **VS Code** (GitHub Copilot), **Goose**, **Postman**, **MCPJam**. ChatGPT support is rolling out.
@@ -77,36 +81,18 @@ No API to learn. Describe what you want in plain English:
77
81
 
78
82
  The AI picks the right tool, formats your data, and renders the chart inline. Click any data point to ask follow-up questions.
79
83
 
80
- ## What makes this different
81
-
82
- Other MCP chart tools generate static images. MCP Dashboards renders **interactive HTML** inside your conversation.
83
-
84
- ### Explore your data in-chat
85
-
86
- - **Hover tooltips** on every data point - no guessing values
87
- - **Click to select** any bar, slice, or point - selections feed back to the AI for follow-up questions
88
- - **Drill-down** into bar charts with breadcrumb navigation
89
- - **Zoom & pan** on geo maps, treemaps, and heatmaps - scroll-wheel zoom up to 12x with hi-res re-rendering
90
- - **Live polling** - real-time charts that auto-update from any API (stock prices, server metrics, crypto)
91
- - **KPI sparklines** - inline trend mini-charts in dashboard metric cards
92
-
93
- ### Meeting in 5 minutes? Export and go
94
-
95
- - **PPT slides** - one-click download as a 16:9 title slide or background-ready image
96
- - **A4 documents** - paginated multi-page export with intelligent page breaks that never cut through a chart
97
- - **PNG** - per-chart screenshots with title and theme baked in
98
- - **CSV** - raw data export from any table
84
+ **Don't know where to start?** Ask *"show me the catalog"* — opens a master dashboard with one live visual tile per customization dimension (a real bar chart for charts, a pie for themes, a progress ring for hero variants, a neon-glowing card for effects). Click any tile, hit Ask, and you'll drill into that sub-catalog.
99
85
 
100
- Ask the AI to build your dashboard. Hit the export dropdown - grab a 16:9 title slide, a background image, or a paginated A4 document. One click, presentation-ready.
86
+ ## Interactive charts, not images
101
87
 
102
- ### 20 themes, 900+ combinations
88
+ Every chart is **interactive HTML** rendered directly in your conversation:
103
89
 
104
- Boardroom gold, neon cyberpunk, forest earth, clinical white. Mix palettes, typography (8 options), and effects (shimmer, neon, glow, glass, lift). Animated gradient titles export correctly to PNG.
105
-
106
- **45+ chart types across 20 themes = 900+ visual combinations.** Every one of them interactive.
90
+ - **Explore in-chat** - hover tooltips, click-to-select (feeds back to the AI), drill-down with breadcrumbs, scroll-zoom up to 12x on maps and heatmaps
91
+ - **Live polling** - real-time charts that auto-update from any API on a timer
92
+ - **Export anywhere** - PPT (16:9 slides), A4 (paginated with smart page breaks), PNG, CSV
107
93
 
108
94
  <details>
109
- <summary><strong>All 31 Tools</strong></summary>
95
+ <summary><strong>All Tools</strong></summary>
110
96
 
111
97
  | Tool | Type | Best For |
112
98
  |------|------|----------|
@@ -142,6 +128,18 @@ Boardroom gold, neon cyberpunk, forest earth, clinical white. Mix palettes, typo
142
128
  | `render_from_json` | Auto-detect | Any JSON data - picks the best chart automatically |
143
129
  | `render_from_url` | URL fetch | Fetches JSON from a URL and auto-visualizes |
144
130
 
131
+ **Discovery / Catalogs** — visual entry points for browsing what's available:
132
+
133
+ | Tool | Shows | When to use |
134
+ |------|-------|-------------|
135
+ | `render_catalog` | Master catalog with 4 live preview tiles | Don't know where to start. Click any tile, hit Ask, drill in. |
136
+ | `render_chart_catalog` | All 31 chart types with mini previews | Looking for the right chart for your data |
137
+ | `render_theme_catalog` | All 21 themes with color/typography/effects | Picking a theme |
138
+ | `render_hero_catalog` | All 11 hero metric variants | Picking a KPI widget style |
139
+ | `render_effects_catalog` | All 5 effect presets, each applied to a real card | Picking an animation/glow style |
140
+
141
+ Typography is still configurable on every chart via `typography=<name>` (8 options listed under [Themes](#themes)) — it just doesn't have its own catalog because all 8 options were visually identical without per-card font loading, which wasn't worth the build for one preview tool.
142
+
145
143
  </details>
146
144
 
147
145
  <details>
@@ -170,7 +168,7 @@ Boardroom gold, neon cyberpunk, forest earth, clinical white. Mix palettes, typo
170
168
 
171
169
  ## Themes
172
170
 
173
- 20 built-in themes. Pass `theme` to any tool.
171
+ 21 built-in themes. Pass `theme` to any tool.
174
172
 
175
173
  | Family | Themes |
176
174
  |--------|--------|
@@ -178,6 +176,7 @@ Boardroom gold, neon cyberpunk, forest earth, clinical white. Mix palettes, typo
178
176
  | **Black/AI** | `black-tron` (cyan neon), `black-elegance` (warm gold), `black-matrix` (green hacker) |
179
177
  | **Forest** | `forest-amber` (autumn), `forest-earth` (terracotta) |
180
178
  | **Sky** | `sky-light` (airy blue), `sky-ocean` (deep navy), `sky-twilight` (sunset) |
179
+ | **Office** | `office-red` (corporate red, white bg - report-ready) |
181
180
  | **Gray/ML** | `gray-hf` (warm yellow accent), `gray-copilot` (teal on dark) |
182
181
 
183
182
  Mix-and-match with `palette`, `typography` (8 options: system, mono, professional, editorial, bold, techno, cyberpunk, luxury), and `effects` (5 presets: none, subtle, shimmer, neon, energetic).
@@ -219,7 +218,37 @@ For public APIs, use the URL directly:
219
218
 
220
219
  Built on [MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps). You ask the AI to visualize data, it calls the right tool, and the chart renders inline in your conversation. Self-contained - zero CDN, zero external requests.
221
220
 
222
- **Requirements:** Node.js 18+ and an MCP Apps-compatible client.
221
+ **MCP Apps supported** (inline rendering): Claude Desktop, VS Code Insiders + MCP Apps extension, Goose, Postman.
222
+
223
+ **No MCP Apps support?** No problem. Every chart response also includes a clickable link to the same interactive chart in your browser:
224
+ - `http://localhost:PORT/chart/{id}` - the clickable link in chat. Backed by a tiny same-machine HTTP server bound to 127.0.0.1, lazy-started on the first preview request.
225
+ - `file:///.../chart-{id}.html` - the persistent shareable artifact. Same HTML bytes, written to disk so you can email, archive, or open it after the server has shut down.
226
+
227
+ Why both: Claude Code (VS Code) strips `file://`, `vscode://`, and `command://` URL schemes from chat markdown, so only `http://` produces a working clickable link. The file:// path is provided for sharing and offline viewing. Tool annotations (`readOnlyHint`, `idempotentHint`, `openWorldHint`) help clients reason about tool behavior.
228
+
229
+ ## Configuration
230
+
231
+ All optional. Defaults are safe — set these only if you need to override.
232
+
233
+ | Env var | Default | What it does |
234
+ |---------|---------|--------------|
235
+ | `MCP_DASHBOARDS_RETAIN_DAYS` | `7` | How long to keep chart preview HTML files on disk. `0` disables auto-cleanup. |
236
+ | `MCP_DASHBOARDS_DISABLE_PREVIEW` | unset | Kill switch — no HTML files written, no preview server started, no preview links. Inline rendering still works in MCP Apps clients. |
237
+ | `MCP_HTTP_BIND_HOST` | `127.0.0.1` | HTTP-transport bind host. Stays on localhost by default. Set to `0.0.0.0` only if you trust your network. |
238
+ | `MCP_CORS_ALLOWED_ORIGINS` | localhost only | Comma-separated origins allowed to call the HTTP endpoint from a browser. Set this to add a non-localhost origin (e.g. `https://your-app.com`). |
239
+ | `MCP_URL_ALLOWLIST` | empty | Comma-separated hostnames that bypass the SSRF guard for `render_from_url` / `poll_http`. Use only for internal endpoints you fully trust. |
240
+ | `MCP_OUTBOUND_RATE_PER_SEC` | `10` | Per-host throttle for outbound HTTP calls. |
241
+ | `MCP_OUTBOUND_BURST` | `20` | How many initial requests can fire immediately before the rate kicks in. |
242
+ | `POLL_PRESET_<NAME>_URL` | — | Server-side preset URL for live polling. See [Live Polling](#live-polling). |
243
+ | `POLL_PRESET_<NAME>_HEADERS` | — | Auth headers (JSON object) for the matching preset URL. |
244
+
245
+ ### What's on disk
246
+
247
+ Chart HTML files are written to `<system temp>/mcp-dashboards/` and auto-deleted after 7 days. The chart's built-in download button (PNG / PPT / A4) is the recommended way to save anything permanently.
248
+
249
+ Ask your AI to *"list chart files"* or *"delete chart files older than 1 day"* anytime — invokes `list_chart_files` / `delete_chart_files`.
250
+
251
+ **Requirements:** Node.js 18+.
223
252
 
224
253
  <details>
225
254
  <summary><strong>Contributing</strong></summary>
@@ -242,6 +271,14 @@ If MCP Dashboards is useful to you:
242
271
  [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-support-yellow?logo=buy-me-a-coffee&logoColor=white)](https://buymeacoffee.com/kyuish)
243
272
  [![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub-ea4aaa?logo=github)](https://github.com/sponsors/KyuRish)
244
273
 
274
+ ## Privacy
275
+
276
+ All processing happens locally. No data is collected, transmitted, or stored externally.
277
+
278
+ External calls only happen when *you* explicitly ask — `render_from_url` and `poll_http` need a URL or preset you provide. Both refuse to fetch private, loopback, or link-local addresses (your `192.168.*` network, AWS metadata at `169.254.169.254`, etc.) so a prompt-injected AI can't quietly reach internal services. They also throttle per hostname so a runaway loop won't get your IP banned from a real API.
279
+
280
+ Credentials in env var presets never leave your machine. The browser preview server and the optional HTTP transport both bind to `127.0.0.1` by default and are not reachable from other devices. Chart HTML files live in your system temp folder and auto-delete after 7 days. See [Configuration](#configuration) to change any of this.
281
+
245
282
  ## License
246
283
 
247
284
  [FSL-1.1-MIT](LICENSE) - Free to use for any purpose except building a competing commercial product. Each version converts to MIT two years after release. For commercial licensing, contact [contact@kyuish.com](mailto:contact@kyuish.com).
package/dist/index.js CHANGED
@@ -12,7 +12,36 @@ async function startStreamableHTTPServer(factory) {
12
12
  const express = await import("express");
13
13
  const cors = await import("cors");
14
14
  const app = express.default();
15
- app.use(cors.default());
15
+ // CORS: default to localhost-only to prevent drive-by attacks from any
16
+ // webpage the user happens to visit (a malicious site could otherwise POST
17
+ // to http://localhost:3001/mcp and invoke our tools - save_file etc.).
18
+ // Override with MCP_CORS_ALLOWED_ORIGINS=https://your-host.com,... for
19
+ // legitimate browser-based access from non-localhost origins.
20
+ const defaultOrigins = [
21
+ `http://localhost:${port}`,
22
+ `http://127.0.0.1:${port}`,
23
+ ];
24
+ const envOrigins = (process.env.MCP_CORS_ALLOWED_ORIGINS ?? "")
25
+ .split(",")
26
+ .map((s) => s.trim())
27
+ .filter(Boolean);
28
+ const allowedOrigins = envOrigins.length > 0 ? envOrigins : defaultOrigins;
29
+ if (envOrigins.includes("*")) {
30
+ console.warn("[mcp-dashboards] WARNING: MCP_CORS_ALLOWED_ORIGINS contains '*' - allowing any origin. This is a serious security risk; only use for trusted environments.");
31
+ }
32
+ app.use(cors.default({
33
+ origin: (origin, cb) => {
34
+ // Same-origin requests / curl / non-browser callers have no Origin header
35
+ if (!origin)
36
+ return cb(null, true);
37
+ if (allowedOrigins.includes("*") || allowedOrigins.includes(origin)) {
38
+ return cb(null, true);
39
+ }
40
+ cb(null, false);
41
+ },
42
+ credentials: false,
43
+ methods: ["GET", "POST", "OPTIONS"],
44
+ }));
16
45
  app.use(express.default.json());
17
46
  app.all("/mcp", async (req, res) => {
18
47
  const server = factory();
@@ -38,8 +67,14 @@ async function startStreamableHTTPServer(factory) {
38
67
  }
39
68
  }
40
69
  });
41
- const httpServer = app.listen(port, () => {
42
- console.log(`MCP Dashboards server listening on http://localhost:${port}/mcp`);
70
+ // Default-bind to 127.0.0.1 so LAN attackers can't reach port 3001 directly
71
+ // and bypass the CORS allowlist (cors only triggers when an Origin header is
72
+ // sent; a raw curl from another machine has no Origin and would otherwise
73
+ // be waved through). Override with MCP_HTTP_BIND_HOST=0.0.0.0 (or any
74
+ // interface) for trusted deployment scenarios.
75
+ const bindHost = process.env.MCP_HTTP_BIND_HOST || "127.0.0.1";
76
+ const httpServer = app.listen(port, bindHost, () => {
77
+ console.log(`MCP Dashboards server listening on http://${bindHost}:${port}/mcp`);
43
78
  });
44
79
  const shutdown = () => {
45
80
  console.log("\nShutting down...");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}