Skip to main content
Glama

render_mermaid_chart

Convert Mermaid diagram code into high-quality PNG, SVG, or PDF files with customizable dimensions, themes, and background colors. Ideal for creating and saving visual diagrams locally or via HTTP URLs.

Instructions

将 Mermaid 代码渲染为高质量图片文件

Input Schema

NameRequiredDescriptionDefault
backgroundColorNo背景颜色,默认为 whitewhite
formatNo输出格式(png, svg, pdf),默认为 pngpng
heightNo图片高度(像素),默认为 800
mermaidCodeYesMermaid 图表代码
outputPathYes输出文件路径(包含文件名和扩展名)
themeNoMermaid 主题,默认为 defaultdefault
widthNo图片宽度(像素),默认为 1200

Input Schema (JSON Schema)

{ "properties": { "backgroundColor": { "default": "white", "description": "背景颜色,默认为 white", "type": "string" }, "format": { "default": "png", "description": "输出格式(png, svg, pdf),默认为 png", "enum": [ "png", "svg", "pdf" ], "type": "string" }, "height": { "default": 800, "description": "图片高度(像素),默认为 800", "type": "number" }, "mermaidCode": { "description": "Mermaid 图表代码", "type": "string" }, "outputPath": { "description": "输出文件路径(包含文件名和扩展名)", "type": "string" }, "theme": { "default": "default", "description": "Mermaid 主题,默认为 default", "enum": [ "default", "dark", "forest", "neutral" ], "type": "string" }, "width": { "default": 1200, "description": "图片宽度(像素),默认为 1200", "type": "number" } }, "required": [ "mermaidCode", "outputPath" ], "type": "object" }

