@vint.tri/report_gen_mcp 1.5.24 → 1.5.26
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/dist/index.js +4 -11
- package/dist/tools/createReportFromText.d.ts.map +1 -1
- package/dist/tools/createReportFromText.js +1 -2
- package/dist/tools/simpleReport.d.ts.map +1 -1
- package/dist/tools/simpleReport.js +1 -37
- package/dist/tools/testReport.d.ts.map +1 -1
- package/dist/tools/testReport.js +6 -39
- package/dist/utils/reportGenerator.d.ts +1 -15
- package/dist/utils/reportGenerator.d.ts.map +1 -1
- package/dist/utils/reportGenerator.js +50 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -40,12 +40,10 @@ app.use(express.json());
|
|
|
40
40
|
app.post('/generate-report', async (req, res) => {
|
|
41
41
|
// For HTTP API mode, use the REPORTS_DIR environment variable
|
|
42
42
|
// This endpoint only runs in non-stdio mode where reportsDir is guaranteed to be defined
|
|
43
|
-
const { document,
|
|
44
|
-
// Поддержка обратной совместимости: если переданы charts, используем их как elements
|
|
45
|
-
const reportElements = elements || charts || {};
|
|
43
|
+
const { document, outputFile = 'report.html' } = req.body;
|
|
46
44
|
const outputPath = path.resolve(reportsDir, outputFile);
|
|
47
45
|
try {
|
|
48
|
-
const result = await generateReport(document,
|
|
46
|
+
const result = await generateReport(document, outputPath);
|
|
49
47
|
// Send the file content back to the client
|
|
50
48
|
const fileContent = await fs.readFile(outputPath, 'utf8');
|
|
51
49
|
res.json({
|
|
@@ -69,21 +67,16 @@ program
|
|
|
69
67
|
program
|
|
70
68
|
.command('generate')
|
|
71
69
|
.option('--document <md>', 'Markdown document with placeholders [[chart:id]] or [[image:id]]')
|
|
72
|
-
.option('--elements <json>', 'JSON string of elements (charts and images) {id: {type: "bar", config: {...}}}')
|
|
73
|
-
.option('--charts <json>', 'JSON string of charts (deprecated, use --elements instead)')
|
|
74
70
|
.option('--output <file>', 'Output HTML file', 'report.html')
|
|
75
71
|
.description('Generate a report in the directory specified by REPORTS_DIR environment variable')
|
|
76
72
|
.action(async (opts) => {
|
|
77
|
-
// Поддержка обратной совместимости: если переданы charts, используем их как elements
|
|
78
|
-
const elements = opts.elements ? JSON.parse(opts.elements) :
|
|
79
|
-
opts.charts ? JSON.parse(opts.charts) : {};
|
|
80
73
|
// Read the document file content if a file path is provided
|
|
81
74
|
let documentContent = opts.document;
|
|
82
75
|
if (opts.document && fs.existsSync(opts.document)) {
|
|
83
76
|
documentContent = await fs.readFile(opts.document, 'utf8');
|
|
84
77
|
}
|
|
85
78
|
// This command only runs in non-stdio mode where reportsDir is guaranteed to be defined
|
|
86
|
-
const result = await generateReport(documentContent,
|
|
79
|
+
const result = await generateReport(documentContent, path.resolve(reportsDir, opts.output));
|
|
87
80
|
// Generate proper file URL for CLI mode
|
|
88
81
|
const fileUrl = pathToFileURL(result.filePath).href;
|
|
89
82
|
// Read the file content
|
|
@@ -116,7 +109,7 @@ if (process.argv.length === 2) {
|
|
|
116
109
|
// No command specified, run in stdio mode using MCP SDK
|
|
117
110
|
const mcpServer = new McpServer({
|
|
118
111
|
name: "report_gen_mcp",
|
|
119
|
-
version: "1.5.
|
|
112
|
+
version: "1.5.26"
|
|
120
113
|
}, {
|
|
121
114
|
// Disable health check to prevent automatic calls
|
|
122
115
|
capabilities: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createReportFromText.d.ts","sourceRoot":"","sources":["../../src/tools/createReportFromText.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,wBAAwB;;;;;;;qBAOZ,GAAG;;;;
|
|
1
|
+
{"version":3,"file":"createReportFromText.d.ts","sourceRoot":"","sources":["../../src/tools/createReportFromText.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,wBAAwB;;;;;;;qBAOZ,GAAG;;;;CAM3B,CAAC"}
|
|
@@ -9,9 +9,8 @@ export const createReportFromTextTool = {
|
|
|
9
9
|
},
|
|
10
10
|
action: async (params) => {
|
|
11
11
|
const { reportContent, outputFile } = params;
|
|
12
|
-
const elements = {}; // No elements to parse from here
|
|
13
12
|
// The reportContent is already in the final format, so we just need to pass it to the report generator.
|
|
14
|
-
const result = await generateReport(reportContent,
|
|
13
|
+
const result = await generateReport(reportContent, outputFile);
|
|
15
14
|
return result;
|
|
16
15
|
},
|
|
17
16
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simpleReport.d.ts","sourceRoot":"","sources":["../../src/tools/simpleReport.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,gBAAgB;;;;;;;mBAON;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;
|
|
1
|
+
{"version":3,"file":"simpleReport.d.ts","sourceRoot":"","sources":["../../src/tools/simpleReport.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,gBAAgB;;;;;;;mBAON;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;iBAajC;YAAE,IAAI,EAAE,UAAU,CAAC;YAAC,QAAQ,EAAE;gBAAE,GAAG,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,EAAE;;CAa7F,CAAC"}
|
|
@@ -13,48 +13,12 @@ export const simpleReportTool = {
|
|
|
13
13
|
},
|
|
14
14
|
action: async (args) => {
|
|
15
15
|
let { document, outputFile } = args;
|
|
16
|
-
const elements = {};
|
|
17
|
-
let chartCount = 0;
|
|
18
|
-
// Regex to find canvas tags and extract their content
|
|
19
|
-
const canvasRegex = /<canvas[^>]*>([\s\S]*?)<\/canvas>/g;
|
|
20
|
-
document = document.replace(canvasRegex, (match, chartConfigString) => {
|
|
21
|
-
const id = `chart${++chartCount}`;
|
|
22
|
-
try {
|
|
23
|
-
// Using Function constructor as a safer alternative to eval
|
|
24
|
-
const chartConfig = new Function(`return ${chartConfigString.trim()}`)();
|
|
25
|
-
elements[id] = chartConfig;
|
|
26
|
-
return `[[chart:${id}]]`;
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
console.error(`Error parsing chart config for id ${id}:`, error);
|
|
30
|
-
// Keep the original tag if parsing fails to avoid losing content
|
|
31
|
-
return match;
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
let imageCount = 0;
|
|
35
|
-
// Regex to find img tags and extract their content
|
|
36
|
-
const imgRegex = /<img[^>]*data-image='([^']*)'[^>]*>/g;
|
|
37
|
-
document = document.replace(imgRegex, (match, imageConfigString) => {
|
|
38
|
-
const idMatch = match.match(/id="([^"]+)"/);
|
|
39
|
-
const id = idMatch ? idMatch[1] : `image${++imageCount}`;
|
|
40
|
-
try {
|
|
41
|
-
// Using Function constructor as a safer alternative to eval
|
|
42
|
-
const imageConfig = new Function(`return ${imageConfigString.trim()}`)();
|
|
43
|
-
elements[id] = imageConfig;
|
|
44
|
-
return `[[image:${id}]]`;
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
console.error(`Error parsing image config for id ${id}:`, error);
|
|
48
|
-
// Keep the original tag if parsing fails
|
|
49
|
-
return match;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
16
|
const reportsDir = process.env.REPORTS_DIR || os.tmpdir();
|
|
53
17
|
if (!outputFile) {
|
|
54
18
|
outputFile = `report-${Date.now()}.html`;
|
|
55
19
|
}
|
|
56
20
|
const outputPath = path.resolve(reportsDir, outputFile);
|
|
57
|
-
const result = await generateReport(document,
|
|
21
|
+
const result = await generateReport(document, outputPath);
|
|
58
22
|
const fileUrl = pathToFileURL(result.filePath).href;
|
|
59
23
|
const fileContent = await fs.readFile(result.filePath, 'utf8');
|
|
60
24
|
const response = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testReport.d.ts","sourceRoot":"","sources":["../../src/tools/testReport.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"testReport.d.ts","sourceRoot":"","sources":["../../src/tools/testReport.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc;;;;;;;;;;CAsC1B,CAAC"}
|
package/dist/tools/testReport.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { generateReport } from '../utils/reportGenerator.js';
|
|
2
2
|
import { pathToFileURL } from 'url';
|
|
3
3
|
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import os from 'os';
|
|
4
6
|
export const testReportTool = {
|
|
5
7
|
name: "test-report",
|
|
6
8
|
description: "Generate a hardcoded test report to verify that the MCP server is working correctly.",
|
|
@@ -16,46 +18,11 @@ This is a test report.
|
|
|
16
18
|
|
|
17
19
|
## Chart
|
|
18
20
|
|
|
19
|
-
<canvas
|
|
20
|
-
<script>
|
|
21
|
-
const ctx = document.getElementById('myChart').getContext('2d');
|
|
22
|
-
new Chart(ctx, {
|
|
23
|
-
type: 'bar',
|
|
24
|
-
data: {
|
|
25
|
-
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
|
|
26
|
-
datasets: [{
|
|
27
|
-
label: '# of Votes',
|
|
28
|
-
data: [12, 19, 3, 5, 2, 3],
|
|
29
|
-
backgroundColor: [
|
|
30
|
-
'rgba(255, 99, 132, 0.2)',
|
|
31
|
-
'rgba(54, 162, 235, 0.2)',
|
|
32
|
-
'rgba(255, 206, 86, 0.2)',
|
|
33
|
-
'rgba(75, 192, 192, 0.2)',
|
|
34
|
-
'rgba(153, 102, 255, 0.2)',
|
|
35
|
-
'rgba(255, 159, 64, 0.2)'
|
|
36
|
-
],
|
|
37
|
-
borderColor: [
|
|
38
|
-
'rgba(255, 99, 132, 1)',
|
|
39
|
-
'rgba(54, 162, 235, 1)',
|
|
40
|
-
'rgba(255, 206, 86, 1)',
|
|
41
|
-
'rgba(75, 192, 192, 1)',
|
|
42
|
-
'rgba(153, 102, 255, 1)',
|
|
43
|
-
'rgba(255, 159, 64, 1)'
|
|
44
|
-
],
|
|
45
|
-
borderWidth: 1
|
|
46
|
-
}]
|
|
47
|
-
},
|
|
48
|
-
options: {
|
|
49
|
-
scales: {
|
|
50
|
-
y: {
|
|
51
|
-
beginAtZero: true
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
</script>
|
|
21
|
+
<canvas data-chart='{"type":"bar","data":{"labels":["Red","Blue","Yellow","Green","Purple","Orange"],"datasets":[{"label":"# of Votes","data":[12,19,3,5,2,3],"backgroundColor":["rgba(255, 99, 132, 0.2)","rgba(54, 162, 235, 0.2)","rgba(255, 206, 86, 0.2)","rgba(75, 192, 192, 0.2)","rgba(153, 102, 255, 0.2)","rgba(255, 159, 64, 0.2)"],"borderColor":["rgba(255, 99, 132, 1)","rgba(54, 162, 235, 1)","rgba(255, 206, 86, 1)","rgba(75, 192, 192, 1)","rgba(153, 102, 255, 1)","rgba(255, 159, 64, 1)"],"borderWidth":1}]},"options":{"scales":{"y":{"beginAtZero":true}}}}'></canvas>
|
|
57
22
|
`;
|
|
58
|
-
const
|
|
23
|
+
const tempDir = path.join(os.tmpdir(), 'report_gen_mcp_temp');
|
|
24
|
+
await fs.ensureDir(tempDir);
|
|
25
|
+
const result = await generateReport(reportContent, path.join(tempDir, "test-report.html"));
|
|
59
26
|
const fileUrl = pathToFileURL(result.filePath).href;
|
|
60
27
|
const fileContent = await fs.readFile(result.filePath, 'utf8');
|
|
61
28
|
return {
|
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import { urlImageSchema } from '../images/urlImages.js';
|
|
3
|
-
type ChartType = 'bar' | 'line' | 'pie' | 'doughnut' | 'radar' | 'polarArea';
|
|
4
|
-
interface ChartElement {
|
|
5
|
-
type: ChartType;
|
|
6
|
-
config: any;
|
|
7
|
-
}
|
|
8
|
-
interface UrlImageElement {
|
|
9
|
-
type: 'url' | 'image';
|
|
10
|
-
config: z.infer<typeof urlImageSchema>;
|
|
11
|
-
}
|
|
12
|
-
type ImageElement = UrlImageElement;
|
|
13
|
-
type ReportElement = ChartElement | ImageElement;
|
|
14
|
-
export declare function generateReport(document: string, elements: Record<string, ReportElement>, outputFile: string): Promise<{
|
|
1
|
+
export declare function generateReport(document: string, outputFile: string): Promise<{
|
|
15
2
|
success: boolean;
|
|
16
3
|
filePath: string;
|
|
17
4
|
}>;
|
|
18
|
-
export {};
|
|
19
5
|
//# sourceMappingURL=reportGenerator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reportGenerator.d.ts","sourceRoot":"","sources":["../../src/utils/reportGenerator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reportGenerator.d.ts","sourceRoot":"","sources":["../../src/utils/reportGenerator.ts"],"names":[],"mappings":"AAuHA,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM;;;GA4InB"}
|
|
@@ -9,6 +9,45 @@ import { doughnutSchema, renderDoughnutChart } from '../charts/doughnut.js';
|
|
|
9
9
|
import { radarSchema, renderRadarChart } from '../charts/radar.js';
|
|
10
10
|
import { polarAreaSchema, renderPolarAreaChart } from '../charts/polarArea.js';
|
|
11
11
|
import { urlImageSchema, renderUrlImage } from '../images/urlImages.js';
|
|
12
|
+
async function extractElementsFromMarkdown(markdown) {
|
|
13
|
+
console.log("Starting extractElementsFromMarkdown...");
|
|
14
|
+
const elements = {};
|
|
15
|
+
let elementCount = 0;
|
|
16
|
+
// Regex to find <canvas data-chart="...">
|
|
17
|
+
const chartRegex = /<canvas\s+data-chart="([^"]*)"[^>]*>/g;
|
|
18
|
+
let chartMatch;
|
|
19
|
+
while ((chartMatch = chartRegex.exec(markdown)) !== null) {
|
|
20
|
+
const chartConfigJson = chartMatch[1];
|
|
21
|
+
try {
|
|
22
|
+
const config = JSON.parse(chartConfigJson);
|
|
23
|
+
if (config && typeof config === 'object' && config.type) {
|
|
24
|
+
const id = `chart-${elementCount++}`;
|
|
25
|
+
elements[id] = { type: config.type, config: config };
|
|
26
|
+
console.log(`Extracted chart: ${id}, type: ${config.type}`);
|
|
27
|
+
// Replace the original canvas tag with a placeholder
|
|
28
|
+
markdown = markdown.replace(chartMatch[0], `[[chart:${id}]]`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
console.error(`Error parsing chart config: ${chartConfigJson}`, e);
|
|
33
|
+
// Continue without adding this chart if parsing fails
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Regex to find <img src="..." alt="...">
|
|
37
|
+
const imageRegex = /<img\s+src="([^"]*)"(?:\s+alt="([^"]*)")?[^>]*>/g;
|
|
38
|
+
let imageMatch;
|
|
39
|
+
while ((imageMatch = imageRegex.exec(markdown)) !== null) {
|
|
40
|
+
const src = imageMatch[1];
|
|
41
|
+
const alt = imageMatch[2] || '';
|
|
42
|
+
const id = `image-${elementCount++}`;
|
|
43
|
+
elements[id] = { type: 'url', config: { url: src, alt: alt } };
|
|
44
|
+
console.log(`Extracted image: ${id}, src: ${src}`);
|
|
45
|
+
// Replace the original img tag with a placeholder
|
|
46
|
+
markdown = markdown.replace(imageMatch[0], `[[image:${id}]]`);
|
|
47
|
+
}
|
|
48
|
+
console.log("Finished extractElementsFromMarkdown. Extracted elements:", Object.keys(elements));
|
|
49
|
+
return elements;
|
|
50
|
+
}
|
|
12
51
|
const chartRenderers = {
|
|
13
52
|
bar: { schema: barSchema, renderer: renderBarChart },
|
|
14
53
|
line: { schema: lineSchema, renderer: renderLineChart },
|
|
@@ -47,9 +86,14 @@ function normalizeChartConfig(config) {
|
|
|
47
86
|
}
|
|
48
87
|
return normalizedConfig;
|
|
49
88
|
}
|
|
50
|
-
export async function generateReport(document,
|
|
89
|
+
export async function generateReport(document, outputFile) {
|
|
90
|
+
console.log("Starting generateReport...");
|
|
91
|
+
const extractedElements = await extractElementsFromMarkdown(document);
|
|
92
|
+
const elements = extractedElements; // Use the extracted elements
|
|
93
|
+
console.log("Elements after extraction:", Object.keys(elements));
|
|
51
94
|
// Validate elements
|
|
52
95
|
for (const [id, element] of Object.entries(elements)) {
|
|
96
|
+
console.log(`Validating element: ${id}, type: ${element.type}`);
|
|
53
97
|
if (element.type in chartRenderers) {
|
|
54
98
|
// Это диаграмма
|
|
55
99
|
const chartElement = element;
|
|
@@ -61,6 +105,7 @@ export async function generateReport(document, elements, outputFile) {
|
|
|
61
105
|
schema.parse(normalizedConfig);
|
|
62
106
|
// Update the element with normalized config for rendering
|
|
63
107
|
element.config = normalizedConfig;
|
|
108
|
+
console.log(`Validated chart: ${id}`);
|
|
64
109
|
}
|
|
65
110
|
else if (element.type in imageRenderers) {
|
|
66
111
|
// Это изображение
|
|
@@ -69,6 +114,7 @@ export async function generateReport(document, elements, outputFile) {
|
|
|
69
114
|
if (!schema)
|
|
70
115
|
throw new Error(`Unsupported image type: ${imageElement.type}`);
|
|
71
116
|
schema.parse(imageElement.config);
|
|
117
|
+
console.log(`Validated image: ${id}`);
|
|
72
118
|
}
|
|
73
119
|
else {
|
|
74
120
|
throw new Error(`Unsupported element type: ${element.type}`);
|
|
@@ -77,17 +123,20 @@ export async function generateReport(document, elements, outputFile) {
|
|
|
77
123
|
// Render elements to interactive HTML
|
|
78
124
|
const renderedElements = {};
|
|
79
125
|
for (const [id, element] of Object.entries(elements)) {
|
|
126
|
+
console.log(`Rendering element: ${id}, type: ${element.type}`);
|
|
80
127
|
if (element.type in chartRenderers) {
|
|
81
128
|
// Это диаграмма
|
|
82
129
|
const chartElement = element;
|
|
83
130
|
const htmlChart = await chartRenderers[chartElement.type].renderer(chartElement.config);
|
|
84
131
|
renderedElements[id] = htmlChart;
|
|
132
|
+
console.log(`Rendered chart: ${id}`);
|
|
85
133
|
}
|
|
86
134
|
else if (element.type in imageRenderers) {
|
|
87
135
|
// Это изображение
|
|
88
136
|
const imageElement = element;
|
|
89
137
|
const htmlImage = await imageRenderers[imageElement.type].renderer(imageElement.config);
|
|
90
138
|
renderedElements[id] = htmlImage;
|
|
139
|
+
console.log(`Rendered image: ${id}`);
|
|
91
140
|
}
|
|
92
141
|
}
|
|
93
142
|
// Replace placeholders in Markdown, e.g., [[chart:id]] or [[image:id]]
|