Skip to content

Commit 1156cbc

Browse files
Feat/add mermaid chart rendering (#5377)
Signed-off-by: Arya Pratap Singh <notaryasingh@gmail.com>
1 parent 786e708 commit 1156cbc

4 files changed

Lines changed: 2294 additions & 0 deletions

File tree

crates/goose-mcp/src/autovisualiser/mod.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,13 @@ pub struct ShowChartParams {
387387
pub data: ChartData,
388388
}
389389

390+
/// Parameters for render_mermaid tool
391+
#[derive(Debug, Serialize, Deserialize, rmcp::schemars::JsonSchema)]
392+
pub struct RenderMermaidParams {
393+
/// The Mermaid diagram code to render
394+
pub mermaid_code: String,
395+
}
396+
390397
/// An extension for automatic data visualization and UI generation
391398
#[derive(Clone)]
392399
pub struct AutoVisualiserRouter {
@@ -448,6 +455,7 @@ impl AutoVisualiserRouter {
448455
- **render_treemap**: Creates interactive treemap visualizations for hierarchical data
449456
- **render_chord**: Creates interactive chord diagrams for relationship/flow visualization
450457
- **render_map**: Creates interactive map visualizations with location markers
458+
- **render_mermaid**: Creates interactive Mermaid diagrams from Mermaid syntax
451459
- **show_chart**: Creates interactive line, scatter, or bar charts for data visualization
452460
"#};
453461

@@ -977,6 +985,61 @@ Example:
977985
.with_audience(vec![Role::User])]))
978986
}
979987

988+
/// show a Mermaid diagram from Mermaid syntax
989+
#[tool(
990+
name = "render_mermaid",
991+
description = r#"show a Mermaid diagram from Mermaid syntax
992+
993+
Provide the Mermaid code as a string. Supports flowcharts, sequence diagrams, Gantt charts, etc.
994+
995+
Example:
996+
graph TD;
997+
A-->B;
998+
A-->C;
999+
B-->D;
1000+
C-->D;
1001+
"#
1002+
)]
1003+
pub async fn render_mermaid(
1004+
&self,
1005+
params: Parameters<RenderMermaidParams>,
1006+
) -> Result<CallToolResult, ErrorData> {
1007+
let mermaid_code = params.0.mermaid_code;
1008+
1009+
// Load all resources at compile time using include_str!
1010+
const TEMPLATE: &str = include_str!("templates/mermaid_template.html");
1011+
const MERMAID_MIN: &str = include_str!("templates/assets/mermaid.min.js");
1012+
1013+
// Replace all placeholders with actual content
1014+
let html_content = TEMPLATE
1015+
.replace("{{MERMAID_MIN}}", MERMAID_MIN)
1016+
.replace("{{MERMAID_CODE}}", &mermaid_code);
1017+
1018+
// Save to /tmp/mermaid.html for debugging
1019+
let debug_path = std::path::Path::new("/tmp/mermaid.html");
1020+
if let Err(e) = std::fs::write(debug_path, &html_content) {
1021+
tracing::warn!("Failed to write debug HTML to /tmp/mermaid.html: {}", e);
1022+
} else {
1023+
tracing::info!("Debug HTML saved to /tmp/mermaid.html");
1024+
}
1025+
1026+
// Use BlobResourceContents with base64 encoding to avoid JSON string escaping issues
1027+
let html_bytes = html_content.as_bytes();
1028+
let base64_encoded = STANDARD.encode(html_bytes);
1029+
1030+
let resource_contents = ResourceContents::BlobResourceContents {
1031+
uri: "ui://mermaid/diagram".to_string(),
1032+
mime_type: Some("text/html".to_string()),
1033+
blob: base64_encoded,
1034+
meta: None,
1035+
};
1036+
1037+
Ok(CallToolResult::success(vec![Content::resource(
1038+
resource_contents,
1039+
)
1040+
.with_audience(vec![Role::User])]))
1041+
}
1042+
9801043
/// show interactive line, scatter, or bar charts
9811044
#[tool(
9821045
name = "show_chart",
@@ -1472,4 +1535,32 @@ mod tests {
14721535
&vec![Role::User]
14731536
);
14741537
}
1538+
1539+
#[tokio::test]
1540+
async fn test_render_mermaid() {
1541+
let router = AutoVisualiserRouter::new();
1542+
let params = Parameters(RenderMermaidParams {
1543+
mermaid_code: r#"graph TD;
1544+
A-->B;
1545+
A-->C;
1546+
B-->D;
1547+
C-->D;"#
1548+
.to_string(),
1549+
});
1550+
1551+
let result = router.render_mermaid(params).await;
1552+
if let Err(e) = &result {
1553+
eprintln!("Error in test_render_mermaid: {:?}", e);
1554+
}
1555+
assert!(result.is_ok());
1556+
let tool_result = result.unwrap();
1557+
assert_eq!(tool_result.content.len(), 1);
1558+
1559+
// Check the audience is set to User
1560+
assert!(tool_result.content[0].audience().is_some());
1561+
assert_eq!(
1562+
tool_result.content[0].audience().unwrap(),
1563+
&vec![Role::User]
1564+
);
1565+
}
14751566
}

crates/goose-mcp/src/autovisualiser/templates/assets/mermaid.min.js