Implementation Reference

  • Core handler function that executes the render_mermaid_chart tool logic: launches headless browser, renders Mermaid diagram via HTML+JS, exports to PNG/SVG/PDF using Sharp/Puppeteer, optionally uploads to MinIO.
    async renderChart(options: RenderOptions): Promise<RenderResult> { const { mermaidCode, outputPath, format = 'png', width = 1200, height = 800, backgroundColor = 'white', theme = 'default', uploadToMinio = false, minioExpiryDays = 7, } = options; try { // 确保输出目录存在 const outputDir = dirname(outputPath); await fs.ensureDir(outputDir); // 初始化浏览器 const browser = await this.initBrowser(); const page = await browser.newPage(); // 设置高分辨率视口和设备像素比 await page.setViewport({ width, height, deviceScaleFactor: 2, // 设置设备像素比为2,提高PNG渲染质量 }); // 增加页面错误监听 page.on('console', msg => { if (msg.type() === 'error') { console.error('页面控制台错误:', msg.text()); } }); page.on('pageerror', error => { console.error('页面运行时错误:', error); }); // 创建 HTML 内容 const htmlContent = this.createHtmlContent(mermaidCode, theme, backgroundColor); // 加载 HTML 内容 console.log('加载HTML内容...'); await page.setContent(htmlContent, { waitUntil: 'domcontentloaded', timeout: 30000 }); // 等待 Mermaid 渲染完成,增加更长的超时时间 console.log('等待Mermaid渲染...'); try { await page.waitForSelector('#mermaid-diagram', { timeout: 30000 }); // 检查是否渲染成功 const isRendered = await page.waitForFunction( () => { const element = document.querySelector('#mermaid-diagram'); const hasRendered = element && element.getAttribute('data-rendered') === 'true'; const hasSvg = element && element.querySelector('svg'); console.log('渲染状态检查:', { hasRendered, hasSvg }); return hasRendered || hasSvg; // 任一条件满足即可 }, { timeout: 60000, polling: 1000 } ); console.log('Mermaid渲染完成'); } catch (timeoutError) { // 如果超时,尝试获取页面状态信息 const pageContent = await page.content(); console.error('渲染超时,页面内容:', pageContent.substring(0, 500)); const diagramElement = await page.$('#mermaid-diagram'); if (diagramElement) { const innerHTML = await page.evaluate(el => el.innerHTML, diagramElement); console.error('图表元素内容:', innerHTML); } throw new Error(`Mermaid渲染超时: ${timeoutError instanceof Error ? timeoutError.message : String(timeoutError)}`); } // 根据格式渲染 if (format === 'svg') { await this.renderSVG(page, outputPath); } else if (format === 'pdf') { await this.renderPDF(page, outputPath, width, height); } else { await this.renderPNG(page, outputPath, width, height); } await page.close(); // 基础渲染结果 const result: RenderResult = { outputPath, format, width, height, }; // 如果需要上传到MinIO if (uploadToMinio) { try { console.log('🔄 开始MinIO上传...'); const minioConfig = createDefaultMinIOConfig(); const minioUploader = new MinIOUploader(minioConfig); await minioUploader.initialize(); const uploadResult = await minioUploader.uploadFile(outputPath, { expiryDays: minioExpiryDays }); result.uploadResult = uploadResult; if (uploadResult.success && uploadResult.url) { result.minioUrl = uploadResult.url; console.log('✅ MinIO上传成功'); } else { console.error('❌ MinIO上传失败:', uploadResult.error); } } catch (minioError) { console.error('❌ MinIO上传过程中发生错误:', minioError); result.uploadResult = { success: false, error: `MinIO上传失败: ${minioError instanceof Error ? minioError.message : String(minioError)}` }; } } return result; } catch (error) { throw new Error(`渲染失败: ${error instanceof Error ? error.message : String(error)}`); } }
  • MCP tool input schema definition including all parameters like mermaidCode, outputPath, format, dimensions, theme, MinIO upload options.
    inputSchema: { type: 'object', properties: { mermaidCode: { type: 'string', description: 'Mermaid 图表代码', }, outputPath: { type: 'string', description: '输出文件路径(包含文件名和扩展名)', }, format: { type: 'string', enum: ['png', 'svg', 'pdf'], description: '输出格式(png, svg, pdf),默认为 png', default: 'png', }, width: { type: 'number', description: '图片宽度(像素),默认为 1200', default: 1200, }, height: { type: 'number', description: '图片高度(像素),默认为 800', default: 800, }, backgroundColor: { type: 'string', description: '背景颜色,默认为 white', default: 'white', }, theme: { type: 'string', enum: ['default', 'dark', 'forest', 'neutral'], description: 'Mermaid 主题,默认为 default', default: 'default', }, uploadToMinio: { type: 'boolean', description: '是否上传到MinIO存储并返回在线链接,默认为 false', default: false, }, minioExpiryDays: { type: 'number', description: 'MinIO文件有效期(天数),默认7天,最大30天,超过30天按30天计算', default: 7, minimum: 1, maximum: 30, }, }, required: ['mermaidCode', 'outputPath'], },
  • src/index.ts:37-99 (registration)
    Registration of the render_mermaid_chart tool in the MCP ListToolsRequestSchema handler.
    this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'render_mermaid_chart', description: '将 Mermaid 代码渲染为高质量图片文件', inputSchema: { type: 'object', properties: { mermaidCode: { type: 'string', description: 'Mermaid 图表代码', }, outputPath: { type: 'string', description: '输出文件路径(包含文件名和扩展名)', }, format: { type: 'string', enum: ['png', 'svg', 'pdf'], description: '输出格式(png, svg, pdf),默认为 png', default: 'png', }, width: { type: 'number', description: '图片宽度(像素),默认为 1200', default: 1200, }, height: { type: 'number', description: '图片高度(像素),默认为 800', default: 800, }, backgroundColor: { type: 'string', description: '背景颜色,默认为 white', default: 'white', }, theme: { type: 'string', enum: ['default', 'dark', 'forest', 'neutral'], description: 'Mermaid 主题,默认为 default', default: 'default', }, uploadToMinio: { type: 'boolean', description: '是否上传到MinIO存储并返回在线链接,默认为 false', default: false, }, minioExpiryDays: { type: 'number', description: 'MinIO文件有效期(天数),默认7天,最大30天,超过30天按30天计算', default: 7, minimum: 1, maximum: 30, }, }, required: ['mermaidCode', 'outputPath'], }, }, ], }; });
  • MCP CallToolRequestSchema dispatch handler that validates params and delegates to MermaidRenderer.renderChart.
    if (name === 'render_mermaid_chart') { try { const { mermaidCode, outputPath, format = 'png', width = 1200, height = 800, backgroundColor = 'white', theme = 'default', uploadToMinio = false, minioExpiryDays = 7, } = args as { mermaidCode: string; outputPath: string; format?: 'png' | 'svg' | 'pdf'; width?: number; height?: number; backgroundColor?: string; theme?: 'default' | 'dark' | 'forest' | 'neutral'; uploadToMinio?: boolean; minioExpiryDays?: number; }; // 验证必需参数 if (!mermaidCode || !outputPath) { throw new Error('mermaidCode 和 outputPath 是必需参数'); } // 渲染图表 const result = await this.renderer.renderChart({ mermaidCode, outputPath, format, width, height, backgroundColor, theme, uploadToMinio, minioExpiryDays, }); return { content: [ { type: 'text', text: this.formatRenderResult(result), }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : '未知错误'; return { content: [ { type: 'text', text: `渲染失败: ${errorMessage}`, }, ], isError: true, }; } }
  • MermaidRenderer class containing all rendering helpers: browser management, HTML generation, format-specific exporters (PNG with Sharp optimization, SVG, PDF).
    export class MermaidRenderer { private browser: Browser | null = null; constructor() {} /** * 初始化浏览器实例 */ private async initBrowser(): Promise<Browser> { if (!this.browser) { this.browser = await puppeteer.launch({ headless: 'new', args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-gpu', '--force-device-scale-factor=2', // 高DPI渲染 '--high-dpi-support', ], }); } return this.browser; } /** * 渲染 Mermaid 图表 */ async renderChart(options: RenderOptions): Promise<RenderResult> { const { mermaidCode, outputPath, format = 'png', width = 1200, height = 800, backgroundColor = 'white', theme = 'default', uploadToMinio = false, minioExpiryDays = 7, } = options; try { // 确保输出目录存在 const outputDir = dirname(outputPath); await fs.ensureDir(outputDir); // 初始化浏览器 const browser = await this.initBrowser(); const page = await browser.newPage(); // 设置高分辨率视口和设备像素比 await page.setViewport({ width, height, deviceScaleFactor: 2, // 设置设备像素比为2,提高PNG渲染质量 }); // 增加页面错误监听 page.on('console', msg => { if (msg.type() === 'error') { console.error('页面控制台错误:', msg.text()); } }); page.on('pageerror', error => { console.error('页面运行时错误:', error); }); // 创建 HTML 内容 const htmlContent = this.createHtmlContent(mermaidCode, theme, backgroundColor); // 加载 HTML 内容 console.log('加载HTML内容...'); await page.setContent(htmlContent, { waitUntil: 'domcontentloaded', timeout: 30000 }); // 等待 Mermaid 渲染完成,增加更长的超时时间 console.log('等待Mermaid渲染...'); try { await page.waitForSelector('#mermaid-diagram', { timeout: 30000 }); // 检查是否渲染成功 const isRendered = await page.waitForFunction( () => { const element = document.querySelector('#mermaid-diagram'); const hasRendered = element && element.getAttribute('data-rendered') === 'true'; const hasSvg = element && element.querySelector('svg'); console.log('渲染状态检查:', { hasRendered, hasSvg }); return hasRendered || hasSvg; // 任一条件满足即可 }, { timeout: 60000, polling: 1000 } ); console.log('Mermaid渲染完成'); } catch (timeoutError) { // 如果超时,尝试获取页面状态信息 const pageContent = await page.content(); console.error('渲染超时,页面内容:', pageContent.substring(0, 500)); const diagramElement = await page.$('#mermaid-diagram'); if (diagramElement) { const innerHTML = await page.evaluate(el => el.innerHTML, diagramElement); console.error('图表元素内容:', innerHTML); } throw new Error(`Mermaid渲染超时: ${timeoutError instanceof Error ? timeoutError.message : String(timeoutError)}`); } // 根据格式渲染 if (format === 'svg') { await this.renderSVG(page, outputPath); } else if (format === 'pdf') { await this.renderPDF(page, outputPath, width, height); } else { await this.renderPNG(page, outputPath, width, height); } await page.close(); // 基础渲染结果 const result: RenderResult = { outputPath, format, width, height, }; // 如果需要上传到MinIO if (uploadToMinio) { try { console.log('🔄 开始MinIO上传...'); const minioConfig = createDefaultMinIOConfig(); const minioUploader = new MinIOUploader(minioConfig); await minioUploader.initialize(); const uploadResult = await minioUploader.uploadFile(outputPath, { expiryDays: minioExpiryDays }); result.uploadResult = uploadResult; if (uploadResult.success && uploadResult.url) { result.minioUrl = uploadResult.url; console.log('✅ MinIO上传成功'); } else { console.error('❌ MinIO上传失败:', uploadResult.error); } } catch (minioError) { console.error('❌ MinIO上传过程中发生错误:', minioError); result.uploadResult = { success: false, error: `MinIO上传失败: ${minioError instanceof Error ? minioError.message : String(minioError)}` }; } } return result; } catch (error) { throw new Error(`渲染失败: ${error instanceof Error ? error.message : String(error)}`); } } /** * 创建包含 Mermaid 图表的 HTML 内容 */ private createHtmlContent(mermaidCode: string, theme: string, backgroundColor: string): string { return `<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Mermaid Chart</title> <script src="https://unpkg.com/mermaid@10.6.1/dist/mermaid.min.js"></script> <style> body { margin: 0; padding: 20px; background-color: ${backgroundColor}; display: flex; justify-content: center; align-items: center; min-height: 100vh; font-family: 'Microsoft YaHei', 'SimHei', 'Arial', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } #mermaid-diagram { max-width: 100%; max-height: 100%; } .mermaid { background-color: ${backgroundColor}; } svg text { font-family: 'Microsoft YaHei', 'SimHei', 'Arial', sans-serif !important; font-size: 14px !important; font-weight: 500 !important; } svg .node rect, svg .node circle, svg .node ellipse, svg .node polygon { stroke-width: 2px !important; } #loading { display: none; } </style> </head> <body> <div id="loading">Loading Mermaid...</div> <div id="mermaid-diagram" class="mermaid"> ${mermaidCode} </div> <script> console.log('Mermaid script loaded'); mermaid.initialize({ startOnLoad: false, theme: '${theme}', securityLevel: 'loose', flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'basis', padding: 15 }, sequence: { useMaxWidth: true, diagramMarginX: 50, diagramMarginY: 10, actorMargin: 50, width: 150, height: 65, boxMargin: 10, boxTextMargin: 5, noteMargin: 10, messageMargin: 35 }, gantt: { useMaxWidth: true, fontSize: 11, fontFamily: 'Microsoft YaHei' }, fontFamily: 'Microsoft YaHei, SimHei, Arial, sans-serif', fontSize: 14 }); async function renderDiagram() { try { console.log('Starting mermaid render'); const element = document.getElementById('mermaid-diagram'); await mermaid.run({ nodes: [element] }); console.log('Mermaid render completed'); if (element) { element.setAttribute('data-rendered', 'true'); } } catch (error) { console.error('Mermaid render error:', error); const errorMsg = error && error.message ? error.message : 'Unknown error'; document.body.innerHTML = '<div style="color: red;">Render Error: ' + errorMsg + '</div>'; } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', renderDiagram); } else { renderDiagram(); } </script> </body> </html>`; } /** * 渲染为 PNG 格式 */ private async renderPNG(page: Page, outputPath: string, width: number, height: number): Promise<void> { // 获取 SVG 元素 const svgElement = await page.$('#mermaid-diagram svg'); if (!svgElement) { throw new Error('无法找到生成的 SVG 元素'); } // 截取 SVG 元素的截图,使用高质量设置 const screenshot = await svgElement.screenshot({ type: 'png', omitBackground: false, clip: undefined, // 让Puppeteer自动检测边界 }); // 使用 Sharp 处理图像,提高质量 const processedImage = await sharp(screenshot) .resize(width * 2, height * 2, { // 先放大2倍提高质量 fit: 'inside', withoutEnlargement: false, background: 'white', }) .resize(width, height, { // 再缩小到目标尺寸,实现抗锯齿效果 fit: 'inside', withoutEnlargement: false, kernel: sharp.kernel.lanczos3, // 使用高质量缩放算法 }) .png({ quality: 100, compressionLevel: 0, // 不压缩,保持最高质量 adaptiveFiltering: false, force: true }) .toBuffer(); await fs.writeFile(outputPath, processedImage); } /** * 渲染为 SVG 格式 */ private async renderSVG(page: Page, outputPath: string): Promise<void> { const svgContent = await page.$eval('#mermaid-diagram svg', (element: Element) => { return element.outerHTML; }); if (!svgContent) { throw new Error('无法获取 SVG 内容'); } await fs.writeFile(outputPath, svgContent, 'utf-8'); } /** * 渲染为 PDF 格式 */ private async renderPDF(page: Page, outputPath: string, width: number, height: number): Promise<void> { const pdf = await page.pdf({ path: outputPath, format: 'A4', printBackground: true, width: `${width}px`, height: `${height}px`, margin: { top: '20px', right: '20px', bottom: '20px', left: '20px', }, }); } /** * 清理资源 */ async cleanup(): Promise<void> { if (this.browser) { await this.browser.close(); this.browser = null; } } }

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pickstar-2002/mermaid-chart-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server