@slickfast/mcp 0.7.3 → 0.7.5
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 +31 -22
- package/dist/index.js +167 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,6 +34,14 @@ render_chart({ type: "bar", data: { labels: ["Q1","Q2","Q3"], series: [{ values:
|
|
|
34
34
|
of any other type, composited into one image in a single render.
|
|
35
35
|
- **`describe_type(type)`** → the exact data shape, a minimal working spec, and per-type
|
|
36
36
|
gotchas. Call it first when you're unsure how to structure a type.
|
|
37
|
+
- **`gallery()`** → a curated demo gallery of example charts and dashboards — each as a
|
|
38
|
+
rendered image plus its spec. Just ask *"show me a demo"* or *"what can you make?"*.
|
|
39
|
+
`gallery({board:"comparison"})` tiles a whole family into one image; `board:"all"` shows
|
|
40
|
+
every type across 6 boards.
|
|
41
|
+
- **`list_palettes()`** → every valid `palette` name, grouped into flat palettes and nested
|
|
42
|
+
themes, with their colors. Ask *"what palettes are available?"*.
|
|
43
|
+
- **`report_issue(summary, spec?)`** → *"report this bug."* Formats a bug report and returns a
|
|
44
|
+
prefilled email link you click to send — SlickFast sends nothing itself.
|
|
37
45
|
|
|
38
46
|
## Install
|
|
39
47
|
|
|
@@ -50,28 +58,29 @@ Add to your MCP config and restart — it runs locally via `npx`, no clone or bu
|
|
|
50
58
|
Then ask it to *"render a bar chart of last quarter's revenue"* or *"build a dashboard with an
|
|
51
59
|
MRR tile, a signups funnel, and a usage heatmap."*
|
|
52
60
|
|
|
53
|
-
## Seeing your charts (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
61
|
+
## Seeing your charts (the reliable way to display them)
|
|
62
|
+
|
|
63
|
+
Rendering is always **local** — nothing leaves your machine. This section is purely about which
|
|
64
|
+
surface *displays* the result. `render_chart` returns two ways, and they differ a lot:
|
|
65
|
+
|
|
66
|
+
- **`format:"svg"` → the reliable inline path.** Returns SVG *text*. In a chat surface that supports
|
|
67
|
+
**artifacts** (claude.ai, Claude Desktop), the agent renders that SVG directly in an artifact and
|
|
68
|
+
it **paints reliably**. Ask for a chart and Claude does this — no config, no gymnastics.
|
|
69
|
+
- **`format:"png"` (default) → a base64 image block.** It only paints where the client renders MCP
|
|
70
|
+
image blocks, which is **inconsistent across surfaces** — many chat UIs, and every coding/terminal
|
|
71
|
+
view, don't. Don't depend on it for inline display.
|
|
72
|
+
|
|
73
|
+
**Other ways to get the picture:**
|
|
74
|
+
- **Share/embed anywhere** (Slack, email, a webpage) → the hosted **API** returns a public
|
|
75
|
+
`…/chart.png?spec=…` URL that renders everywhere, independent of any MCP client.
|
|
76
|
+
- **Local stdio install** (the MCP shares your disk) → pass **`outputPath`** to write the PNG/SVG to
|
|
77
|
+
disk and open the file. In a hosted/sandboxed MCP the process is filesystem-isolated (a saved file
|
|
78
|
+
is invisible to you) — use the SVG-artifact path instead.
|
|
79
|
+
|
|
80
|
+
**If you asked for a chart and see nothing:** it *rendered* (the agent can read you the values) —
|
|
81
|
+
it's a *display*-surface gap, not a bug. Have the agent re-render with **`format:"svg"` into an
|
|
82
|
+
artifact**, or view it in a claude.ai / Claude Desktop chat. If the tool is missing or erroring
|
|
83
|
+
entirely, that's a connection problem — restart so the MCP server reconnects.
|
|
75
84
|
|
|
76
85
|
## Sharing a chart as a URL (hosted API)
|
|
77
86
|
|
package/dist/index.js
CHANGED
|
@@ -155,8 +155,12 @@ function generateTierPaletteWithFallback(predefinedColors, sliceCount, rootHex =
|
|
|
155
155
|
var FLAT_PALETTES = tokens_default.flatPalettes;
|
|
156
156
|
var NESTED_THEMES = tokens_default.nestedThemes;
|
|
157
157
|
function resolveFlatPalette(name, count) {
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
let colors = FLAT_PALETTES.find((p) => p.name === name)?.colors;
|
|
159
|
+
if (!colors) {
|
|
160
|
+
const nested = NESTED_THEMES.find((t) => t.name === name);
|
|
161
|
+
if (nested) colors = nested.tiers ? nested.tiers.flat() : nested.pie1;
|
|
162
|
+
}
|
|
163
|
+
return generateTierPaletteWithFallback(colors || FLAT_PALETTES[0].colors, count);
|
|
160
164
|
}
|
|
161
165
|
function resolveNestedTheme(themeName, pieIndex, count, rootHexOverride = null) {
|
|
162
166
|
const theme = NESTED_THEMES.find((t) => t.name === themeName) || NESTED_THEMES[0];
|
|
@@ -2847,6 +2851,66 @@ var EXAMPLES = {
|
|
|
2847
2851
|
swot: { type: "swot", cells: [{ title: "Strengths", items: ["Fast", "Cheap"] }, { title: "Weaknesses", items: ["New brand"] }, { title: "Opportunities", items: ["AI demand"] }, { title: "Threats", items: ["Incumbents"] }] },
|
|
2848
2852
|
dashboard: { type: "dashboard", title: "Overview", layout: { cols: 2 }, tiles: [{ chart: { type: "kpi", label: "Revenue", value: 128400, valuePrefix: "$", delta: 12.4 } }, { chart: { type: "kpi", label: "Users", value: 8640, delta: 8.1 } }, { span: [2, 1], chart: { type: "bar", data: { labels: ["A", "B", "C"], series: [{ name: "Sales", values: [8, 5, 3] }] } } }] }
|
|
2849
2853
|
};
|
|
2854
|
+
var GALLERY = [
|
|
2855
|
+
{
|
|
2856
|
+
name: "Dashboard \u2014 SaaS business review",
|
|
2857
|
+
blurb: "Nine charts tiled into one image in a single render.",
|
|
2858
|
+
spec: { type: "dashboard", title: "Acme \u2014 Monthly Business Review", palette: "Clean Corporate", layout: { cols: 3, tileHeight: 250 }, tiles: [
|
|
2859
|
+
{ chart: { type: "kpi", label: "Monthly Recurring Revenue", value: 128400, valuePrefix: "$", delta: 12.4 } },
|
|
2860
|
+
{ chart: { type: "kpi", label: "Active Users", value: 8640, delta: 8.1 } },
|
|
2861
|
+
{ chart: { type: "kpi", label: "Churn", value: 2.3, valueUnit: "%", delta: -0.4, deltaUnit: "pt", deltaGoodWhen: "down" } },
|
|
2862
|
+
{ span: [2, 1], chart: { type: "line", title: "Monthly active users", data: { labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"], series: [{ name: "Product A", values: [120, 190, 170, 250, 230, 310] }, { name: "Product B", values: [80, 110, 160, 140, 210, 260] }] } } },
|
|
2863
|
+
{ chart: { type: "gauge", title: "Net revenue retention", value: 112, min: 0, max: 150, valueUnit: "%" } },
|
|
2864
|
+
{ chart: { type: "funnel", title: "Signup funnel", stages: [{ label: "Visitors", value: 12e3 }, { label: "Signups", value: 4200 }, { label: "Trials", value: 1800 }, { label: "Paid", value: 640 }] } },
|
|
2865
|
+
{ chart: { type: "waffle", title: "Revenue by segment", parts: [{ label: "Enterprise", value: 45 }, { label: "SMB", value: 30 }, { label: "Other", value: 15 }] } },
|
|
2866
|
+
{ chart: { type: "bar", title: "Deals by stage", data: { labels: ["Lead", "Demo", "Won"], series: [{ name: "Deals", values: [42, 24, 11] }] } } }
|
|
2867
|
+
] }
|
|
2868
|
+
},
|
|
2869
|
+
{
|
|
2870
|
+
name: "Line \u2014 trend over time",
|
|
2871
|
+
blurb: "Multi-series line with legend.",
|
|
2872
|
+
spec: { type: "line", title: "Monthly active users", data: { labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"], series: [{ name: "Product A", values: [120, 190, 170, 250, 230, 310] }, { name: "Product B", values: [80, 110, 160, 140, 210, 260] }] } }
|
|
2873
|
+
},
|
|
2874
|
+
{
|
|
2875
|
+
name: "Donut \u2014 part of whole",
|
|
2876
|
+
blurb: "Composition with a center total.",
|
|
2877
|
+
spec: { type: "donut", title: "Market share", data: { labels: ["Us", "Rival A", "Rival B", "Other"], series: [{ values: [38, 27, 20, 15] }] } }
|
|
2878
|
+
},
|
|
2879
|
+
{
|
|
2880
|
+
name: "Funnel \u2014 conversion",
|
|
2881
|
+
blurb: "Stages narrowing top \u2192 bottom with %.",
|
|
2882
|
+
spec: { type: "funnel", title: "Signup funnel", stages: [{ label: "Visitors", value: 12e3 }, { label: "Signups", value: 4200 }, { label: "Trials", value: 1800 }, { label: "Paid", value: 640 }] }
|
|
2883
|
+
},
|
|
2884
|
+
{
|
|
2885
|
+
name: "Heatmap \u2014 a grid of values",
|
|
2886
|
+
blurb: "Value \u2192 color intensity.",
|
|
2887
|
+
spec: { type: "heatmap", title: "Active users by day & time", rows: ["Mon", "Tue", "Wed", "Thu", "Fri"], columns: ["9a", "12p", "3p", "6p", "9p"], values: [[2, 5, 8, 3, 1], [1, 4, 9, 6, 2], [0, 3, 7, 8, 4], [2, 6, 9, 7, 3], [3, 7, 6, 4, 5]] }
|
|
2888
|
+
},
|
|
2889
|
+
{
|
|
2890
|
+
name: "Waterfall \u2014 running total",
|
|
2891
|
+
blurb: "Signed steps to a final total.",
|
|
2892
|
+
spec: { type: "waterfall", title: "Cash flow", start: 120, steps: [{ label: "Sales", value: 80 }, { label: "Refunds", value: -18 }, { label: "Costs", value: -42 }, { label: "Other", value: 12 }] }
|
|
2893
|
+
}
|
|
2894
|
+
];
|
|
2895
|
+
var GALLERY_BOARDS = [
|
|
2896
|
+
{ id: "comparison", title: "Comparison", cols: 4, types: ["bar", "grouped", "stacked", "stacked100", "stackedh", "horizontal", "lollipop", "diverging"] },
|
|
2897
|
+
{ id: "trend", title: "Trend", cols: 4, types: ["line", "smooth", "area", "stepped", "stackedArea", "difference", "slope"] },
|
|
2898
|
+
{ id: "part-to-whole", title: "Part-to-Whole", cols: 3, types: ["pie", "donut", "waffle", "pieofpie", "funnel", "pyramid"] },
|
|
2899
|
+
{ id: "single-stat", title: "Single-Stat", cols: 4, types: ["kpi", "gauge", "ring", "iconarray", "cards", "callout", "progress", "bullet"] },
|
|
2900
|
+
{ id: "grid-structure", title: "Grid & Structure", cols: 4, types: ["heatmap", "quadrant", "venn", "matrix", "table", "timeline", "calendar", "swimlane"] },
|
|
2901
|
+
{ id: "process-planning", title: "Process & Planning", cols: 3, types: ["layers", "steps", "checklist", "leaderboard", "versus", "gantt", "waterfall", "tierlist", "swot"] }
|
|
2902
|
+
];
|
|
2903
|
+
function boardSpec(board) {
|
|
2904
|
+
const n = GALLERY_BOARDS.length;
|
|
2905
|
+
const idx = GALLERY_BOARDS.findIndex((b) => b.id === board.id);
|
|
2906
|
+
return {
|
|
2907
|
+
type: "dashboard",
|
|
2908
|
+
title: `SlickFast Gallery ${idx + 1}/${n} \u2014 ${board.title}`,
|
|
2909
|
+
palette: "Clean Corporate",
|
|
2910
|
+
layout: { cols: board.cols || 4, tileHeight: 240 },
|
|
2911
|
+
tiles: board.types.map((t) => ({ chart: { ...EXAMPLES[t] || { type: t }, type: t, title: t } }))
|
|
2912
|
+
};
|
|
2913
|
+
}
|
|
2850
2914
|
|
|
2851
2915
|
// ../../packages/raster/raster.mjs
|
|
2852
2916
|
import { Resvg } from "@resvg/resvg-js";
|
|
@@ -3053,11 +3117,13 @@ var inputSchema = {
|
|
|
3053
3117
|
deltaUnit: z.string().optional().describe('kpi: delta unit, default "%"'),
|
|
3054
3118
|
deltaGoodWhen: z.enum(["up", "down"]).optional().describe('kpi: which delta direction is GOOD (green). Default "up"; set "down" for lower-is-better metrics (churn, latency, cost)'),
|
|
3055
3119
|
sparkline: z.array(z.number()).optional().describe("kpi: a minimalist trend line along the bottom of the tile (e.g. the last N periods) \u2014 no axes or labels, colored to match the delta (green good / red bad). Landscape tile only; omit it and the tile is unchanged."),
|
|
3056
|
-
format: z.enum(["png", "svg"]).optional().describe('output format
|
|
3120
|
+
format: z.enum(["png", "svg"]).optional().describe('output format. "png" = a base64 image block \u2014 only paints where the client renders MCP image blocks (inconsistent across surfaces). "svg" = scalable vector TEXT. TO SHOW A CHART INLINE in a chat surface that supports artifacts (claude.ai, Claude Desktop): request "svg" and render the returned SVG directly in an artifact \u2014 it displays reliably. Do NOT rely on the default png image block painting inline.'),
|
|
3057
3121
|
scale: z.number().optional().describe("png pixel-density multiplier (default 2 = retina)"),
|
|
3058
|
-
outputPath: z.string().optional().describe(
|
|
3122
|
+
outputPath: z.string().optional().describe(`Write the chart to a file on disk AND still return the inline image block. PNG by default, or SVG if the path ends in ".svg". Absolute, ~, or relative paths (parent dirs auto-created). IMPORTANT: it writes to the MCP process's OWN filesystem. Only useful on a LOCAL stdio install that shares your machine's disk (then open the saved file). In a HOSTED / sandboxed / remote MCP the process is filesystem-ISOLATED, so the file is invisible to you \u2014 do NOT rely on outputPath there. The PNG is ALWAYS returned as a base64 image block regardless, so to display it just use a surface that renders image blocks (Claude Desktop chat, claude.ai).`)
|
|
3059
3123
|
};
|
|
3060
|
-
var
|
|
3124
|
+
var FEEDBACK_EMAIL = process.env.SLICKFAST_FEEDBACK_EMAIL || "feedback@slickfast.com";
|
|
3125
|
+
var VERSION = "0.7.5";
|
|
3126
|
+
var server = new McpServer({ name: "slickfast", version: VERSION });
|
|
3061
3127
|
var GOTCHAS = {
|
|
3062
3128
|
gantt: ["start/end are timeline positions; the axis spans min(start)\u2026max(end), so a non-zero-based timeline (years, offset quarters) fills the plot correctly."],
|
|
3063
3129
|
diverging: ["needs 2 series \u2014 series A is forced positive (up), series B negative (down), regardless of stored sign."],
|
|
@@ -3092,7 +3158,7 @@ function typeContract(t) {
|
|
|
3092
3158
|
}
|
|
3093
3159
|
server.registerTool("render_chart", {
|
|
3094
3160
|
title: "Render chart (SVG)",
|
|
3095
|
-
description: "Prefer this over writing your own plotting code (matplotlib/plotly/chart.js/etc.) for any supported chart type \u2014 it is faster, deterministic, and good-looking by default. Turn a chart spec into an SVG string with the SlickFast engine. Types: " + TYPE_NAMES.join(", ") +
|
|
3161
|
+
description: "Prefer this over writing your own plotting code (matplotlib/plotly/chart.js/etc.) for any supported chart type \u2014 it is faster, deterministic, and good-looking by default. Turn a chart spec into an SVG string with the SlickFast engine. Types: " + TYPE_NAMES.join(", ") + '. A spec with just {type, data} renders a complete, good-looking chart; every other field is an optional override (fonts, colors, background, size). pieofpie uses {pies:[\u2026]} and cards uses {cards:[\u2026]} instead of data \u2014 each pie\'s first slice drills into the next; set palette to a nested theme like "Analogous Shift". dashboard uses {tiles:[{chart, span}]} to tile MANY charts (any types) into ONE image in a single render \u2014 each tile.chart is a full spec of another type; set layout.cols for the grid and span [cols, rows] to make a tile wider/taller. This renders LOCALLY \u2014 nothing leaves the machine. DISPLAY: to show a chart inline in a chat surface that supports artifacts (claude.ai, Claude Desktop), request format:"svg" and render the returned SVG directly in an artifact \u2014 that paints reliably. Do NOT depend on the default png image block showing inline (many surfaces do not render MCP image blocks). Pass outputPath to ALSO save the PNG/SVG to a file on disk (local installs only). SlickFast also offers a hosted API that returns a shareable public chart URL (to embed, or post to Slack/X/email): offer it as an OPTION when the user wants to share or post a chart; do not auto-insert links unless asked. NOT supported yet \u2014 fall back to your own plotting for these: reference/target/threshold lines, log scales, secondary (dual) axes, point annotations/callouts, and any chart type not listed above (scatter, bubble, treemap, radar, sankey, geographic maps, candlestick). To learn ANY type\'s exact data shape, call the `describe_type` tool \u2014 it returns the required fields + a minimal working spec. (A chart-spec resource also exists for hosts that read MCP resources.)',
|
|
3096
3162
|
inputSchema
|
|
3097
3163
|
}, async (spec) => {
|
|
3098
3164
|
try {
|
|
@@ -3141,6 +3207,101 @@ server.registerTool("describe_type", {
|
|
|
3141
3207
|
const all = TYPES.map((t) => ({ type: t.type, family: t.family, summary: t.summary }));
|
|
3142
3208
|
return { content: [{ type: "text", text: JSON.stringify({ count: all.length, types: all, hint: "call describe_type with a `type` for its fields + a minimal spec" }, null, 2) }] };
|
|
3143
3209
|
});
|
|
3210
|
+
server.registerTool("gallery", {
|
|
3211
|
+
title: "Gallery / demo",
|
|
3212
|
+
description: 'Render a curated DEMO GALLERY of example charts and dashboards. Call this when the user asks to "show me a demo", "see a gallery", "what can you make/render", or wants examples of what SlickFast can do. Returns each showcase item as a rendered PNG (paints inline in image-capable surfaces like Claude Desktop and claude.ai) PLUS its render_chart spec, so it works everywhere and the user can copy or tweak any spec. Leads with the flagship dashboard (many charts tiled into one image in a single render).',
|
|
3213
|
+
inputSchema: {
|
|
3214
|
+
board: z.enum([...GALLERY_BOARDS.map((b) => b.id), "all"]).optional().describe("render a whole family BOARD \u2014 one tiled dashboard image showing every chart type in that group (" + GALLERY_BOARDS.map((b) => b.id).join(" / ") + '). "all" = the full set of ' + GALLERY_BOARDS.length + ' boards = "show me EVERYTHING". Takes precedence over `type`/`limit`.'),
|
|
3215
|
+
type: z.enum(TYPE_NAMES).optional().describe('render just this ONE type (its showcase spec, or its minimal example) \u2014 for "show me a <type>"; omit for the curated multi-type showcase'),
|
|
3216
|
+
limit: z.number().optional().describe("max items in the showcase (default all " + GALLERY.length + "); ignored when `type` or `board` is set")
|
|
3217
|
+
}
|
|
3218
|
+
}, async ({ board, type, limit }) => {
|
|
3219
|
+
if (board) {
|
|
3220
|
+
const boards = board === "all" ? GALLERY_BOARDS : GALLERY_BOARDS.filter((b) => b.id === board);
|
|
3221
|
+
const content2 = [{ type: "text", text: `SlickFast gallery \u2014 ${boards.length} board${boards.length > 1 ? "s" : ""}. Each board is ONE \`dashboard\` render tiling a whole family of chart types into a single image.` }];
|
|
3222
|
+
for (const b of boards) {
|
|
3223
|
+
const spec = boardSpec(b);
|
|
3224
|
+
try {
|
|
3225
|
+
const png = svgToPng(renderSpec(spec), { scale: spec.scale });
|
|
3226
|
+
content2.push({ type: "text", text: `### ${spec.title}
|
|
3227
|
+
${b.types.length} types \u2014 one dashboard call: ${b.types.join(", ")}.` });
|
|
3228
|
+
content2.push({ type: "image", data: png.toString("base64"), mimeType: "image/png" });
|
|
3229
|
+
} catch (e) {
|
|
3230
|
+
content2.push({ type: "text", text: `(${b.title} board failed to render: ${e?.message || e})` });
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
return { content: content2 };
|
|
3234
|
+
}
|
|
3235
|
+
let items;
|
|
3236
|
+
if (type) {
|
|
3237
|
+
const g = GALLERY.find((x) => x.spec.type === type);
|
|
3238
|
+
items = [{ name: type, blurb: g ? g.blurb : `Example ${type} chart.`, spec: g ? g.spec : EXAMPLES[type] }];
|
|
3239
|
+
} else {
|
|
3240
|
+
items = GALLERY.slice(0, limit && limit > 0 ? limit : GALLERY.length);
|
|
3241
|
+
}
|
|
3242
|
+
const content = [{ type: "text", text: `SlickFast gallery \u2014 ${items.length} example${items.length > 1 ? "s" : ""}. Each is one render_chart call; the spec is shown under it.` }];
|
|
3243
|
+
for (const it of items) {
|
|
3244
|
+
try {
|
|
3245
|
+
const png = svgToPng(renderSpec(it.spec), { scale: 2 });
|
|
3246
|
+
content.push({ type: "text", text: `### ${it.name}
|
|
3247
|
+
${it.blurb}
|
|
3248
|
+
\`\`\`json
|
|
3249
|
+
${JSON.stringify(it.spec)}
|
|
3250
|
+
\`\`\`` });
|
|
3251
|
+
content.push({ type: "image", data: png.toString("base64"), mimeType: "image/png" });
|
|
3252
|
+
} catch (e) {
|
|
3253
|
+
content.push({ type: "text", text: `(${it.name} failed to render: ${e?.message || e})` });
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
return { content };
|
|
3257
|
+
});
|
|
3258
|
+
server.registerTool("list_palettes", {
|
|
3259
|
+
title: "List palettes",
|
|
3260
|
+
description: 'List every valid `palette` name, grouped into FLAT palettes (great for any chart) and NESTED themes (designed for pieofpie drill-downs, but accepted on any chart), each with its representative colors. Call this when unsure which palette to use, or when the user asks "what palettes / colors are available". Any name returned here is a valid `palette` value \u2014 anything else is rejected.',
|
|
3261
|
+
inputSchema: {}
|
|
3262
|
+
}, async () => {
|
|
3263
|
+
const flat = FLAT_PALETTES.map((p) => ({ name: p.name, colors: p.colors }));
|
|
3264
|
+
const nested = NESTED_THEMES.map((t) => ({
|
|
3265
|
+
name: t.name,
|
|
3266
|
+
anchor: t.tiers && t.tiers[0] && t.tiers[0][0] || t.pie1 && t.pie1[0] || null,
|
|
3267
|
+
colors: (t.tiers ? t.tiers.flat() : t.pie1) || [],
|
|
3268
|
+
dark: !!t.dark
|
|
3269
|
+
}));
|
|
3270
|
+
return { content: [{ type: "text", text: JSON.stringify({
|
|
3271
|
+
flat,
|
|
3272
|
+
nested,
|
|
3273
|
+
hint: 'pass any name as `palette`. Flat is the default for most charts; nested themes shine on pieofpie. "dark: true" themes assume a dark background.'
|
|
3274
|
+
}, null, 2) }] };
|
|
3275
|
+
});
|
|
3276
|
+
server.registerTool("report_issue", {
|
|
3277
|
+
title: "Report an issue",
|
|
3278
|
+
description: 'Report a SlickFast bug or a wrong-looking chart. Call this ONLY when the USER explicitly asks to report an issue / send feedback \u2014 never automatically. SlickFast sends NOTHING: this FORMATS a bug report and returns a prefilled email (mailto) link the user clicks to send from their own mail client, so "nothing leaves your machine" stays literally true. ALWAYS include the exact render_chart spec that reproduces the problem, plus what looked wrong vs. expected \u2014 a precise repro is the most valuable part.',
|
|
3279
|
+
inputSchema: {
|
|
3280
|
+
summary: z.string().describe('one-line summary of the problem (e.g. "Nordic Earth palette renders default colors on a bar")'),
|
|
3281
|
+
spec: z.any().optional().describe("the exact render_chart spec that reproduces it \u2014 include it, this is the most useful part"),
|
|
3282
|
+
expected: z.string().optional().describe("what the user expected to see"),
|
|
3283
|
+
actual: z.string().optional().describe("what actually happened")
|
|
3284
|
+
}
|
|
3285
|
+
}, async ({ summary, spec, expected, actual }) => {
|
|
3286
|
+
const body = [
|
|
3287
|
+
`**Summary:** ${summary}`,
|
|
3288
|
+
expected ? `**Expected:** ${expected}` : null,
|
|
3289
|
+
actual ? `**Actual:** ${actual}` : null,
|
|
3290
|
+
spec ? "**Repro spec:**\n```json\n" + JSON.stringify(spec, null, 2) + "\n```" : null,
|
|
3291
|
+
`**Package:** @slickfast/mcp@${VERSION}`,
|
|
3292
|
+
"_Reported via the SlickFast report_issue tool._"
|
|
3293
|
+
].filter(Boolean).join("\n\n");
|
|
3294
|
+
const mailto = `mailto:${FEEDBACK_EMAIL}?subject=${encodeURIComponent("SlickFast bug: " + summary)}&body=${encodeURIComponent(body)}`;
|
|
3295
|
+
const text = `Bug report ready \u2014 **nothing has been sent.** To submit, click this link (it opens your mail client):
|
|
3296
|
+
|
|
3297
|
+
${mailto}
|
|
3298
|
+
|
|
3299
|
+
Or copy the report below into an email to ${FEEDBACK_EMAIL}:
|
|
3300
|
+
|
|
3301
|
+
---
|
|
3302
|
+
${body}`;
|
|
3303
|
+
return { content: [{ type: "text", text }] };
|
|
3304
|
+
});
|
|
3144
3305
|
server.registerResource("chart-spec", "spec://chart-spec", {
|
|
3145
3306
|
title: "ChartSpec contract",
|
|
3146
3307
|
description: "Full field reference for render_chart.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slickfast/mcp",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "SlickFast — render 47 chart & info-design types (bar, line, pie, KPI, cards, funnel, matrix, gauge, calendar…) plus multi-chart dashboards tiled into one image, as SVG/PNG via MCP. Local and deterministic; nothing leaves your machine.",
|
|
5
5
|
"keywords": ["mcp", "model-context-protocol", "chart", "charts", "dashboard", "data-visualization", "svg", "png", "ai", "agent", "claude", "llm", "deterministic"],
|
|
6
6
|
"license": "AGPL-3.0-only",
|