@volare-consulting/mermaid-mcp 0.1.0 → 0.2.0
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 +15 -5
- package/dist/render.js +64 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,14 +28,24 @@ supplied, the file is written there and the path is included in the summary.
|
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
npm install
|
|
31
|
-
npx puppeteer browsers install chrome # download the headless Chromium used for rendering
|
|
32
31
|
npm run build
|
|
33
32
|
```
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
(
|
|
38
|
-
|
|
34
|
+
## Browser requirement
|
|
35
|
+
|
|
36
|
+
Mermaid renders inside a real browser (it needs a DOM for layout), so **Google Chrome / Chromium is
|
|
37
|
+
required**. Puppeteer resolves it, in this order:
|
|
38
|
+
|
|
39
|
+
1. **`PUPPETEER_EXECUTABLE_PATH`** — an explicit Chrome/Chromium binary you point it at.
|
|
40
|
+
2. **Puppeteer's bundled Chromium** — downloaded by `@mermaid-js/mermaid-cli` during `npm install`.
|
|
41
|
+
3. **A system-installed Google Chrome** — used as a fallback (`channel: "chrome"`) if the bundled
|
|
42
|
+
browser can't be launched.
|
|
43
|
+
|
|
44
|
+
If none can be launched, the tool returns an actionable error: install Chrome, run
|
|
45
|
+
`npx puppeteer browsers install chrome`, or set `PUPPETEER_EXECUTABLE_PATH`.
|
|
46
|
+
|
|
47
|
+
> If Puppeteer's automatic Chromium download is blocked (a locked-down network, or — on some Windows
|
|
48
|
+
> machines — a stalled extraction), just install Google Chrome and the renderer falls back to it.
|
|
39
49
|
|
|
40
50
|
## Test
|
|
41
51
|
|
package/dist/render.js
CHANGED
|
@@ -3,11 +3,37 @@ import { tmpdir } from "node:os";
|
|
|
3
3
|
import { join, isAbsolute } from "node:path";
|
|
4
4
|
import { run as mmdcRun } from "@mermaid-js/mermaid-cli";
|
|
5
5
|
const PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47]);
|
|
6
|
+
// Substrings that mean "Puppeteer couldn't find or launch a browser" (as opposed
|
|
7
|
+
// to a genuine diagram/syntax error, which we must not silently retry).
|
|
8
|
+
const BROWSER_LAUNCH_HINTS = [
|
|
9
|
+
"could not find",
|
|
10
|
+
"failed to launch",
|
|
11
|
+
"browser was not found",
|
|
12
|
+
"executable doesn't exist",
|
|
13
|
+
"no usable sandbox",
|
|
14
|
+
"spawn",
|
|
15
|
+
"enoent",
|
|
16
|
+
];
|
|
17
|
+
function isBrowserLaunchError(err) {
|
|
18
|
+
const message = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
19
|
+
return BROWSER_LAUNCH_HINTS.some((hint) => message.includes(hint));
|
|
20
|
+
}
|
|
21
|
+
function browserSetupError(original) {
|
|
22
|
+
const detail = original instanceof Error ? original.message : String(original);
|
|
23
|
+
return new Error("Could not launch Chrome to render the diagram. Fix any one of:\n" +
|
|
24
|
+
" - Install Google Chrome, or run: npx puppeteer browsers install chrome\n" +
|
|
25
|
+
" - Or set PUPPETEER_EXECUTABLE_PATH to a Chrome/Chromium binary.\n" +
|
|
26
|
+
`Original error: ${detail}`);
|
|
27
|
+
}
|
|
6
28
|
/**
|
|
7
29
|
* Render a Mermaid diagram to a PNG using @mermaid-js/mermaid-cli (Puppeteer/Chromium).
|
|
8
30
|
*
|
|
9
31
|
* The mermaid-cli programmatic API works off files, so the source is written to a
|
|
10
32
|
* temporary `.mmd` file and the PNG is produced either at `outputPath` or in a temp dir.
|
|
33
|
+
*
|
|
34
|
+
* Puppeteer resolves the browser: the first attempt uses `PUPPETEER_EXECUTABLE_PATH`
|
|
35
|
+
* (if set) or Puppeteer's bundled Chromium; if that can't launch, it falls back to a
|
|
36
|
+
* system-installed Google Chrome (`channel: "chrome"`). No hardcoded paths.
|
|
11
37
|
*/
|
|
12
38
|
export async function renderMermaidToPng(opts) {
|
|
13
39
|
const diagram = opts.diagram?.trim();
|
|
@@ -25,22 +51,45 @@ export async function renderMermaidToPng(opts) {
|
|
|
25
51
|
const isTemp = !opts.outputPath;
|
|
26
52
|
const outputPath = opts.outputPath ?? join(workDir, "diagram.png");
|
|
27
53
|
await writeFile(inputPath, diagram, "utf8");
|
|
54
|
+
const parseMMDOptions = {
|
|
55
|
+
backgroundColor: opts.backgroundColor ?? "white",
|
|
56
|
+
mermaidConfig: { theme: opts.theme ?? "default" },
|
|
57
|
+
viewport: opts.width || opts.height || opts.scale
|
|
58
|
+
? {
|
|
59
|
+
width: opts.width ?? 800,
|
|
60
|
+
height: opts.height ?? 600,
|
|
61
|
+
deviceScaleFactor: opts.scale ?? 1,
|
|
62
|
+
}
|
|
63
|
+
: undefined,
|
|
64
|
+
};
|
|
65
|
+
const baseArgs = ["--no-sandbox", "--disable-setuid-sandbox"];
|
|
66
|
+
// Browser sources tried in order, all resolved by Puppeteer itself.
|
|
67
|
+
const puppeteerConfigs = [
|
|
68
|
+
{ args: baseArgs }, // PUPPETEER_EXECUTABLE_PATH or Puppeteer's bundled Chromium
|
|
69
|
+
{ args: baseArgs, channel: "chrome" }, // system-installed Google Chrome
|
|
70
|
+
];
|
|
28
71
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
72
|
+
let launchError;
|
|
73
|
+
let rendered = false;
|
|
74
|
+
for (const puppeteerConfig of puppeteerConfigs) {
|
|
75
|
+
try {
|
|
76
|
+
await mmdcRun(inputPath, outputPath, {
|
|
77
|
+
quiet: true,
|
|
78
|
+
puppeteerConfig,
|
|
79
|
+
parseMMDOptions,
|
|
80
|
+
});
|
|
81
|
+
rendered = true;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
if (!isBrowserLaunchError(err))
|
|
86
|
+
throw err; // genuine render/syntax error
|
|
87
|
+
launchError = err; // browser source unavailable — try the next one
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!rendered) {
|
|
91
|
+
throw browserSetupError(launchError);
|
|
92
|
+
}
|
|
44
93
|
const bytes = await readFile(outputPath);
|
|
45
94
|
if (!bytes.subarray(0, 4).equals(PNG_MAGIC)) {
|
|
46
95
|
throw new Error("Render produced a file that is not a valid PNG.");
|