Lines changed: 2029 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Mermaid Diagram</title>
7+
8+
<script>
9+
{{MERMAID_MIN}}
10+
</script>
11+
12+
<style>
13+
body {
14+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
15+
margin: 0;
16+
padding: 20px;
17+
background-color: #f5f5f5;
18+
color: #333;
19+
}
20+
21+
.container {
22+
max-width: 1200px;
23+
margin: 0 auto;
24+
background: white;
25+
border-radius: 12px;
26+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
27+
overflow: hidden;
28+
padding: 30px;
29+
}
30+
31+
.header {
32+
text-align: center;
33+
margin-bottom: 30px;
34+
}
35+
36+
.header h1 {
37+
margin: 0 0 10px 0;
38+
font-size: 2em;
39+
font-weight: 300;
40+
color: #333;
41+
}
42+
43+
.mermaid {
44+
display: flex;
45+
justify-content: center;
46+
align-items: center;
47+
min-height: 400px;
48+
}
49+
50+
/* Mermaid specific styles */
51+
.mermaid .node {
52+
fill: #667eea !important;
53+
stroke: #4a5fc1 !important;
54+
stroke-width: 2px !important;
55+
}
56+
57+
.mermaid .node text {
58+
fill: white !important;
59+
font-weight: 500 !important;
60+
}
61+
62+
.mermaid .edgePath path {
63+
stroke: #667eea !important;
64+
stroke-width: 2px !important;
65+
}
66+
67+
.mermaid .edgeLabel text {
68+
fill: #333 !important;
69+
}
70+
</style>
71+
</head>
72+
<body>
73+
<div class="container">
74+
<div class="header">
75+
<h1>Mermaid Diagram</h1>
76+
</div>
77+
<div class="mermaid">
78+
{{MERMAID_CODE}}
79+
</div>
80+
</div>
81+
82+
<script>
83+
// Initialize Mermaid
84+
mermaid.initialize({
85+
startOnLoad: true,
86+
theme: 'default',
87+
themeVariables: {
88+
primaryColor: '#667eea',
89+
primaryTextColor: '#fff',
90+
primaryBorderColor: '#4a5fc1',
91+
lineColor: '#667eea',
92+
sectionBkgColor: '#f8f9fa',
93+
altSectionBkgColor: '#e9ecef',
94+
gridColor: '#e9ecef',
95+
tertiaryColor: '#f8f9fa'
96+
},
97+
flowchart: {
98+
useMaxWidth: true,
99+
htmlLabels: true,
100+
curve: 'basis'
101+
},
102+
sequence: {
103+
useMaxWidth: true,
104+
htmlLabels: true,
105+
diagramMarginX: 50,
106+
diagramMarginY: 10,
107+
actorMargin: 50,
108+
width: 150,
109+
height: 65,
110+
boxMargin: 10,
111+
boxTextMargin: 5,
112+
noteMargin: 10,
113+
messageMargin: 35,
114+
mirrorActors: true,
115+
bottomMarginAdj: 1,
116+
useMaxWidth: true
117+
},
118+
gantt: {
119+
useMaxWidth: true,
120+
titleTopMargin: 25,
121+
barHeight: 20,
122+
barGap: 4,
123+
topPadding: 50,
124+
leftPadding: 75,
125+
gridLineStartPadding: 35,
126+
fontSize: 11,
127+
fontFamily: '"Open Sans", sans-serif',
128+
numberSectionStyles: 4,
129+
axisFormat: '%Y-%m-%d'
130+
}
131+
});
132+
133+
function reportContentSize() {
134+
const contentHeight = Math.max(
135+
document.body.scrollHeight,
136+
document.body.offsetHeight,
137+
document.documentElement.clientHeight,
138+
document.documentElement.scrollHeight,
139+
document.documentElement.offsetHeight
140+
);
141+
142+
// Send size change message to parent window (for MCP-UI iframe auto-resize)
143+
if (window.parent !== window) {
144+
window.parent.postMessage({
145+
type: 'ui-size-change',
146+
payload: {
147+
height: contentHeight
148+
}
149+
}, '*');
150+
}
151+
}
152+
153+
// Initialize on load
154+
window.onload = function() {
155+
// Report initial size
156+
setTimeout(reportContentSize, 100);
157+
158+
// Watch for size changes using ResizeObserver if available
159+
if (typeof ResizeObserver !== 'undefined') {
160+
const resizeObserver = new ResizeObserver(() => {
161+
reportContentSize();
162+
});
163+
resizeObserver.observe(document.body);
164+
resizeObserver.observe(document.documentElement);
165+
}
166+
167+
// Fallback: also report on window resize
168+
window.addEventListener('resize', reportContentSize);
169+
};
170+
</script>
171+
</body>
172+
</html></content>
173+
<parameter name="filePath">c:\Users\ARYA SINGH\Dropbox\PC\Desktop\goose\crates\goose-mcp\src\autovisualiser\templates\mermaid_template.html

documentation/docs/mcp/autovisualiser-mcp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ The Auto Visualiser is a powerful extension that integrates with Goose's MCP-UI
6161
| **Treemap Visualizations** | Hierarchical data with proportional area representation | Hierarchical data <br/>(nested categories, organizational structures) |
6262
| **Chord Diagrams** | Relationship and flow visualization between entities | Relationship matrices <br/>(network connections, cross-references) |
6363
| **Interactive Maps** | Geographic data visualization with location markers using Leaflet | Geographic information <br/>(location data, coordinates, addresses) |
64+
| **Mermaid Diagrams** | Flowcharts, sequence diagrams, Gantt charts, and other diagram types using Mermaid syntax | Diagram creation <br/>(flowcharts, sequence diagrams, architecture diagrams) |
6465
| **Line/Bar/Scatter Charts** | Traditional chart types for data analysis | Time series data <br/>(historical data, trends over time) |
6566

6667
### Example Visualizations

0 commit comments

Comments
 (0)