@vint.tri/report_gen_mcp 1.1.0 → 1.1.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/FIX_SUMMARY.md +103 -0
- package/README.md +5 -5
- package/dist/charts/bar.js +33 -22
- package/dist/charts/doughnut.js +49 -0
- package/dist/charts/line.js +33 -22
- package/dist/charts/pie.js +33 -22
- package/dist/charts/polarArea.js +49 -0
- package/dist/charts/radar.js +50 -0
- package/dist/index.js +23 -12
- package/dist/utils/reportGenerator.js +17 -9
- package/dollar_rate_charts.json +35 -0
- package/dollar_rate_report.md +7 -0
- package/dollar_rate_report_fixed.json +39 -0
- package/generate_report.sh +15 -0
- package/package.json +5 -6
- package/test_fixed_report.js +56 -0
package/FIX_SUMMARY.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Report Generation Issue Fix Summary
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
The report generation was failing with the following error:
|
|
5
|
+
```
|
|
6
|
+
Error calling tool generate-report: Error: Error invoking remote method 'mcp:call-tool': McpError: MCP error -32602: MCP error -32602: Invalid arguments for tool generate-report: [
|
|
7
|
+
{
|
|
8
|
+
"code": "invalid_type",
|
|
9
|
+
"expected": "array",
|
|
10
|
+
"received": "string",
|
|
11
|
+
"path": [
|
|
12
|
+
"charts",
|
|
13
|
+
"dollar_rate_chart",
|
|
14
|
+
"config",
|
|
15
|
+
"datasets",
|
|
16
|
+
0,
|
|
17
|
+
"backgroundColor"
|
|
18
|
+
],
|
|
19
|
+
"message": "Expected array, received string"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"code": "invalid_type",
|
|
23
|
+
"expected": "array",
|
|
24
|
+
"received": "string",
|
|
25
|
+
"path": [
|
|
26
|
+
"charts",
|
|
27
|
+
"dollar_rate_chart",
|
|
28
|
+
"config",
|
|
29
|
+
"datasets",
|
|
30
|
+
0,
|
|
31
|
+
"borderColor"
|
|
32
|
+
],
|
|
33
|
+
"message": "Expected array, received string"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Root Cause
|
|
39
|
+
The issue was in the chart configuration where `backgroundColor` and `borderColor` properties were provided as strings instead of arrays of strings. According to the schema validation in `src/charts/line.ts`, these properties are expected to be arrays:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
export const lineSchema = z.object({
|
|
43
|
+
labels: z.array(z.string()),
|
|
44
|
+
datasets: z.array(z.object({
|
|
45
|
+
label: z.string(),
|
|
46
|
+
data: z.array(z.number()),
|
|
47
|
+
borderColor: z.array(z.string()).optional(),
|
|
48
|
+
fill: z.boolean().optional(),
|
|
49
|
+
})),
|
|
50
|
+
options: z.object({
|
|
51
|
+
title: z.string().optional(),
|
|
52
|
+
}).optional(),
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Solution
|
|
57
|
+
I fixed the configuration by:
|
|
58
|
+
1. Removing the `backgroundColor` property entirely since it's not defined in the schema
|
|
59
|
+
2. Changing `borderColor` from a string `"rgba(54, 162, 235, 1)"` to an array `["rgba(54, 162, 235, 1)"]`
|
|
60
|
+
|
|
61
|
+
## Fixed Configuration
|
|
62
|
+
The corrected chart configuration is:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"dollar_rate_chart": {
|
|
67
|
+
"type": "line",
|
|
68
|
+
"config": {
|
|
69
|
+
"labels": [
|
|
70
|
+
"16.08.2025",
|
|
71
|
+
"19.08.2025",
|
|
72
|
+
"20.08.2025",
|
|
73
|
+
"21.08.2025",
|
|
74
|
+
"22.08.2025",
|
|
75
|
+
"23.08.2025"
|
|
76
|
+
],
|
|
77
|
+
"datasets": [
|
|
78
|
+
{
|
|
79
|
+
"label": "Курс USD/RUB",
|
|
80
|
+
"data": [
|
|
81
|
+
80.0224,
|
|
82
|
+
80.4256,
|
|
83
|
+
80.3466,
|
|
84
|
+
80.1045,
|
|
85
|
+
80.2548,
|
|
86
|
+
80.7498
|
|
87
|
+
],
|
|
88
|
+
"borderColor": [
|
|
89
|
+
"rgba(54, 162, 235, 1)"
|
|
90
|
+
],
|
|
91
|
+
"fill": false
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"options": {
|
|
95
|
+
"title": "Динамика курса доллара США к рублю"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Verification
|
|
103
|
+
The report was successfully generated using the fixed configuration and can be found at `test_reports/dollar_rate_report.html`.
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# Report Generator MCP Tool
|
|
1
|
+
# Report Generator MCP Tool v1.1.1
|
|
2
2
|
|
|
3
|
-
A powerful CLI tool for generating HTML reports with embedded charts, designed to work seamlessly with Claude Desktop as an MCP (Model Context Protocol) extension.
|
|
3
|
+
A powerful CLI tool for generating HTML reports with embedded interactive charts, designed to work seamlessly with Claude Desktop as an MCP (Model Context Protocol) extension.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- Generate professional HTML reports from Markdown documents
|
|
8
|
-
- Embed customizable charts (bar, line, pie) directly in reports
|
|
8
|
+
- Embed customizable interactive charts (bar, line, pie, doughnut, radar, polarArea) directly in reports
|
|
9
9
|
- Dual operation modes:
|
|
10
10
|
- Command-line interface for direct usage
|
|
11
11
|
- Stdio mode for Claude Desktop integration
|
|
@@ -131,7 +131,7 @@ Different regions show varying performance levels.
|
|
|
131
131
|
|
|
132
132
|
The `charts` parameter is an object where each key is a chart ID that matches a placeholder in your document, and each value is a chart configuration object with:
|
|
133
133
|
|
|
134
|
-
- `type`: The chart type ("bar", "line", or "
|
|
134
|
+
- `type`: The chart type ("bar", "line", "pie", "doughnut", "radar", or "polarArea")
|
|
135
135
|
- `config`: The Chart.js configuration for that chart
|
|
136
136
|
|
|
137
137
|
#### Required Chart Data Structure
|
|
@@ -389,7 +389,7 @@ npm test
|
|
|
389
389
|
**Parameters:**
|
|
390
390
|
- `document` (string): Markdown document with chart placeholders `[[chart:id]]`
|
|
391
391
|
- `charts` (object): Chart configurations mapped by ID
|
|
392
|
-
- `type` (string): Chart type ("bar", "line", or "
|
|
392
|
+
- `type` (string): Chart type ("bar", "line", "pie", "doughnut", "radar", or "polarArea")
|
|
393
393
|
- `config` (object): Chart.js configuration object
|
|
394
394
|
- `outputFile` (string, optional): Output HTML file path (defaults to "report.html")
|
|
395
395
|
- `tempDirectory` (string, **required for Claude Desktop**): Temporary directory for file storage. This parameter is required when using the tool with Claude Desktop to prevent read-only file system errors.
|
package/dist/charts/bar.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { ChartJSNodeCanvas } from 'chartjs-node-canvas';
|
|
3
2
|
export const barSchema = z.object({
|
|
4
3
|
labels: z.array(z.string()),
|
|
5
4
|
datasets: z.array(z.object({
|
|
@@ -14,26 +13,38 @@ export const barSchema = z.object({
|
|
|
14
13
|
}).optional(),
|
|
15
14
|
});
|
|
16
15
|
export async function renderBarChart(config) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
16
|
+
// Generate a unique ID for the chart container
|
|
17
|
+
const chartId = `chart_${Math.random().toString(36).substr(2, 9)}`;
|
|
18
|
+
// Prepare the chart data as JSON
|
|
19
|
+
const chartData = {
|
|
20
|
+
labels: config.labels,
|
|
21
|
+
datasets: config.datasets,
|
|
22
|
+
};
|
|
23
|
+
// Prepare the chart options
|
|
24
|
+
const chartOptions = {
|
|
25
|
+
...config.options,
|
|
26
|
+
plugins: {
|
|
27
|
+
title: config.options?.title ? {
|
|
28
|
+
display: true,
|
|
29
|
+
text: config.options.title
|
|
30
|
+
} : undefined
|
|
31
|
+
}
|
|
32
32
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
// Return interactive Chart.js HTML
|
|
34
|
+
return `
|
|
35
|
+
<div>
|
|
36
|
+
<canvas id="${chartId}" width="800" height="600"></canvas>
|
|
37
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
38
|
+
<script>
|
|
39
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
40
|
+
const ctx = document.getElementById('${chartId}').getContext('2d');
|
|
41
|
+
new Chart(ctx, {
|
|
42
|
+
type: 'bar',
|
|
43
|
+
data: ${JSON.stringify(chartData)},
|
|
44
|
+
options: ${JSON.stringify(chartOptions)}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
</script>
|
|
48
|
+
</div>
|
|
49
|
+
`.trim();
|
|
39
50
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const doughnutSchema = z.object({
|
|
3
|
+
labels: z.array(z.string()),
|
|
4
|
+
datasets: z.array(z.object({
|
|
5
|
+
data: z.array(z.number()),
|
|
6
|
+
backgroundColor: z.array(z.string()).optional(),
|
|
7
|
+
borderColor: z.array(z.string()).optional(),
|
|
8
|
+
})),
|
|
9
|
+
options: z.object({
|
|
10
|
+
title: z.string().optional(),
|
|
11
|
+
// Add more Chart.js options as needed
|
|
12
|
+
}).optional(),
|
|
13
|
+
});
|
|
14
|
+
export async function renderDoughnutChart(config) {
|
|
15
|
+
// Generate a unique ID for the chart container
|
|
16
|
+
const chartId = `chart_${Math.random().toString(36).substr(2, 9)}`;
|
|
17
|
+
// Prepare the chart data as JSON
|
|
18
|
+
const chartData = {
|
|
19
|
+
labels: config.labels,
|
|
20
|
+
datasets: config.datasets,
|
|
21
|
+
};
|
|
22
|
+
// Prepare the chart options
|
|
23
|
+
const chartOptions = {
|
|
24
|
+
...config.options,
|
|
25
|
+
plugins: {
|
|
26
|
+
title: config.options?.title ? {
|
|
27
|
+
display: true,
|
|
28
|
+
text: config.options.title
|
|
29
|
+
} : undefined
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
// Return interactive Chart.js HTML
|
|
33
|
+
return `
|
|
34
|
+
<div>
|
|
35
|
+
<canvas id="${chartId}" width="800" height="600"></canvas>
|
|
36
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
37
|
+
<script>
|
|
38
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
39
|
+
const ctx = document.getElementById('${chartId}').getContext('2d');
|
|
40
|
+
new Chart(ctx, {
|
|
41
|
+
type: 'doughnut',
|
|
42
|
+
data: ${JSON.stringify(chartData)},
|
|
43
|
+
options: ${JSON.stringify(chartOptions)}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
</script>
|
|
47
|
+
</div>
|
|
48
|
+
`.trim();
|
|
49
|
+
}
|
package/dist/charts/line.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { ChartJSNodeCanvas } from 'chartjs-node-canvas';
|
|
3
2
|
export const lineSchema = z.object({
|
|
4
3
|
labels: z.array(z.string()),
|
|
5
4
|
datasets: z.array(z.object({
|
|
@@ -14,26 +13,38 @@ export const lineSchema = z.object({
|
|
|
14
13
|
}).optional(),
|
|
15
14
|
});
|
|
16
15
|
export async function renderLineChart(config) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
16
|
+
// Generate a unique ID for the chart container
|
|
17
|
+
const chartId = `chart_${Math.random().toString(36).substr(2, 9)}`;
|
|
18
|
+
// Prepare the chart data as JSON
|
|
19
|
+
const chartData = {
|
|
20
|
+
labels: config.labels,
|
|
21
|
+
datasets: config.datasets,
|
|
22
|
+
};
|
|
23
|
+
// Prepare the chart options
|
|
24
|
+
const chartOptions = {
|
|
25
|
+
...config.options,
|
|
26
|
+
plugins: {
|
|
27
|
+
title: config.options?.title ? {
|
|
28
|
+
display: true,
|
|
29
|
+
text: config.options.title
|
|
30
|
+
} : undefined
|
|
31
|
+
}
|
|
32
32
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
// Return interactive Chart.js HTML
|
|
34
|
+
return `
|
|
35
|
+
<div>
|
|
36
|
+
<canvas id="${chartId}" width="800" height="600"></canvas>
|
|
37
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
38
|
+
<script>
|
|
39
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
40
|
+
const ctx = document.getElementById('${chartId}').getContext('2d');
|
|
41
|
+
new Chart(ctx, {
|
|
42
|
+
type: 'line',
|
|
43
|
+
data: ${JSON.stringify(chartData)},
|
|
44
|
+
options: ${JSON.stringify(chartOptions)}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
</script>
|
|
48
|
+
</div>
|
|
49
|
+
`.trim();
|
|
39
50
|
}
|
package/dist/charts/pie.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { ChartJSNodeCanvas } from 'chartjs-node-canvas';
|
|
3
2
|
export const pieSchema = z.object({
|
|
4
3
|
labels: z.array(z.string()),
|
|
5
4
|
datasets: z.array(z.object({
|
|
@@ -13,26 +12,38 @@ export const pieSchema = z.object({
|
|
|
13
12
|
}).optional(),
|
|
14
13
|
});
|
|
15
14
|
export async function renderPieChart(config) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
15
|
+
// Generate a unique ID for the chart container
|
|
16
|
+
const chartId = `chart_${Math.random().toString(36).substr(2, 9)}`;
|
|
17
|
+
// Prepare the chart data as JSON
|
|
18
|
+
const chartData = {
|
|
19
|
+
labels: config.labels,
|
|
20
|
+
datasets: config.datasets,
|
|
21
|
+
};
|
|
22
|
+
// Prepare the chart options
|
|
23
|
+
const chartOptions = {
|
|
24
|
+
...config.options,
|
|
25
|
+
plugins: {
|
|
26
|
+
title: config.options?.title ? {
|
|
27
|
+
display: true,
|
|
28
|
+
text: config.options.title
|
|
29
|
+
} : undefined
|
|
30
|
+
}
|
|
31
31
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
// Return interactive Chart.js HTML
|
|
33
|
+
return `
|
|
34
|
+
<div>
|
|
35
|
+
<canvas id="${chartId}" width="800" height="600"></canvas>
|
|
36
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
37
|
+
<script>
|
|
38
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
39
|
+
const ctx = document.getElementById('${chartId}').getContext('2d');
|
|
40
|
+
new Chart(ctx, {
|
|
41
|
+
type: 'pie',
|
|
42
|
+
data: ${JSON.stringify(chartData)},
|
|
43
|
+
options: ${JSON.stringify(chartOptions)}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
</script>
|
|
47
|
+
</div>
|
|
48
|
+
`.trim();
|
|
38
49
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const polarAreaSchema = z.object({
|
|
3
|
+
labels: z.array(z.string()),
|
|
4
|
+
datasets: z.array(z.object({
|
|
5
|
+
data: z.array(z.number()),
|
|
6
|
+
backgroundColor: z.array(z.string()).optional(),
|
|
7
|
+
borderColor: z.array(z.string()).optional(),
|
|
8
|
+
})),
|
|
9
|
+
options: z.object({
|
|
10
|
+
title: z.string().optional(),
|
|
11
|
+
// Add more Chart.js options as needed
|
|
12
|
+
}).optional(),
|
|
13
|
+
});
|
|
14
|
+
export async function renderPolarAreaChart(config) {
|
|
15
|
+
// Generate a unique ID for the chart container
|
|
16
|
+
const chartId = `chart_${Math.random().toString(36).substr(2, 9)}`;
|
|
17
|
+
// Prepare the chart data as JSON
|
|
18
|
+
const chartData = {
|
|
19
|
+
labels: config.labels,
|
|
20
|
+
datasets: config.datasets,
|
|
21
|
+
};
|
|
22
|
+
// Prepare the chart options
|
|
23
|
+
const chartOptions = {
|
|
24
|
+
...config.options,
|
|
25
|
+
plugins: {
|
|
26
|
+
title: config.options?.title ? {
|
|
27
|
+
display: true,
|
|
28
|
+
text: config.options.title
|
|
29
|
+
} : undefined
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
// Return interactive Chart.js HTML
|
|
33
|
+
return `
|
|
34
|
+
<div>
|
|
35
|
+
<canvas id="${chartId}" width="800" height="600"></canvas>
|
|
36
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
37
|
+
<script>
|
|
38
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
39
|
+
const ctx = document.getElementById('${chartId}').getContext('2d');
|
|
40
|
+
new Chart(ctx, {
|
|
41
|
+
type: 'polarArea',
|
|
42
|
+
data: ${JSON.stringify(chartData)},
|
|
43
|
+
options: ${JSON.stringify(chartOptions)}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
</script>
|
|
47
|
+
</div>
|
|
48
|
+
`.trim();
|
|
49
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const radarSchema = z.object({
|
|
3
|
+
labels: z.array(z.string()),
|
|
4
|
+
datasets: z.array(z.object({
|
|
5
|
+
label: z.string(),
|
|
6
|
+
data: z.array(z.number()),
|
|
7
|
+
backgroundColor: z.array(z.string()).optional(),
|
|
8
|
+
borderColor: z.array(z.string()).optional(),
|
|
9
|
+
})),
|
|
10
|
+
options: z.object({
|
|
11
|
+
title: z.string().optional(),
|
|
12
|
+
// Add more Chart.js options as needed
|
|
13
|
+
}).optional(),
|
|
14
|
+
});
|
|
15
|
+
export async function renderRadarChart(config) {
|
|
16
|
+
// Generate a unique ID for the chart container
|
|
17
|
+
const chartId = `chart_${Math.random().toString(36).substr(2, 9)}`;
|
|
18
|
+
// Prepare the chart data as JSON
|
|
19
|
+
const chartData = {
|
|
20
|
+
labels: config.labels,
|
|
21
|
+
datasets: config.datasets,
|
|
22
|
+
};
|
|
23
|
+
// Prepare the chart options
|
|
24
|
+
const chartOptions = {
|
|
25
|
+
...config.options,
|
|
26
|
+
plugins: {
|
|
27
|
+
title: config.options?.title ? {
|
|
28
|
+
display: true,
|
|
29
|
+
text: config.options.title
|
|
30
|
+
} : undefined
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
// Return interactive Chart.js HTML
|
|
34
|
+
return `
|
|
35
|
+
<div>
|
|
36
|
+
<canvas id="${chartId}" width="800" height="600"></canvas>
|
|
37
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
38
|
+
<script>
|
|
39
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
40
|
+
const ctx = document.getElementById('${chartId}').getContext('2d');
|
|
41
|
+
new Chart(ctx, {
|
|
42
|
+
type: 'radar',
|
|
43
|
+
data: ${JSON.stringify(chartData)},
|
|
44
|
+
options: ${JSON.stringify(chartOptions)}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
</script>
|
|
48
|
+
</div>
|
|
49
|
+
`.trim();
|
|
50
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -80,18 +80,24 @@ program
|
|
|
80
80
|
const result = await generateReport(documentContent, charts, path.resolve(reportsDir, opts.output));
|
|
81
81
|
// Generate proper file URL for CLI mode
|
|
82
82
|
const fileUrl = pathToFileURL(result.filePath).href;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
// Read the file content
|
|
84
|
+
const fileContent = await fs.readFile(result.filePath, 'utf8');
|
|
85
|
+
// Create a comprehensive result object
|
|
86
|
+
const comprehensiveResult = {
|
|
87
|
+
success: true,
|
|
88
|
+
message: "Report generated successfully",
|
|
89
|
+
filePath: result.filePath,
|
|
90
|
+
fileUrl: fileUrl,
|
|
91
|
+
fileContent: fileContent
|
|
86
92
|
};
|
|
87
|
-
console.log(JSON.stringify(
|
|
93
|
+
console.log(JSON.stringify(comprehensiveResult, null, 2));
|
|
88
94
|
});
|
|
89
95
|
// Handle stdio mode for Claude Desktop integration
|
|
90
96
|
if (process.argv.length === 2) {
|
|
91
97
|
// No command specified, run in stdio mode using MCP SDK
|
|
92
98
|
const mcpServer = new McpServer({
|
|
93
99
|
name: "report_gen_mcp",
|
|
94
|
-
version: "1.
|
|
100
|
+
version: "1.1.1",
|
|
95
101
|
}, {
|
|
96
102
|
// Disable health check to prevent automatic calls
|
|
97
103
|
capabilities: {
|
|
@@ -104,7 +110,7 @@ if (process.argv.length === 2) {
|
|
|
104
110
|
inputSchema: {
|
|
105
111
|
document: z.string().describe("Markdown document with chart placeholders [[chart:id]]"),
|
|
106
112
|
charts: z.record(z.object({
|
|
107
|
-
type: z.enum(["bar", "line", "pie"]),
|
|
113
|
+
type: z.enum(["bar", "line", "pie", "doughnut", "radar", "polarArea"]),
|
|
108
114
|
config: z.object({
|
|
109
115
|
labels: z.array(z.string()),
|
|
110
116
|
datasets: z.array(z.object({
|
|
@@ -163,16 +169,21 @@ if (process.argv.length === 2) {
|
|
|
163
169
|
const result = await generateReport(document, charts, outputPath);
|
|
164
170
|
// Generate proper file URL
|
|
165
171
|
const fileUrl = pathToFileURL(outputPath).href;
|
|
172
|
+
// Read the file content
|
|
173
|
+
const fileContent = await fs.readFile(outputPath, 'utf8');
|
|
174
|
+
// Create a comprehensive result object
|
|
175
|
+
const comprehensiveResult = {
|
|
176
|
+
success: true,
|
|
177
|
+
message: "Report generated successfully",
|
|
178
|
+
filePath: outputPath,
|
|
179
|
+
fileUrl: fileUrl,
|
|
180
|
+
fileContent: fileContent
|
|
181
|
+
};
|
|
166
182
|
return {
|
|
167
183
|
content: [
|
|
168
184
|
{
|
|
169
185
|
type: "text",
|
|
170
|
-
text: JSON.stringify(
|
|
171
|
-
...result,
|
|
172
|
-
message: "Report generated successfully",
|
|
173
|
-
filePath: outputPath,
|
|
174
|
-
fileUrl: fileUrl
|
|
175
|
-
})
|
|
186
|
+
text: JSON.stringify(comprehensiveResult, null, 2)
|
|
176
187
|
}
|
|
177
188
|
]
|
|
178
189
|
};
|
|
@@ -4,12 +4,16 @@ import { marked } from 'marked';
|
|
|
4
4
|
import { barSchema, renderBarChart } from '../charts/bar.js';
|
|
5
5
|
import { lineSchema, renderLineChart } from '../charts/line.js';
|
|
6
6
|
import { pieSchema, renderPieChart } from '../charts/pie.js';
|
|
7
|
-
|
|
7
|
+
import { doughnutSchema, renderDoughnutChart } from '../charts/doughnut.js';
|
|
8
|
+
import { radarSchema, renderRadarChart } from '../charts/radar.js';
|
|
9
|
+
import { polarAreaSchema, renderPolarAreaChart } from '../charts/polarArea.js';
|
|
8
10
|
const chartRenderers = {
|
|
9
11
|
bar: { schema: barSchema, renderer: renderBarChart },
|
|
10
12
|
line: { schema: lineSchema, renderer: renderLineChart },
|
|
11
13
|
pie: { schema: pieSchema, renderer: renderPieChart },
|
|
12
|
-
|
|
14
|
+
doughnut: { schema: doughnutSchema, renderer: renderDoughnutChart },
|
|
15
|
+
radar: { schema: radarSchema, renderer: renderRadarChart },
|
|
16
|
+
polarArea: { schema: polarAreaSchema, renderer: renderPolarAreaChart },
|
|
13
17
|
};
|
|
14
18
|
export async function generateReport(document, charts, outputFile) {
|
|
15
19
|
// Validate charts
|
|
@@ -19,27 +23,31 @@ export async function generateReport(document, charts, outputFile) {
|
|
|
19
23
|
throw new Error(`Unsupported chart type: ${type}`);
|
|
20
24
|
schema.parse(config);
|
|
21
25
|
}
|
|
22
|
-
// Render charts to HTML
|
|
26
|
+
// Render charts to interactive HTML
|
|
23
27
|
const renderedCharts = {};
|
|
24
28
|
for (const [id, { type, config }] of Object.entries(charts)) {
|
|
25
|
-
const
|
|
26
|
-
renderedCharts[id] =
|
|
29
|
+
const htmlChart = await chartRenderers[type].renderer(config);
|
|
30
|
+
renderedCharts[id] = htmlChart;
|
|
27
31
|
}
|
|
28
32
|
// Replace placeholders in Markdown, e.g., [[chart:id]]
|
|
29
33
|
let mdWithCharts = document;
|
|
30
|
-
for (const [id,
|
|
31
|
-
mdWithCharts = mdWithCharts.replace(new RegExp(`\\[\\[chart:${id}\\]\\]`, 'g'),
|
|
34
|
+
for (const [id, htmlChart] of Object.entries(renderedCharts)) {
|
|
35
|
+
mdWithCharts = mdWithCharts.replace(new RegExp(`\\[\\[chart:${id}\\]\\]`, 'g'), htmlChart);
|
|
32
36
|
}
|
|
33
37
|
// Convert Markdown to HTML
|
|
34
38
|
const htmlContent = await marked(mdWithCharts);
|
|
35
|
-
// Wrap in full HTML template
|
|
39
|
+
// Wrap in full HTML template with Chart.js library
|
|
36
40
|
const template = `
|
|
37
41
|
<!DOCTYPE html>
|
|
38
42
|
<html lang="en">
|
|
39
43
|
<head>
|
|
40
44
|
<meta charset="UTF-8">
|
|
41
45
|
<title>Report</title>
|
|
42
|
-
<style>
|
|
46
|
+
<style>
|
|
47
|
+
body { font-family: Arial, sans-serif; }
|
|
48
|
+
.chart-container { margin: 20px 0; }
|
|
49
|
+
</style>
|
|
50
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
43
51
|
</head>
|
|
44
52
|
<body>
|
|
45
53
|
<%- htmlContent %>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"dollar_rate_chart": {
|
|
3
|
+
"type": "line",
|
|
4
|
+
"config": {
|
|
5
|
+
"labels": [
|
|
6
|
+
"16.08.2025",
|
|
7
|
+
"19.08.2025",
|
|
8
|
+
"20.08.2025",
|
|
9
|
+
"21.08.2025",
|
|
10
|
+
"22.08.2025",
|
|
11
|
+
"23.08.2025"
|
|
12
|
+
],
|
|
13
|
+
"datasets": [
|
|
14
|
+
{
|
|
15
|
+
"label": "Курс USD/RUB",
|
|
16
|
+
"data": [
|
|
17
|
+
80.0224,
|
|
18
|
+
80.4256,
|
|
19
|
+
80.3466,
|
|
20
|
+
80.1045,
|
|
21
|
+
80.2548,
|
|
22
|
+
80.7498
|
|
23
|
+
],
|
|
24
|
+
"borderColor": [
|
|
25
|
+
"rgba(54, 162, 235, 1)"
|
|
26
|
+
],
|
|
27
|
+
"fill": false
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"options": {
|
|
31
|
+
"title": "Динамика курса доллара США к рублю"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"document": "# Отчет о курсе доллара за неделю (16.08.2025 - 23.08.2025)\n\nЭтот отчет показывает динамику курса доллара США к российскому рублю за последнюю неделю.\n\n[[chart:dollar_rate_chart]]\n\nДанные взяты с официального источника Банка России.",
|
|
3
|
+
"charts": {
|
|
4
|
+
"dollar_rate_chart": {
|
|
5
|
+
"type": "line",
|
|
6
|
+
"config": {
|
|
7
|
+
"labels": [
|
|
8
|
+
"16.08.2025",
|
|
9
|
+
"19.08.2025",
|
|
10
|
+
"20.08.2025",
|
|
11
|
+
"21.08.2025",
|
|
12
|
+
"22.08.2025",
|
|
13
|
+
"23.08.2025"
|
|
14
|
+
],
|
|
15
|
+
"datasets": [
|
|
16
|
+
{
|
|
17
|
+
"label": "Курс USD/RUB",
|
|
18
|
+
"data": [
|
|
19
|
+
80.0224,
|
|
20
|
+
80.4256,
|
|
21
|
+
80.3466,
|
|
22
|
+
80.1045,
|
|
23
|
+
80.2548,
|
|
24
|
+
80.7498
|
|
25
|
+
],
|
|
26
|
+
"borderColor": [
|
|
27
|
+
"rgba(54, 162, 235, 1)"
|
|
28
|
+
],
|
|
29
|
+
"fill": false
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"options": {
|
|
33
|
+
"title": "Динамика курса доллара США к рублю"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"outputFile": "dollar_rate_report.html"
|
|
39
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Set the reports directory
|
|
4
|
+
export REPORTS_DIR="./test_reports"
|
|
5
|
+
|
|
6
|
+
# Create the reports directory if it doesn't exist
|
|
7
|
+
mkdir -p "$REPORTS_DIR"
|
|
8
|
+
|
|
9
|
+
# Generate the report using the CLI
|
|
10
|
+
node dist/index.js generate \
|
|
11
|
+
--document dollar_rate_report.md \
|
|
12
|
+
--charts "$(cat dollar_rate_charts.json)" \
|
|
13
|
+
--output dollar_rate_report.html
|
|
14
|
+
|
|
15
|
+
echo "Report generated in $REPORTS_DIR/dollar_rate_report.html"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vint.tri/report_gen_mcp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "CLI tool for generating HTML reports with embedded charts",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,21 +13,20 @@
|
|
|
13
13
|
"test": "echo \"No tests yet\""
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@modelcontextprotocol/sdk": "^1.0.
|
|
17
|
-
"chart.js": "^
|
|
18
|
-
"chartjs-node-canvas": "^4.1.6",
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.0.3",
|
|
17
|
+
"chart.js": "^4.4.4",
|
|
19
18
|
"commander": "^12.1.0",
|
|
20
19
|
"ejs": "^3.1.10",
|
|
21
20
|
"express": "^4.19.2",
|
|
22
21
|
"fs-extra": "^11.2.0",
|
|
23
|
-
"marked": "^
|
|
22
|
+
"marked": "^14.1.2",
|
|
24
23
|
"zod": "^3.23.8"
|
|
25
24
|
},
|
|
26
25
|
"devDependencies": {
|
|
27
26
|
"@types/ejs": "^3.1.5",
|
|
28
27
|
"@types/express": "^4.17.21",
|
|
29
28
|
"@types/fs-extra": "^11.0.4",
|
|
30
|
-
"@types/node": "^22.
|
|
29
|
+
"@types/node": "^22.5.0",
|
|
31
30
|
"typescript": "^5.5.4"
|
|
32
31
|
},
|
|
33
32
|
"license": "MIT",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
|
|
4
|
+
// Read the fixed configuration
|
|
5
|
+
const config = JSON.parse(fs.readFileSync('./dollar_rate_report_fixed.json', 'utf8'));
|
|
6
|
+
|
|
7
|
+
// Set the reports directory
|
|
8
|
+
process.env.REPORTS_DIR = './test_reports';
|
|
9
|
+
|
|
10
|
+
// Spawn the report generator in stdio mode
|
|
11
|
+
const child = spawn('node', ['dist/index.js']);
|
|
12
|
+
|
|
13
|
+
// Listen for data from the child process
|
|
14
|
+
child.stdout.on('data', (data) => {
|
|
15
|
+
console.log(`stdout: ${data}`);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
child.stderr.on('data', (data) => {
|
|
19
|
+
console.error(`stderr: ${data}`);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
child.on('close', (code) => {
|
|
23
|
+
console.log(`child process exited with code ${code}`);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Send the MCP initialize message
|
|
27
|
+
const initializeMessage = {
|
|
28
|
+
jsonrpc: "2.0",
|
|
29
|
+
id: 1,
|
|
30
|
+
method: "initialize",
|
|
31
|
+
params: {
|
|
32
|
+
protocolVersion: "2024-08-07",
|
|
33
|
+
capabilities: {},
|
|
34
|
+
clientInfo: {
|
|
35
|
+
name: "test-client",
|
|
36
|
+
version: "1.0.0"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
child.stdin.write(JSON.stringify(initializeMessage) + '\n');
|
|
42
|
+
|
|
43
|
+
// Send the tool call message
|
|
44
|
+
const toolCallMessage = {
|
|
45
|
+
jsonrpc: "2.0",
|
|
46
|
+
id: 2,
|
|
47
|
+
method: "tools/call",
|
|
48
|
+
params: {
|
|
49
|
+
name: "generate-report",
|
|
50
|
+
arguments: config
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
child.stdin.write(JSON.stringify(toolCallMessage) + '\n');
|
|
56
|
+
}, 1000);
|