ztd-charts-mcp 0.2.1 → 0.3.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 +20 -2
- package/dist/{chunk-5RVK2R4T.js → chunk-RLRGVTG6.js} +7 -6
- package/dist/chunk-RLRGVTG6.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-5RVK2R4T.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
# ztd-charts-mcp
|
|
2
2
|
|
|
3
|
-
A stdio MCP server for ztd charts
|
|
3
|
+
A stdio MCP server for ztd charts, so an AI agent can look up the 33 charts, check a chart spec and render it.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[Website](https://charts.ztd.app) · [Gallery](https://charts.ztd.app/docs/) · [GitHub](https://github.com/Junhan2/ztd-charts)
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
- `list_charts`: the 33 chart types with level, group, a one-line summary, when to use or avoid them, and their encoding channels.
|
|
10
|
+
- `get_chart_schema`: the JSON Schema for one chart type, with a complete example spec and a React snippet.
|
|
11
|
+
- `validate_spec`: errors and warnings with JSON paths, such as an unknown type, a missing field or a wrong option.
|
|
12
|
+
- `render_chart`: a PNG image and SVG of the spec in its card, plus React code ready to paste. Data can be inline rows, CSV text or a URL.
|
|
13
|
+
- `suggest_chart`: chart types ranked for your data, each with a reason and a ready spec.
|
|
14
|
+
|
|
15
|
+
Resource: `ztd-charts://schema/chart-spec.json`. Set `locale: "ko"` for Korean messages and chart text.
|
|
16
|
+
|
|
17
|
+
Data URLs that point to a private or local network (localhost, 10.x, 192.168.x, 169.254.169.254 and similar) are refused, so an agent cannot read internal services through the server. To load data from a local dev server, add `ZTD_CHARTS_ALLOW_PRIVATE_URLS=1` to the server's environment: `-e` in `claude mcp add`, or the `env` field in a JSON config below.
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
## Setup
|
|
6
22
|
|
|
7
23
|
Claude Code:
|
|
8
24
|
|
|
@@ -19,3 +35,5 @@ Claude Desktop (`claude_desktop_config.json`), Cursor (`.cursor/mcp.json`) and o
|
|
|
19
35
|
}
|
|
20
36
|
}
|
|
21
37
|
```
|
|
38
|
+
|
|
39
|
+
Renamed from `@junhanlee/chart-kit-mcp` in 0.2.0. The server name in configs is now `ztd-charts`.
|
|
@@ -4,7 +4,7 @@ import { z } from "zod";
|
|
|
4
4
|
import { getChartInfo, getChartSchema, isHttpUrl, listCharts, loadRows, reactSnippet, render, specSnippet, suggest, validate } from "ztd-charts-cli";
|
|
5
5
|
import { GROUPS, LEVELS, parseCSV, normalizeData, specLocale } from "ztd-charts/spec";
|
|
6
6
|
var SERVER_NAME = "ztd-charts";
|
|
7
|
-
var SERVER_VERSION = "0.
|
|
7
|
+
var SERVER_VERSION = "0.3.0";
|
|
8
8
|
var GROUP_IDS = GROUPS.map((g) => g.id);
|
|
9
9
|
var LEVEL_IDS = LEVELS.map((l) => l.id);
|
|
10
10
|
var text = (t) => ({ type: "text", text: t });
|
|
@@ -14,6 +14,7 @@ function trimSpec(spec, rows = 20) {
|
|
|
14
14
|
return { ...spec, data: spec.data.slice(0, rows) };
|
|
15
15
|
}
|
|
16
16
|
function createServer() {
|
|
17
|
+
const allowPrivate = process.env.ZTD_CHARTS_ALLOW_PRIVATE_URLS === "1";
|
|
17
18
|
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {}, resources: {} }, instructions: 'ztd-charts: 33 animated React chart types driven by a JSON spec { type, data, encoding, options, card }. Typical flow: suggest_chart (or list_charts) -> get_chart_schema -> write a spec -> validate_spec -> render_chart to preview -> use the returned React snippet. Built-in chart text (tooltips, screen reader text, badges) and number formats are English by default. For Korean, pass locale "ko" to render_chart or set options.locale in the spec, and write card text in Korean. validate_spec and suggest_chart take locale too.' });
|
|
18
19
|
server.registerTool(
|
|
19
20
|
"list_charts",
|
|
@@ -66,7 +67,7 @@ function createServer() {
|
|
|
66
67
|
"render_chart",
|
|
67
68
|
{
|
|
68
69
|
title: "Render chart",
|
|
69
|
-
description: "Server-render a chart spec to PNG (returned as an image) and/or SVG text, with the card frame (number, title, legend, takeaway, level badge) when spec.card is set. Also returns ready-to-paste React code. data may be inline rows, { csv }, or { url } (fetched). Set locale for the language of built-in chart text and number formats.",
|
|
70
|
+
description: "Server-render a chart spec to PNG (returned as an image) and/or SVG text, with the card frame (number, title, legend, takeaway, level badge) when spec.card is set. Also returns ready-to-paste React code. data may be inline rows, { csv }, or { url } (fetched; private and local network addresses are refused unless the server runs with ZTD_CHARTS_ALLOW_PRIVATE_URLS=1). Set locale for the language of built-in chart text and number formats.",
|
|
70
71
|
inputSchema: {
|
|
71
72
|
spec: z.record(z.string(), z.unknown()).describe("Chart spec"),
|
|
72
73
|
format: z.enum(["png", "svg", "both"]).optional().describe("Default png"),
|
|
@@ -82,7 +83,7 @@ function createServer() {
|
|
|
82
83
|
const v = validate(s, specLocale(s, locale));
|
|
83
84
|
if (!v.valid) return { isError: true, content: [text("Invalid spec. Fix the errors below (the same result validate_spec returns)."), json(v)] };
|
|
84
85
|
try {
|
|
85
|
-
const res = await render(s, { format: format === "svg" ? "svg" : "png", width, theme, scale, locale });
|
|
86
|
+
const res = await render(s, { format: format === "svg" ? "svg" : "png", width, theme, scale, locale, allowPrivate });
|
|
86
87
|
const content = [];
|
|
87
88
|
if (format !== "svg" && res.png) content.push({ type: "image", data: res.png.toString("base64"), mimeType: "image/png" });
|
|
88
89
|
if ((format === "svg" || format === "both") && res.svg) content.push(text(res.svg));
|
|
@@ -102,7 +103,7 @@ ${specSnippet(s)}`));
|
|
|
102
103
|
"suggest_chart",
|
|
103
104
|
{
|
|
104
105
|
title: "Suggest charts for data",
|
|
105
|
-
description: "Profile tabular data (column kinds, cardinality, hierarchy, source/target pairs, dates) and recommend chart types ranked by fit, each with a reason and a ready spec. Pass csv text, rows (array of objects), or a url to CSV/JSON (Google Sheets CSV export, REST). Set locale for the language of the reasons and the suggested specs.",
|
|
106
|
+
description: "Profile tabular data (column kinds, cardinality, hierarchy, source/target pairs, dates) and recommend chart types ranked by fit, each with a reason and a ready spec. Pass csv text, rows (array of objects), or a url to CSV/JSON (Google Sheets CSV export, REST; private and local network addresses are refused unless the server runs with ZTD_CHARTS_ALLOW_PRIVATE_URLS=1). Set locale for the language of the reasons and the suggested specs.",
|
|
106
107
|
inputSchema: {
|
|
107
108
|
csv: z.string().optional().describe("CSV text with header row"),
|
|
108
109
|
rows: z.array(z.record(z.string(), z.unknown())).optional().describe("Array of row objects"),
|
|
@@ -119,7 +120,7 @@ ${specSnippet(s)}`));
|
|
|
119
120
|
else if (csv) data = parseCSV(csv);
|
|
120
121
|
else if (url) {
|
|
121
122
|
if (!isHttpUrl(url)) return { isError: true, content: [text("url must start with http:// or https://. For a local file, pass its text as csv or its rows as rows.")] };
|
|
122
|
-
data = await loadRows(url, locale);
|
|
123
|
+
data = await loadRows(url, locale, { allowPrivate });
|
|
123
124
|
} else return { isError: true, content: [text("Pass one of csv, rows or url.")] };
|
|
124
125
|
} catch (e) {
|
|
125
126
|
return { isError: true, content: [text(`Couldn't read the data: ${e.message}`)] };
|
|
@@ -149,4 +150,4 @@ export {
|
|
|
149
150
|
SERVER_VERSION,
|
|
150
151
|
createServer
|
|
151
152
|
};
|
|
152
|
-
//# sourceMappingURL=chunk-
|
|
153
|
+
//# sourceMappingURL=chunk-RLRGVTG6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getChartInfo, getChartSchema, isHttpUrl, listCharts, loadRows, reactSnippet, render, specSnippet, suggest, validate, type ChartSpec } from \"ztd-charts-cli\";\nimport { GROUPS, LEVELS, parseCSV, normalizeData, specLocale, type ChartGroup, type ChartLevel } from \"ztd-charts/spec\";\n\nexport const SERVER_NAME = \"ztd-charts\";\nexport const SERVER_VERSION = \"0.3.0\";\n\nconst GROUP_IDS = GROUPS.map((g) => g.id) as [ChartGroup, ...ChartGroup[]];\nconst LEVEL_IDS = LEVELS.map((l) => l.id) as [ChartLevel, ...ChartLevel[]];\n\nconst text = (t: string) => ({ type: \"text\" as const, text: t });\nconst json = (v: unknown) => text(JSON.stringify(v, null, 2));\n\n/** 데이터가 큰 스펙은 앞부분만 보여줘요 (응답 크기 관리) */\nfunction trimSpec(spec: ChartSpec, rows = 20): ChartSpec {\n if (!Array.isArray(spec.data) || spec.data.length <= rows) return spec;\n return { ...spec, data: spec.data.slice(0, rows) };\n}\n\nexport function createServer(): McpServer {\n // 에이전트가 넘기는 주소로 사내망, 이 컴퓨터, 클라우드 메타데이터를 읽지 못하게 기본으로 막아요. 로컬 개발 서버의 데이터가 필요하면 이 값을 1로 켜요\n const allowPrivate = process.env.ZTD_CHARTS_ALLOW_PRIVATE_URLS === \"1\";\n const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {}, resources: {} }, instructions: 'ztd-charts: 33 animated React chart types driven by a JSON spec { type, data, encoding, options, card }. Typical flow: suggest_chart (or list_charts) -> get_chart_schema -> write a spec -> validate_spec -> render_chart to preview -> use the returned React snippet. Built-in chart text (tooltips, screen reader text, badges) and number formats are English by default. For Korean, pass locale \"ko\" to render_chart or set options.locale in the spec, and write card text in Korean. validate_spec and suggest_chart take locale too.' });\n\n server.registerTool(\n \"list_charts\",\n {\n title: \"List chart types\",\n description: \"List the 33 chart types with name, level (basic, intermediate, advanced), group, one-line summary, when to use or avoid, and encoding channels. Filter by group or level.\",\n inputSchema: { group: z.enum(GROUP_IDS).optional().describe(\"Chart group\"), level: z.enum(LEVEL_IDS).optional().describe(\"Difficulty level\") },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ group, level }) => {\n const list = listCharts({ group, level });\n return { content: [json({ count: list.length, charts: list })] };\n },\n );\n\n server.registerTool(\n \"get_chart_schema\",\n {\n title: \"Get chart schema\",\n description: \"Get the JSON Schema for one chart type (encoding channels, options with types/defaults), plus a complete example spec and a React snippet. Omit type for the full schema of all 33 charts (about 200 KB, so prefer asking for one type).\",\n inputSchema: { type: z.string().optional().describe('Chart type, e.g. \"pie\", \"line\", \"sankey\". Aliases like \"scatter\", \"violin\" work.') },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ type }) => {\n if (!type) return { content: [json(getChartSchema())] };\n try {\n const info = getChartInfo(type);\n return { content: [json({ type: info.type, name: info.name, summary: info.summary, useWhen: info.useWhen, avoidWhen: info.avoidWhen, encoding: info.encoding, options: info.options, example: trimSpec(info.example), schema: info.schema }), text(info.react)] };\n } catch (e) {\n return { isError: true, content: [text((e as Error).message)] };\n }\n },\n );\n\n server.registerTool(\n \"validate_spec\",\n {\n title: \"Validate chart spec\",\n description: \"Validate a chart spec against the registry and its inline data: unknown type (with did-you-mean), missing/unknown encoding channels, fields missing from data, non-numeric value fields, option types and enums. Returns { valid, errors[], warnings[] } with JSON paths and messages. Set locale for the language of the messages.\",\n inputSchema: {\n spec: z.record(z.string(), z.unknown()).describe(\"{ type, data, encoding, options?, card?, theme? }\"),\n locale: z.string().optional().describe('Locale for the error and warning messages. Default \"en-US\", or the spec\\'s options.locale when set. \"ko\" or \"ko-KR\" gives Korean messages'),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ spec, locale }) => {\n // render_chart와 같은 로캘(locale, 그다음 스펙 options.locale)로 검사해요. 둘 다 없으면 영어\n const r = validate(spec, specLocale(spec as unknown as ChartSpec, locale));\n return { content: [json(r)] };\n },\n );\n\n server.registerTool(\n \"render_chart\",\n {\n title: \"Render chart\",\n description: \"Server-render a chart spec to PNG (returned as an image) and/or SVG text, with the card frame (number, title, legend, takeaway, level badge) when spec.card is set. Also returns ready-to-paste React code. data may be inline rows, { csv }, or { url } (fetched; private and local network addresses are refused unless the server runs with ZTD_CHARTS_ALLOW_PRIVATE_URLS=1). Set locale for the language of built-in chart text and number formats.\",\n inputSchema: {\n spec: z.record(z.string(), z.unknown()).describe(\"Chart spec\"),\n format: z.enum([\"png\", \"svg\", \"both\"]).optional().describe(\"Default png\"),\n width: z.number().int().min(200).max(2400).optional().describe(\"Pixel width, default 720\"),\n theme: z.enum([\"dark\", \"light\"]).optional().describe(\"Default dark\"),\n scale: z.number().min(1).max(4).optional().describe(\"PNG pixel ratio, default 2\"),\n locale: z.string().optional().describe('Locale for built-in chart text and number formats. Default \"en-US\" (1.2K). \"ko\" or \"ko-KR\" gives Korean text and 1.2만 style numbers'),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ spec, format = \"png\", width, theme, scale, locale }) => {\n const s = spec as unknown as ChartSpec;\n // 그릴 때와 같은 로캘(locale, 그다음 스펙 options.locale)로 검사해요\n const v = validate(s, specLocale(s, locale));\n if (!v.valid) return { isError: true, content: [text(\"Invalid spec. Fix the errors below (the same result validate_spec returns).\"), json(v)] };\n try {\n const res = await render(s, { format: format === \"svg\" ? \"svg\" : \"png\", width, theme, scale, locale, allowPrivate });\n const content: ({ type: \"text\"; text: string } | { type: \"image\"; data: string; mimeType: string })[] = [];\n if (format !== \"svg\" && res.png) content.push({ type: \"image\", data: res.png.toString(\"base64\"), mimeType: \"image/png\" });\n if ((format === \"svg\" || format === \"both\") && res.svg) content.push(text(res.svg));\n content.push(text(`Size ${res.width}×${res.height}px${v.warnings.length ? `\\nWarnings: ${v.warnings.map((w) => w.message).join(\" / \")}` : \"\"}`));\n content.push(text(`// React (components)\\n${reactSnippet(s)}\\n// React (spec as is)\\n${specSnippet(s)}`));\n return { content };\n } catch (e) {\n return { isError: true, content: [text(`Render failed: ${(e as Error).message}`)] };\n }\n },\n );\n\n server.registerTool(\n \"suggest_chart\",\n {\n title: \"Suggest charts for data\",\n description: \"Profile tabular data (column kinds, cardinality, hierarchy, source/target pairs, dates) and recommend chart types ranked by fit, each with a reason and a ready spec. Pass csv text, rows (array of objects), or a url to CSV/JSON (Google Sheets CSV export, REST; private and local network addresses are refused unless the server runs with ZTD_CHARTS_ALLOW_PRIVATE_URLS=1). Set locale for the language of the reasons and the suggested specs.\",\n inputSchema: {\n csv: z.string().optional().describe(\"CSV text with header row\"),\n rows: z.array(z.record(z.string(), z.unknown())).optional().describe(\"Array of row objects\"),\n url: z.string().optional().describe(\"http(s) URL returning CSV or JSON\"),\n limit: z.number().int().min(1).max(10).optional().describe(\"Default 5\"),\n locale: z.string().optional().describe('Locale for chart names, reasons and card text in the suggestions. Default \"en-US\". \"ko\" or \"ko-KR\" gives Korean text and sets options.locale \"ko-KR\" in each spec'),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ csv, rows, url, limit, locale }) => {\n let data: Record<string, unknown>[];\n try {\n if (rows) data = normalizeData(rows);\n else if (csv) data = parseCSV(csv);\n else if (url) {\n // http(s)만 받아요. loadRows는 파일 경로도 읽어서, 그대로 두면 서버가 도는 컴퓨터의 파일을 돌려줄 수 있어요\n if (!isHttpUrl(url)) return { isError: true, content: [text(\"url must start with http:// or https://. For a local file, pass its text as csv or its rows as rows.\")] };\n data = await loadRows(url, locale, { allowPrivate });\n } else return { isError: true, content: [text(\"Pass one of csv, rows or url.\")] };\n } catch (e) {\n return { isError: true, content: [text(`Couldn't read the data: ${(e as Error).message}`)] };\n }\n const { profile, suggestions } = suggest(data, { limit: limit ?? 5, locale });\n return {\n content: [\n json({\n profile,\n suggestions: suggestions.map((s) => ({ type: s.type, name: s.name, score: s.score, reason: s.reason, spec: trimSpec(s.spec, 8), note: s.spec.data && Array.isArray(s.spec.data) && s.spec.data.length > 8 ? `spec.data shows only the first 8 rows. Put all ${s.spec.data.length} rows in the real spec.` : undefined })),\n }),\n ],\n };\n },\n );\n\n server.registerResource(\n \"chart-spec-schema\",\n \"ztd-charts://schema/chart-spec.json\",\n { title: \"ztd charts spec JSON Schema\", description: \"JSON Schema (draft 2020-12) for all chart specs\", mimeType: \"application/json\" },\n async (uri) => ({ contents: [{ uri: uri.href, mimeType: \"application/json\", text: JSON.stringify(getChartSchema(), null, 2) }] }),\n );\n\n return server;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
package/dist/server.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
|
|
3
3
|
declare const SERVER_NAME = "ztd-charts";
|
|
4
|
-
declare const SERVER_VERSION = "0.
|
|
4
|
+
declare const SERVER_VERSION = "0.3.0";
|
|
5
5
|
declare function createServer(): McpServer;
|
|
6
6
|
|
|
7
7
|
export { SERVER_NAME, SERVER_VERSION, createServer };
|
package/dist/server.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ztd-charts-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "ztd-charts MCP server (stdio): list charts, get schemas, validate specs, render SVG/PNG, and get chart suggestions from data",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Junhan2",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
34
34
|
"zod": "^4.6.7",
|
|
35
|
-
"ztd-charts": "0.
|
|
36
|
-
"ztd-charts-cli": "0.
|
|
35
|
+
"ztd-charts": "0.3.0",
|
|
36
|
+
"ztd-charts-cli": "0.3.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"tsup": "^9.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getChartInfo, getChartSchema, isHttpUrl, listCharts, loadRows, reactSnippet, render, specSnippet, suggest, validate, type ChartSpec } from \"ztd-charts-cli\";\nimport { GROUPS, LEVELS, parseCSV, normalizeData, specLocale, type ChartGroup, type ChartLevel } from \"ztd-charts/spec\";\n\nexport const SERVER_NAME = \"ztd-charts\";\nexport const SERVER_VERSION = \"0.2.1\";\n\nconst GROUP_IDS = GROUPS.map((g) => g.id) as [ChartGroup, ...ChartGroup[]];\nconst LEVEL_IDS = LEVELS.map((l) => l.id) as [ChartLevel, ...ChartLevel[]];\n\nconst text = (t: string) => ({ type: \"text\" as const, text: t });\nconst json = (v: unknown) => text(JSON.stringify(v, null, 2));\n\n/** 데이터가 큰 스펙은 앞부분만 보여줘요 (응답 크기 관리) */\nfunction trimSpec(spec: ChartSpec, rows = 20): ChartSpec {\n if (!Array.isArray(spec.data) || spec.data.length <= rows) return spec;\n return { ...spec, data: spec.data.slice(0, rows) };\n}\n\nexport function createServer(): McpServer {\n const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {}, resources: {} }, instructions: 'ztd-charts: 33 animated React chart types driven by a JSON spec { type, data, encoding, options, card }. Typical flow: suggest_chart (or list_charts) -> get_chart_schema -> write a spec -> validate_spec -> render_chart to preview -> use the returned React snippet. Built-in chart text (tooltips, screen reader text, badges) and number formats are English by default. For Korean, pass locale \"ko\" to render_chart or set options.locale in the spec, and write card text in Korean. validate_spec and suggest_chart take locale too.' });\n\n server.registerTool(\n \"list_charts\",\n {\n title: \"List chart types\",\n description: \"List the 33 chart types with name, level (basic, intermediate, advanced), group, one-line summary, when to use or avoid, and encoding channels. Filter by group or level.\",\n inputSchema: { group: z.enum(GROUP_IDS).optional().describe(\"Chart group\"), level: z.enum(LEVEL_IDS).optional().describe(\"Difficulty level\") },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ group, level }) => {\n const list = listCharts({ group, level });\n return { content: [json({ count: list.length, charts: list })] };\n },\n );\n\n server.registerTool(\n \"get_chart_schema\",\n {\n title: \"Get chart schema\",\n description: \"Get the JSON Schema for one chart type (encoding channels, options with types/defaults), plus a complete example spec and a React snippet. Omit type for the full schema of all 33 charts (about 200 KB, so prefer asking for one type).\",\n inputSchema: { type: z.string().optional().describe('Chart type, e.g. \"pie\", \"line\", \"sankey\". Aliases like \"scatter\", \"violin\" work.') },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ type }) => {\n if (!type) return { content: [json(getChartSchema())] };\n try {\n const info = getChartInfo(type);\n return { content: [json({ type: info.type, name: info.name, summary: info.summary, useWhen: info.useWhen, avoidWhen: info.avoidWhen, encoding: info.encoding, options: info.options, example: trimSpec(info.example), schema: info.schema }), text(info.react)] };\n } catch (e) {\n return { isError: true, content: [text((e as Error).message)] };\n }\n },\n );\n\n server.registerTool(\n \"validate_spec\",\n {\n title: \"Validate chart spec\",\n description: \"Validate a chart spec against the registry and its inline data: unknown type (with did-you-mean), missing/unknown encoding channels, fields missing from data, non-numeric value fields, option types and enums. Returns { valid, errors[], warnings[] } with JSON paths and messages. Set locale for the language of the messages.\",\n inputSchema: {\n spec: z.record(z.string(), z.unknown()).describe(\"{ type, data, encoding, options?, card?, theme? }\"),\n locale: z.string().optional().describe('Locale for the error and warning messages. Default \"en-US\", or the spec\\'s options.locale when set. \"ko\" or \"ko-KR\" gives Korean messages'),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ spec, locale }) => {\n // render_chart와 같은 로캘(locale, 그다음 스펙 options.locale)로 검사해요. 둘 다 없으면 영어\n const r = validate(spec, specLocale(spec as unknown as ChartSpec, locale));\n return { content: [json(r)] };\n },\n );\n\n server.registerTool(\n \"render_chart\",\n {\n title: \"Render chart\",\n description: \"Server-render a chart spec to PNG (returned as an image) and/or SVG text, with the card frame (number, title, legend, takeaway, level badge) when spec.card is set. Also returns ready-to-paste React code. data may be inline rows, { csv }, or { url } (fetched). Set locale for the language of built-in chart text and number formats.\",\n inputSchema: {\n spec: z.record(z.string(), z.unknown()).describe(\"Chart spec\"),\n format: z.enum([\"png\", \"svg\", \"both\"]).optional().describe(\"Default png\"),\n width: z.number().int().min(200).max(2400).optional().describe(\"Pixel width, default 720\"),\n theme: z.enum([\"dark\", \"light\"]).optional().describe(\"Default dark\"),\n scale: z.number().min(1).max(4).optional().describe(\"PNG pixel ratio, default 2\"),\n locale: z.string().optional().describe('Locale for built-in chart text and number formats. Default \"en-US\" (1.2K). \"ko\" or \"ko-KR\" gives Korean text and 1.2만 style numbers'),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ spec, format = \"png\", width, theme, scale, locale }) => {\n const s = spec as unknown as ChartSpec;\n // 그릴 때와 같은 로캘(locale, 그다음 스펙 options.locale)로 검사해요\n const v = validate(s, specLocale(s, locale));\n if (!v.valid) return { isError: true, content: [text(\"Invalid spec. Fix the errors below (the same result validate_spec returns).\"), json(v)] };\n try {\n const res = await render(s, { format: format === \"svg\" ? \"svg\" : \"png\", width, theme, scale, locale });\n const content: ({ type: \"text\"; text: string } | { type: \"image\"; data: string; mimeType: string })[] = [];\n if (format !== \"svg\" && res.png) content.push({ type: \"image\", data: res.png.toString(\"base64\"), mimeType: \"image/png\" });\n if ((format === \"svg\" || format === \"both\") && res.svg) content.push(text(res.svg));\n content.push(text(`Size ${res.width}×${res.height}px${v.warnings.length ? `\\nWarnings: ${v.warnings.map((w) => w.message).join(\" / \")}` : \"\"}`));\n content.push(text(`// React (components)\\n${reactSnippet(s)}\\n// React (spec as is)\\n${specSnippet(s)}`));\n return { content };\n } catch (e) {\n return { isError: true, content: [text(`Render failed: ${(e as Error).message}`)] };\n }\n },\n );\n\n server.registerTool(\n \"suggest_chart\",\n {\n title: \"Suggest charts for data\",\n description: \"Profile tabular data (column kinds, cardinality, hierarchy, source/target pairs, dates) and recommend chart types ranked by fit, each with a reason and a ready spec. Pass csv text, rows (array of objects), or a url to CSV/JSON (Google Sheets CSV export, REST). Set locale for the language of the reasons and the suggested specs.\",\n inputSchema: {\n csv: z.string().optional().describe(\"CSV text with header row\"),\n rows: z.array(z.record(z.string(), z.unknown())).optional().describe(\"Array of row objects\"),\n url: z.string().optional().describe(\"http(s) URL returning CSV or JSON\"),\n limit: z.number().int().min(1).max(10).optional().describe(\"Default 5\"),\n locale: z.string().optional().describe('Locale for chart names, reasons and card text in the suggestions. Default \"en-US\". \"ko\" or \"ko-KR\" gives Korean text and sets options.locale \"ko-KR\" in each spec'),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ csv, rows, url, limit, locale }) => {\n let data: Record<string, unknown>[];\n try {\n if (rows) data = normalizeData(rows);\n else if (csv) data = parseCSV(csv);\n else if (url) {\n // http(s)만 받아요. loadRows는 파일 경로도 읽어서, 그대로 두면 서버가 도는 컴퓨터의 파일을 돌려줄 수 있어요\n if (!isHttpUrl(url)) return { isError: true, content: [text(\"url must start with http:// or https://. For a local file, pass its text as csv or its rows as rows.\")] };\n data = await loadRows(url, locale);\n } else return { isError: true, content: [text(\"Pass one of csv, rows or url.\")] };\n } catch (e) {\n return { isError: true, content: [text(`Couldn't read the data: ${(e as Error).message}`)] };\n }\n const { profile, suggestions } = suggest(data, { limit: limit ?? 5, locale });\n return {\n content: [\n json({\n profile,\n suggestions: suggestions.map((s) => ({ type: s.type, name: s.name, score: s.score, reason: s.reason, spec: trimSpec(s.spec, 8), note: s.spec.data && Array.isArray(s.spec.data) && s.spec.data.length > 8 ? `spec.data shows only the first 8 rows. Put all ${s.spec.data.length} rows in the real spec.` : undefined })),\n }),\n ],\n };\n },\n );\n\n server.registerResource(\n \"chart-spec-schema\",\n \"ztd-charts://schema/chart-spec.json\",\n { title: \"ztd charts spec JSON Schema\", description: \"JSON Schema (draft 2020-12) for all chart specs\", mimeType: \"application/json\" },\n async (uri) => ({ contents: [{ uri: uri.href, mimeType: \"application/json\", text: JSON.stringify(getChartSchema(), null, 2) }] }),\n );\n\n return server;\n}\n"],"mappings":"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","names":[]}
|