@dotzero.ai/export-mcp 1.2.1
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 +474 -0
- package/dist/chart-engine.d.ts +29 -0
- package/dist/chart-engine.js +1 -0
- package/dist/colors.d.ts +21 -0
- package/dist/colors.js +1 -0
- package/dist/export-engine.d.ts +17 -0
- package/dist/export-engine.js +1 -0
- package/dist/file-output.d.ts +10 -0
- package/dist/file-output.js +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2 -0
- package/dist/schemas.d.ts +126 -0
- package/dist/schemas.js +1 -0
- package/dist/tool-registry.d.ts +10 -0
- package/dist/tool-registry.js +1 -0
- package/dist/tools/charts/bar.d.ts +214 -0
- package/dist/tools/charts/bar.js +1 -0
- package/dist/tools/charts/control-chart.d.ts +109 -0
- package/dist/tools/charts/control-chart.js +1 -0
- package/dist/tools/charts/gauge.d.ts +120 -0
- package/dist/tools/charts/gauge.js +1 -0
- package/dist/tools/charts/index.d.ts +9 -0
- package/dist/tools/charts/index.js +1 -0
- package/dist/tools/charts/line.d.ts +226 -0
- package/dist/tools/charts/line.js +1 -0
- package/dist/tools/charts/multi.d.ts +466 -0
- package/dist/tools/charts/multi.js +1 -0
- package/dist/tools/charts/oee-breakdown.d.ts +156 -0
- package/dist/tools/charts/oee-breakdown.js +1 -0
- package/dist/tools/charts/pie.d.ts +90 -0
- package/dist/tools/charts/pie.js +1 -0
- package/dist/tools/charts/scatter.d.ts +250 -0
- package/dist/tools/charts/scatter.js +1 -0
- package/dist/tools/charts/timeline.d.ts +128 -0
- package/dist/tools/charts/timeline.js +1 -0
- package/dist/tools/exports/csv.d.ts +51 -0
- package/dist/tools/exports/csv.js +1 -0
- package/dist/tools/exports/index.d.ts +2 -0
- package/dist/tools/exports/index.js +1 -0
- package/dist/tools/exports/xlsx.d.ts +85 -0
- package/dist/tools/exports/xlsx.js +1 -0
- package/dist/tools/smart/chart-from-json.d.ts +84 -0
- package/dist/tools/smart/chart-from-json.js +1 -0
- package/dist/tools/smart/index.d.ts +2 -0
- package/dist/tools/smart/index.js +1 -0
- package/dist/tools/smart/table-from-json.d.ts +63 -0
- package/dist/tools/smart/table-from-json.js +1 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.js +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# @dotzero.ai/export-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for chart generation and data export — PNG/JPG charts, CSV/XLSX files.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
A pure rendering engine that generates charts and exports data files. No authentication required, no external API calls. Designed to work alongside other DotZero MCP servers: fetch data with existing tools, then visualize or export with this package.
|
|
8
|
+
|
|
9
|
+
**Architecture**: `User → AI Agent → 1. Fetch data (existing MCP tools) → 2. Render/Export (export-mcp)`
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @dotzero.ai/setup
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or manually:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
claude mcp add dotzero-export --command "npx" --args "@dotzero.ai/export-mcp"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Tools (14)
|
|
24
|
+
|
|
25
|
+
### Generic Charts (5)
|
|
26
|
+
|
|
27
|
+
| Tool | Description | Key Parameters |
|
|
28
|
+
|------|-------------|----------------|
|
|
29
|
+
| `chart_bar` | Bar chart (vertical/horizontal/stacked) | `title`, `labels[]`, `datasets[]`, `options?` |
|
|
30
|
+
| `chart_line` | Line chart (multi-series, area fill) | `title`, `labels[]`, `datasets[]`, `options?` |
|
|
31
|
+
| `chart_pie` | Pie or doughnut chart | `title`, `labels[]`, `values[]`, `chart_type?` |
|
|
32
|
+
| `chart_scatter` | Scatter plot | `title`, `datasets[{data: [{x,y}]}]` |
|
|
33
|
+
| `chart_gauge` | Gauge/dial for single KPI | `value`, `label?`, `min?`, `max?`, `thresholds?` |
|
|
34
|
+
|
|
35
|
+
### DotZero-Specific Charts (4)
|
|
36
|
+
|
|
37
|
+
| Tool | Description | Data Source |
|
|
38
|
+
|------|-------------|-------------|
|
|
39
|
+
| `chart_oee_breakdown` | OEE A/Q/P breakdown chart | `oee_device`, `oee_devices`, `oee_line`, `oee_factory` |
|
|
40
|
+
| `chart_control` | SPC control chart (UCL/CL/LCL) | `spc_measure_history_*` + `spc_statistics_capability` |
|
|
41
|
+
| `chart_timeline` | Device state timeline | `equip_machine_status_history` |
|
|
42
|
+
| `chart_multi` | Multi-chart dashboard (max 9) | Multiple chart configs |
|
|
43
|
+
|
|
44
|
+
### Export (2)
|
|
45
|
+
|
|
46
|
+
| Tool | Description | Key Parameters |
|
|
47
|
+
|------|-------------|----------------|
|
|
48
|
+
| `export_csv` | Export to CSV file | `headers[]`, `rows[][]`, `filename?` |
|
|
49
|
+
| `export_xlsx` | Export to Excel XLSX (multi-sheet) | `sheets[{name, headers, rows}]`, `filename?` |
|
|
50
|
+
|
|
51
|
+
### Smart Tools (2)
|
|
52
|
+
|
|
53
|
+
| Tool | Description |
|
|
54
|
+
|------|-------------|
|
|
55
|
+
| `chart_from_json` | Auto-detect JSON data shape and pick best chart type |
|
|
56
|
+
| `export_table_from_json` | Auto-convert JSON to CSV/XLSX (extracts headers from keys) |
|
|
57
|
+
|
|
58
|
+
## Shared Parameters
|
|
59
|
+
|
|
60
|
+
All chart tools share these base parameters:
|
|
61
|
+
|
|
62
|
+
| Parameter | Type | Default | Description |
|
|
63
|
+
|-----------|------|---------|-------------|
|
|
64
|
+
| `output_path` | string | auto | Custom output file path |
|
|
65
|
+
| `width` | int | 800 | Image width in pixels (200-3000) |
|
|
66
|
+
| `height` | int | 500 | Image height in pixels (200-3000) |
|
|
67
|
+
| `format` | enum | `"png"` | `"png"` or `"jpg"` |
|
|
68
|
+
|
|
69
|
+
All export tools share:
|
|
70
|
+
|
|
71
|
+
| Parameter | Type | Default | Description |
|
|
72
|
+
|-----------|------|---------|-------------|
|
|
73
|
+
| `output_path` | string | auto | Custom output file path |
|
|
74
|
+
| `filename` | string | auto | Output filename (without extension) |
|
|
75
|
+
|
|
76
|
+
## Chart Options
|
|
77
|
+
|
|
78
|
+
Bar, line, and scatter charts accept an `options` object:
|
|
79
|
+
|
|
80
|
+
| Option | Type | Default | Description |
|
|
81
|
+
|--------|------|---------|-------------|
|
|
82
|
+
| `stacked` | boolean | false | Enable stacking |
|
|
83
|
+
| `horizontal` | boolean | false | Horizontal orientation (bar charts only) |
|
|
84
|
+
| `show_legend` | boolean | true | Show legend |
|
|
85
|
+
| `show_grid` | boolean | true | Show grid lines |
|
|
86
|
+
| `x_label` | string | - | X-axis label |
|
|
87
|
+
| `y_label` | string | - | Y-axis label |
|
|
88
|
+
| `min_y` | number | - | Y-axis minimum |
|
|
89
|
+
| `max_y` | number | - | Y-axis maximum |
|
|
90
|
+
|
|
91
|
+
## Tool Reference
|
|
92
|
+
|
|
93
|
+
### chart_bar
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
chart_bar(
|
|
97
|
+
title: "Production by Line",
|
|
98
|
+
labels: ["Line A", "Line B", "Line C"],
|
|
99
|
+
datasets: [
|
|
100
|
+
{ label: "Good", data: [1200, 980, 1150] },
|
|
101
|
+
{ label: "Defect", data: [23, 15, 31] }
|
|
102
|
+
],
|
|
103
|
+
options: { stacked: true, y_label: "Quantity" }
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### chart_line
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
chart_line(
|
|
111
|
+
title: "Weekly Production Trend",
|
|
112
|
+
labels: ["Mon", "Tue", "Wed", "Thu", "Fri"],
|
|
113
|
+
datasets: [
|
|
114
|
+
{ label: "Output", data: [450, 520, 480, 510, 530], fill: true }
|
|
115
|
+
],
|
|
116
|
+
options: { y_label: "Units" }
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### chart_pie
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
chart_pie(
|
|
124
|
+
title: "Defect Distribution",
|
|
125
|
+
labels: ["Scratch", "Dent", "Crack", "Other"],
|
|
126
|
+
values: [45, 28, 15, 12],
|
|
127
|
+
chart_type: "doughnut"
|
|
128
|
+
)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### chart_scatter
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
chart_scatter(
|
|
135
|
+
title: "Cycle Time vs Output",
|
|
136
|
+
datasets: [{
|
|
137
|
+
label: "Devices",
|
|
138
|
+
data: [
|
|
139
|
+
{ x: 30, y: 120 },
|
|
140
|
+
{ x: 45, y: 95 },
|
|
141
|
+
{ x: 25, y: 140 }
|
|
142
|
+
]
|
|
143
|
+
}],
|
|
144
|
+
options: { x_label: "Cycle Time (s)", y_label: "Output" }
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### chart_gauge
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
chart_gauge(
|
|
152
|
+
value: 85.2,
|
|
153
|
+
label: "OEE",
|
|
154
|
+
min: 0,
|
|
155
|
+
max: 100,
|
|
156
|
+
thresholds: [
|
|
157
|
+
{ value: 60, color: "#F44336" },
|
|
158
|
+
{ value: 80, color: "#FFC107" },
|
|
159
|
+
{ value: 100, color: "#4CAF50" }
|
|
160
|
+
]
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### chart_oee_breakdown
|
|
165
|
+
|
|
166
|
+
Single device:
|
|
167
|
+
```
|
|
168
|
+
chart_oee_breakdown(
|
|
169
|
+
title: "CNC-001 OEE",
|
|
170
|
+
availability: 92.5,
|
|
171
|
+
quality: 98.1,
|
|
172
|
+
performance: 87.3
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Multi-device comparison:
|
|
177
|
+
```
|
|
178
|
+
chart_oee_breakdown(
|
|
179
|
+
title: "Line A OEE Comparison",
|
|
180
|
+
availability: 0, quality: 0, performance: 0,
|
|
181
|
+
devices: [
|
|
182
|
+
{ name: "CNC-001", availability: 92.5, quality: 98.1, performance: 87.3 },
|
|
183
|
+
{ name: "CNC-002", availability: 88.0, quality: 96.5, performance: 91.2 },
|
|
184
|
+
{ name: "CNC-003", availability: 95.1, quality: 97.8, performance: 84.6 }
|
|
185
|
+
]
|
|
186
|
+
)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### chart_control
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
chart_control(
|
|
193
|
+
title: "Diameter Control Chart",
|
|
194
|
+
values: [10.02, 10.05, 9.98, 10.01, 10.12, 9.95, 10.03, 10.08],
|
|
195
|
+
labels: ["S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"],
|
|
196
|
+
ucl: 10.10,
|
|
197
|
+
cl: 10.00,
|
|
198
|
+
lcl: 9.90,
|
|
199
|
+
usl: 10.15,
|
|
200
|
+
lsl: 9.85
|
|
201
|
+
)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Points exceeding UCL/LCL are highlighted in red. USL/LSL specification limits are shown as orange dashed lines.
|
|
205
|
+
|
|
206
|
+
### chart_timeline
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
chart_timeline(
|
|
210
|
+
title: "CNC-001 State History",
|
|
211
|
+
device_name: "CNC-001",
|
|
212
|
+
segments: [
|
|
213
|
+
{ start: "2026-02-08T08:00:00Z", end: "2026-02-08T10:30:00Z", state: "running" },
|
|
214
|
+
{ start: "2026-02-08T10:30:00Z", end: "2026-02-08T11:00:00Z", state: "idle" },
|
|
215
|
+
{ start: "2026-02-08T11:00:00Z", end: "2026-02-08T11:45:00Z", state: "down" },
|
|
216
|
+
{ start: "2026-02-08T11:45:00Z", end: "2026-02-08T17:00:00Z", state: "running" }
|
|
217
|
+
]
|
|
218
|
+
)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Default state colors: running=green, idle=yellow, down=red, off=grey. Override with the `color` field per segment.
|
|
222
|
+
|
|
223
|
+
### chart_multi
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
chart_multi(
|
|
227
|
+
title: "Production Dashboard",
|
|
228
|
+
layout: "grid",
|
|
229
|
+
charts: [
|
|
230
|
+
{
|
|
231
|
+
type: "bar",
|
|
232
|
+
title: "Output by Line",
|
|
233
|
+
labels: ["Line A", "Line B"],
|
|
234
|
+
datasets: [{ label: "Output", data: [1200, 980] }]
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
type: "gauge",
|
|
238
|
+
title: "OEE",
|
|
239
|
+
value: 85.2,
|
|
240
|
+
label: "OEE"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
type: "pie",
|
|
244
|
+
title: "Defects",
|
|
245
|
+
labels: ["Scratch", "Dent"],
|
|
246
|
+
values: [65, 35]
|
|
247
|
+
}
|
|
248
|
+
]
|
|
249
|
+
)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Layout options: `grid` (auto rows/cols), `vertical` (1 column), `horizontal` (1 row). Max 9 sub-charts.
|
|
253
|
+
|
|
254
|
+
### export_csv
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
export_csv(
|
|
258
|
+
headers: ["Work Order", "Product", "Qty", "Status"],
|
|
259
|
+
rows: [
|
|
260
|
+
["WO-001", "Widget A", 100, "Completed"],
|
|
261
|
+
["WO-002", "Widget B", 250, "In Progress"],
|
|
262
|
+
["WO-003", "Widget A", 75, "Not Started"]
|
|
263
|
+
],
|
|
264
|
+
filename: "work-orders"
|
|
265
|
+
)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### export_xlsx
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
export_xlsx(
|
|
272
|
+
sheets: [
|
|
273
|
+
{
|
|
274
|
+
name: "Work Orders",
|
|
275
|
+
headers: ["ID", "Product", "Qty", "Status"],
|
|
276
|
+
rows: [
|
|
277
|
+
["WO-001", "Widget A", 100, "Completed"],
|
|
278
|
+
["WO-002", "Widget B", 250, "In Progress"]
|
|
279
|
+
]
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: "Summary",
|
|
283
|
+
headers: ["Metric", "Value"],
|
|
284
|
+
rows: [
|
|
285
|
+
["Total Orders", 2],
|
|
286
|
+
["Total Qty", 350]
|
|
287
|
+
]
|
|
288
|
+
}
|
|
289
|
+
],
|
|
290
|
+
filename: "production-report"
|
|
291
|
+
)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Headers are bold, column widths are auto-fit.
|
|
295
|
+
|
|
296
|
+
### chart_from_json
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
chart_from_json(
|
|
300
|
+
title: "Auto Chart",
|
|
301
|
+
data: <raw JSON from any DotZero tool>,
|
|
302
|
+
chart_type: "auto"
|
|
303
|
+
)
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Auto-detection rules:
|
|
307
|
+
- Objects with `availability`/`quality`/`performance` fields → OEE breakdown chart
|
|
308
|
+
- Array of OEE objects → Multi-device OEE comparison
|
|
309
|
+
- Single number → Gauge chart
|
|
310
|
+
- Array of objects with name + numeric fields → Bar chart
|
|
311
|
+
- Force `chart_type` to override: `"bar"`, `"line"`, `"pie"`, `"gauge"`, `"oee"`
|
|
312
|
+
|
|
313
|
+
### export_table_from_json
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
export_table_from_json(
|
|
317
|
+
data: <raw JSON from any DotZero tool>,
|
|
318
|
+
format: "xlsx",
|
|
319
|
+
filename: "export",
|
|
320
|
+
sheet_name: "Data"
|
|
321
|
+
)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Accepts arrays of objects, single objects, or paginated responses (`{ data: [...] }`). Automatically extracts column headers from object keys.
|
|
325
|
+
|
|
326
|
+
## Workflow Examples
|
|
327
|
+
|
|
328
|
+
### 1. OEE Breakdown Chart (OEE 分解圖)
|
|
329
|
+
|
|
330
|
+
```
|
|
331
|
+
Step 1: oee_device(device_uuid: "...", start_time: "...", end_time: "...", response_format: "json")
|
|
332
|
+
Step 2: chart_oee_breakdown(title: "CNC-001 OEE", availability: 92.5, quality: 98.1, performance: 87.3)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Or use the smart tool:
|
|
336
|
+
```
|
|
337
|
+
Step 1: oee_devices(line_uuid: "...", response_format: "json")
|
|
338
|
+
Step 2: chart_from_json(title: "Line A OEE", data: <result from step 1>)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### 2. SPC Control Chart (SPC 管制圖)
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
Step 1: spc_measure_history_manufacture(config_uuid: "...", response_format: "json")
|
|
345
|
+
Step 2: spc_statistics_capability(config_uuid: "...", response_format: "json")
|
|
346
|
+
Step 3: chart_control(
|
|
347
|
+
title: "Diameter Control",
|
|
348
|
+
values: [extracted from step 1],
|
|
349
|
+
ucl: [from step 2],
|
|
350
|
+
cl: [from step 2],
|
|
351
|
+
lcl: [from step 2]
|
|
352
|
+
)
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### 3. Production Report Excel Export (生產報表匯出)
|
|
356
|
+
|
|
357
|
+
```
|
|
358
|
+
Step 1: workorder_list(status: 3, response_format: "json")
|
|
359
|
+
Step 2: export_table_from_json(data: <result>, format: "xlsx", filename: "completed-orders")
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Or with custom formatting:
|
|
363
|
+
```
|
|
364
|
+
Step 1: production_summary(response_format: "json")
|
|
365
|
+
Step 2: export_xlsx(
|
|
366
|
+
sheets: [
|
|
367
|
+
{ name: "Summary", headers: [...], rows: [...] },
|
|
368
|
+
{ name: "Details", headers: [...], rows: [...] }
|
|
369
|
+
],
|
|
370
|
+
filename: "production-report"
|
|
371
|
+
)
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### 4. Worker Efficiency Ranking (作業員效率排名)
|
|
375
|
+
|
|
376
|
+
```
|
|
377
|
+
Step 1: worker_efficiency_ranking(response_format: "json")
|
|
378
|
+
Step 2: chart_bar(
|
|
379
|
+
title: "Worker Efficiency",
|
|
380
|
+
labels: [worker names],
|
|
381
|
+
datasets: [{ label: "Efficiency %", data: [values] }],
|
|
382
|
+
options: { horizontal: true, y_label: "Worker" }
|
|
383
|
+
)
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### 5. Device State Distribution (設備狀態分佈)
|
|
387
|
+
|
|
388
|
+
```
|
|
389
|
+
Step 1: equip_state_counts_factory(factory_uuid: "...", response_format: "json")
|
|
390
|
+
Step 2: chart_pie(
|
|
391
|
+
title: "Device States",
|
|
392
|
+
labels: ["Running", "Idle", "Down", "Off"],
|
|
393
|
+
values: [running_count, idle_count, down_count, off_count],
|
|
394
|
+
colors: ["#4CAF50", "#FFC107", "#F44336", "#9E9E9E"]
|
|
395
|
+
)
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### 6. Multi-Chart Dashboard (多圖表儀表板)
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
Step 1: Fetch OEE, production summary, and defect data
|
|
402
|
+
Step 2: chart_multi(
|
|
403
|
+
title: "Daily Dashboard",
|
|
404
|
+
layout: "grid",
|
|
405
|
+
charts: [
|
|
406
|
+
{ type: "gauge", title: "OEE", value: 85.2, label: "OEE" },
|
|
407
|
+
{ type: "bar", title: "Output", labels: [...], datasets: [...] },
|
|
408
|
+
{ type: "pie", title: "Defects", labels: [...], values: [...] },
|
|
409
|
+
{ type: "line", title: "Trend", labels: [...], datasets: [...] }
|
|
410
|
+
]
|
|
411
|
+
)
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## File Output
|
|
415
|
+
|
|
416
|
+
- Default path: `.dotzero/exports/<type>-<name>-<timestamp>.<ext>`
|
|
417
|
+
- `.dotzero/` is in `.gitignore` — files won't be committed
|
|
418
|
+
- Override with `output_path` parameter on any tool
|
|
419
|
+
|
|
420
|
+
## Output Format
|
|
421
|
+
|
|
422
|
+
Chart tools return:
|
|
423
|
+
```json
|
|
424
|
+
{
|
|
425
|
+
"content": [
|
|
426
|
+
{ "type": "text", "text": "Chart saved to: .dotzero/exports/chart-bar-2026-02-08T14-30-00.png (800x500)" },
|
|
427
|
+
{ "type": "image", "data": "<base64>", "mimeType": "image/png" }
|
|
428
|
+
]
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Export tools return:
|
|
433
|
+
```json
|
|
434
|
+
{
|
|
435
|
+
"content": [
|
|
436
|
+
{ "type": "text", "text": "Exported 42 rows to: .dotzero/exports/export-2026-02-08.csv\n\nPreview (first 5 rows):\n| Col1 | Col2 |\n|---|---|\n| ... | ... |" }
|
|
437
|
+
]
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
## DotZero Color Palette
|
|
442
|
+
|
|
443
|
+
| Name | Color | Hex | Usage |
|
|
444
|
+
|------|-------|-----|-------|
|
|
445
|
+
| availability | Green | `#4CAF50` | OEE Availability |
|
|
446
|
+
| quality | Blue | `#2196F3` | OEE Quality |
|
|
447
|
+
| performance | Orange | `#FF9800` | OEE Performance |
|
|
448
|
+
| oee | Purple | `#9C27B0` | Combined OEE |
|
|
449
|
+
| good/running | Green | `#4CAF50` | Good parts, running state |
|
|
450
|
+
| defect/down | Red | `#F44336` | Defects, down state |
|
|
451
|
+
| idle | Yellow | `#FFC107` | Idle state |
|
|
452
|
+
| off | Grey | `#9E9E9E` | Off state |
|
|
453
|
+
|
|
454
|
+
## Dependencies
|
|
455
|
+
|
|
456
|
+
| Library | Purpose |
|
|
457
|
+
|---------|---------|
|
|
458
|
+
| `chartjs-node-canvas` + `chart.js` v4 | Server-side chart rendering |
|
|
459
|
+
| `@napi-rs/canvas` | Native canvas backend (no cairo/pango) |
|
|
460
|
+
| `chartjs-plugin-annotation` | UCL/CL/LCL horizontal lines |
|
|
461
|
+
| `exceljs` | Excel XLSX generation |
|
|
462
|
+
| `zod` | Input schema validation |
|
|
463
|
+
|
|
464
|
+
## Build
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
cd packages/export-mcp
|
|
468
|
+
pnpm install
|
|
469
|
+
pnpm build
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## License
|
|
473
|
+
|
|
474
|
+
MIT
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chart rendering engine using chartjs-node-canvas.
|
|
3
|
+
* Manages canvas instances cached by dimension.
|
|
4
|
+
*/
|
|
5
|
+
import type { ChartConfiguration } from "chart.js";
|
|
6
|
+
import type { ChartResult } from "./types.js";
|
|
7
|
+
export interface RenderOptions {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
format: "png" | "jpg";
|
|
11
|
+
outputPath?: string;
|
|
12
|
+
title: string;
|
|
13
|
+
}
|
|
14
|
+
/** Render a Chart.js configuration to an image file */
|
|
15
|
+
export declare function renderChart(config: ChartConfiguration, opts: RenderOptions): Promise<ChartResult>;
|
|
16
|
+
/** Build MCP content blocks from a ChartResult */
|
|
17
|
+
export declare function chartResultToContent(result: ChartResult): {
|
|
18
|
+
content: ({
|
|
19
|
+
type: "text";
|
|
20
|
+
text: string;
|
|
21
|
+
data?: undefined;
|
|
22
|
+
mimeType?: undefined;
|
|
23
|
+
} | {
|
|
24
|
+
type: "image";
|
|
25
|
+
data: string;
|
|
26
|
+
mimeType: "image/png" | "image/jpeg";
|
|
27
|
+
text?: undefined;
|
|
28
|
+
})[];
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ChartJSNodeCanvas}from"chartjs-node-canvas";import{resolveOutputPath,writeBinary}from"./file-output.js";const canvasCache=new Map;function getCanvas(t,e){const a=`${t}x${e}`;let n=canvasCache.get(a);return n||(n=new ChartJSNodeCanvas({width:t,height:e,backgroundColour:"white",plugins:{modern:["chartjs-plugin-annotation"]}}),canvasCache.set(a,n)),n}export async function renderChart(t,e){const a=getCanvas(e.width,e.height);let n;n="jpg"===e.format?await a.renderToBuffer(t,"image/jpeg"):await a.renderToBuffer(t,"image/png");const r="jpg"===e.format?"jpg":"png",i="jpg"===e.format?"image/jpeg":"image/png",o=resolveOutputPath(e.outputPath,"chart",e.title,r);return writeBinary(o,n),{filePath:o,width:e.width,height:e.height,base64:n.toString("base64"),mimeType:i}}export function chartResultToContent(t){return{content:[{type:"text",text:`Chart saved to: ${t.filePath} (${t.width}x${t.height})`},{type:"image",data:t.base64,mimeType:t.mimeType}]}}
|
package/dist/colors.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DotZero color palette for charts
|
|
3
|
+
*/
|
|
4
|
+
export declare const DOTZERO_COLORS: {
|
|
5
|
+
readonly availability: "#4CAF50";
|
|
6
|
+
readonly quality: "#2196F3";
|
|
7
|
+
readonly performance: "#FF9800";
|
|
8
|
+
readonly oee: "#9C27B0";
|
|
9
|
+
readonly good: "#4CAF50";
|
|
10
|
+
readonly defect: "#F44336";
|
|
11
|
+
readonly running: "#4CAF50";
|
|
12
|
+
readonly idle: "#FFC107";
|
|
13
|
+
readonly down: "#F44336";
|
|
14
|
+
readonly off: "#9E9E9E";
|
|
15
|
+
};
|
|
16
|
+
/** Default series colors for multi-dataset charts */
|
|
17
|
+
export declare const SERIES_COLORS: readonly ["#2196F3", "#4CAF50", "#FF9800", "#F44336", "#9C27B0", "#00BCD4", "#FF5722", "#795548", "#607D8B", "#E91E63", "#3F51B5", "#009688", "#CDDC39", "#FFC107", "#8BC34A"];
|
|
18
|
+
/** Get a color for a series index (cycles through palette) */
|
|
19
|
+
export declare function getSeriesColor(index: number): string;
|
|
20
|
+
/** Add alpha transparency to a hex color */
|
|
21
|
+
export declare function withAlpha(hex: string, alpha: number): string;
|
package/dist/colors.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DOTZERO_COLORS={availability:"#4CAF50",quality:"#2196F3",performance:"#FF9800",oee:"#9C27B0",good:"#4CAF50",defect:"#F44336",running:"#4CAF50",idle:"#FFC107",down:"#F44336",off:"#9E9E9E"};export const SERIES_COLORS=["#2196F3","#4CAF50","#FF9800","#F44336","#9C27B0","#00BCD4","#FF5722","#795548","#607D8B","#E91E63","#3F51B5","#009688","#CDDC39","#FFC107","#8BC34A"];export function getSeriesColor(e){return SERIES_COLORS[e%SERIES_COLORS.length]}export function withAlpha(e,F){return`rgba(${parseInt(e.slice(1,3),16)}, ${parseInt(e.slice(3,5),16)}, ${parseInt(e.slice(5,7),16)}, ${F})`}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data export engine — CSV and XLSX generation.
|
|
3
|
+
*/
|
|
4
|
+
import type { ExportResult, ExportSheet } from "./types.js";
|
|
5
|
+
/** Generate a CSV string from headers and rows */
|
|
6
|
+
export declare function generateCsvString(headers: string[], rows: (string | number | boolean | null)[][]): string;
|
|
7
|
+
/** Generate CSV file and return result */
|
|
8
|
+
export declare function generateCsv(headers: string[], rows: (string | number | boolean | null)[][], filename?: string, outputPath?: string): ExportResult;
|
|
9
|
+
/** Generate XLSX file and return result */
|
|
10
|
+
export declare function generateXlsx(sheets: ExportSheet[], filename?: string, outputPath?: string): Promise<ExportResult>;
|
|
11
|
+
/** Build MCP content blocks from an ExportResult */
|
|
12
|
+
export declare function exportResultToContent(result: ExportResult): {
|
|
13
|
+
content: {
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
}[];
|
|
17
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import ExcelJS from"exceljs";import{resolveOutputPath,writeText}from"./file-output.js";function csvEscape(e){if(null==e)return"";const t=String(e);return t.includes(",")||t.includes('"')||t.includes("\n")?`"${t.replace(/"/g,'""')}"`:t}export function generateCsvString(e,t){const n=[e.map(csvEscape).join(",")];for(const e of t)n.push(e.map(csvEscape).join(","));return n.join("\n")+"\n"}export function generateCsv(e,t,n,o){const r=generateCsvString(e,t),s=resolveOutputPath(o,"export",n||"export","csv");return writeText(s,r),{filePath:s,rowCount:t.length,preview:buildPreview(e,t,5)}}export async function generateXlsx(e,t,n){const o=new ExcelJS.Workbook;let r=0;for(const t of e){const e=o.addWorksheet(t.name);e.addRow(t.headers).font={bold:!0};for(const n of t.rows)e.addRow(n.map(e=>null===e?"":e));e.columns.forEach((e,n)=>{let o=(t.headers[n]||"").length;for(const e of t.rows){const t=String(e[n]??"").length;t>o&&(o=t)}e.width=Math.min(Math.max(o+2,8),50)}),r+=t.rows.length}const s=resolveOutputPath(n,"export",t||"export","xlsx");await o.xlsx.writeFile(s);const i=e[0];return{filePath:s,rowCount:r,preview:i?buildPreview(i.headers,i.rows,5):"(empty)"}}function buildPreview(e,t,n){const o=["| "+e.join(" | ")+" |","| "+e.map(()=>"---").join(" | ")+" |",...t.slice(0,n).map(e=>"| "+e.map(e=>null===e?"":String(e)).join(" | ")+" |")];return t.length>n&&o.push(`| ... (${t.length-n} more rows) | ${e.slice(1).map(()=>"").join(" | ")} |`),o.join("\n")}export function exportResultToContent(e){return{content:[{type:"text",text:`Exported ${e.rowCount} rows to: ${e.filePath}\n\nPreview (first 5 rows):\n${e.preview}`}]}}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File output utilities for export-mcp.
|
|
3
|
+
* Default output directory: .dotzero/exports/
|
|
4
|
+
*/
|
|
5
|
+
/** Resolve output path — use custom path or generate default */
|
|
6
|
+
export declare function resolveOutputPath(customPath: string | undefined, prefix: string, name: string, ext: string): string;
|
|
7
|
+
/** Write binary data (PNG/JPG) */
|
|
8
|
+
export declare function writeBinary(filePath: string, data: Buffer): void;
|
|
9
|
+
/** Write text data (CSV) */
|
|
10
|
+
export declare function writeText(filePath: string, data: string): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{existsSync,mkdirSync,writeFileSync}from"node:fs";import{dirname,join,resolve}from"node:path";const DEFAULT_DIR=".dotzero/exports";function ensureDir(e){existsSync(e)||mkdirSync(e,{recursive:!0})}function timestamp(){return(new Date).toISOString().replace(/[:.]/g,"-").slice(0,19)}export function resolveOutputPath(e,r,t,i){if(e){const r=resolve(e),t=process.cwd(),i=resolve(DEFAULT_DIR);if(!r.startsWith(t+"/")&&r!==t&&!r.startsWith(i+"/")&&r!==i)throw new Error(`Output path must be within the project directory. Got: "${e}"`);return ensureDir(dirname(r)),r}const n=resolve(DEFAULT_DIR);ensureDir(n);const o=t.replace(/[^a-zA-Z0-9_-]/g,"-").toLowerCase();return join(n,`${r}-${o}-${timestamp()}.${i}`)}export function writeBinary(e,r){ensureDir(dirname(e)),writeFileSync(e,r)}export function writeText(e,r){ensureDir(dirname(e)),writeFileSync(e,r,"utf-8")}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{McpServer}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport}from"@modelcontextprotocol/sdk/server/stdio.js";import{StreamableHTTPServerTransport}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import express from"express";import{registerAllTools}from"./tool-registry.js";const server=new McpServer({name:"export-mcp-server",version:"1.0.0"});async function runStdio(){const r=new StdioServerTransport;await server.connect(r),console.error("Export MCP Server running via stdio")}async function runHTTP(){const r=express();r.use(express.json()),r.post("/mcp",async(r,e)=>{const o=new StreamableHTTPServerTransport({sessionIdGenerator:void 0,enableJsonResponse:!0});e.on("close",()=>o.close()),await server.connect(o),await o.handleRequest(r,e,r.body)}),r.get("/health",(r,e)=>{e.json({status:"ok",name:"export-mcp-server",version:"1.0.0"})});const e=parseInt(process.env.PORT||"3001");r.listen(e,()=>{console.error(`Export MCP Server running on http://localhost:${e}/mcp`)})}registerAllTools(server);const transport=process.env.TRANSPORT||"stdio";"http"===transport?runHTTP().catch(r=>{console.error("Server error:",r),process.exit(1)}):runStdio().catch(r=>{console.error("Server error:",r),process.exit(1)});
|