// npm package
mcp-mermaid
❤️ Generate mermaid diagram and chart with AI MCP dynamically.
versions
10
maintainers
1
license
MIT
first publish
2025-05-20
publisher
atool
tarball
28,854 B
AUTO-PUBLISHED·1 version indexed·latest published 2026-02-12
// publisher campaignby atool
9 caught packages from this accountThis is not an isolated catch. The same publisher has shipped 8 other packages that our pipeline flagged — the shape of a coordinated campaign, not a one-off. Each link below opens that sibling's analysis.
// offending code· @0.4.1· no static-pattern hits
llm: benign · 0.85→ No suspicious destination, no remote-exec shape — 1 known-vendor host(s).
- @0.4.1··AUTO-PUBLISHED·publisher: atoolheuristic 75/100static flags 0llm benign (0.85) via ollamainstall-scripts:postinstallmature-packagepublisher-multi-name-burst:5osv-flagged:MAL-2026-4147
→ No suspicious destination, no remote-exec shape — 1 known-vendor host(s).
// offending code· no static-pattern hits
--- install scripts --- ### postinstall playwright install --with-deps chromium ### prepare husky && npm run build ### prepublishOnly npm run build --- package.json (entry) --- { "name": "mcp-mermaid", "description": "❤️ Generate mermaid diagram and chart with AI MCP dynamically.", "version": "0.4.1", "main": "build/index.js", "type": "module", "scripts": { "test": "vitest", "prebuild": "rm -rf build/*", "build": "tsc && tsc-alias -p tsconfig.json", "start": "npx @modelcontextprotocol/inspector node build/index.js", "start:sse": "node build/index.js --transport sse --port 3033", "start:streamable": "node build/index.js --transport streamable --port 1122", "prepare": "husky && npm run build", "prepublishOnly": "npm run build", "postinstall": "playwright install --with-deps chromium" }, "bin": { "mcp-mermaid": "./build/index.js" }, "files": ["build"], "keywords": ["mcp", "mermaid", "data-visualization", "chart", "diagram"], "dependencies": { "@modelcontextprotocol/sdk": "^1.11.4", "cors": "^2.8.5", "express": "^5.1.0", "mermaid-isomorphic": "^3.0.4", "playwright": "^1.52.0", "zod": "^3.25.16", "zod-to-json-schema": "^3.24.5" }, "devDependencies": { "@biomejs/biome": "1.9.4", "@modelcontextprotocol/inspector": "^0.12.0", "@types/cors": "^2.8.19", " --- index.js (entry) --- #!/usr/bin/env node import { parseArgs } from "node:util"; import { runHTTPStreamableServer, runSSEServer, runStdioServer, } from "./server.js"; // Parse command line arguments const { values } = parseArgs({ options: { transport: { type: "string", short: "t", default: "stdio", }, port: { type: "string", short: "p", default: "3033", }, host: { type: "string", short: "H", default: "", }, endpoint: { type: "string", short: "e", default: "", // We'll handle defaults per transport type }, help: { type: "boolean", short: "h", }, }, }); // Display help information if requested if (values.help) { console.log(` MCP Mermaid CLI Options: --transport, -t Specify the transport protocol: "stdio", "sse", or "streamable" (default: "stdio") --port, -p Specify the port for SSE or streamable transport (default: 3033) --host, -H Specify the host to bind (e.g., 0.0.0.0) --endpoint, -e Specify the endpoint for the transport: --- bundled output (OSV-MAL flagged — LLM scope expansion) --- --- build/index.js (bundled) --- #!/usr/bin/env node import { parseArgs } from "node:util"; import { runHTTPStreamableServer, runSSEServer, runStdioServer, } from "./server.js"; // Parse command line arguments const { values } = parseArgs({ options: { transport: { type: "string", short: "t", default: "stdio", }, port: { type: "string", short: "p", default: "3033", }, host: { type: "string", short: "H", default: "", }, endpoint: { type: "string", short: "e", default: "", // We'll handle defaults per transport type }, help: { type: "boolean", short: "h", }, }, }); // Display help information if requested if (values.help) { console.log(` MCP Mermaid CLI Options: --transport, -t Specify the transport protocol: "stdio", "sse", or "streamable" (default: "stdio") --port, -p Specify the port for SSE or streamable transport (default: 3033) --host, -H Specify the host to bind (e.g., 0.0.0.0) --endpoint, -e Specify the endpoint for the transport: - For SSE: default is "/sse" - For streamable: default is "/mcp" --help, -h Show this help message `); process.exit(0); } // Run in the specified transport mode const transport = values.transport.toLowerCase(); if (transport === "sse") { const port = Number.parseInt(values.port, 10); // Use provided endpoint or default to "/sse" for SSE const endpoint = values.endpoint || "/sse"; const host = values.host || undefined; runSSEServer(endpoint, port, host).catch(console.error); } else if (transport === "streamable") { const port = Number.parseInt(values.port, 10); // Use provided endpoint or default to "/mcp" for streamable const endpoint = values.endpoint || "/mcp"; const host = values.host || undefined; --- build/server.js (bundled) --- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import * as fs from "node:fs"; import * as path from "node:path"; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js"; import { startHTTPStreamableServer, startSSEMcpServer, startStdioMcpServer, } from "./services/index.js"; import { schema, tool } from "./tools/index.js"; import { createMermaidInkUrl, renderMermaid } from "./utils/index.js"; import { Logger } from "./utils/logger.js"; /** * Creates and configures an MCP server for mermaid generation. */ export function createServer() { const server = new Server({ name: "mcp-mermaid", version: "0.1.3", }, { capabilities: { tools: {}, }, }); setupToolHandlers(server); server.onerror = (error) => Logger.error("MCP Error", error); return server; } /** * Sets up tool handlers for the MCP server. */ function setupToolHandlers(server) { server.setRequestHandler(ListToolsRequestSchema, () => __awaiter(this, void 0, void 0, function* () { return ({ tools: [tool], }); })); server.setRequestHandler(CallToolRequestSchema, (request) => __awaiter(this, void 0, void 0, function* () { if (request.params.name == --- build/utils/index.js (bundled) --- export { zodToJsonSchema } from "./schema.js"; export { renderMermaid } from "./render.js"; export { Logger } from "./logger.js"; export { createMermaidInkUrl } from "./mermaidUrl.js"; --- build/utils/logger.js (bundled) --- /** * Unified logger for consistent logging across the application */ const prefix = "[MCP-Mermaid]"; /** * Log info message */ export function info(message, ...args) { console.log(`${prefix} ℹ️ ${message}`, ...args); } /** * Log warning message */ export function warn(message, ...args) { console.warn(`${prefix} ⚠️ ${message}`, ...args); } /** * Log error message */ export function error(message, error) { console.error(`${prefix} ❌ ${message}`, error || ""); } /** * Log success message */ export function success(message, ...args) { console.log(`${prefix} ✅ ${message}`, ...args); } /** * Log server startup information */ export function serverStartup(serverType, port, endpoint) { const serverUrl = `http://localhost:${port}${endpoint}`; const healthUrl = `http://localhost:${port}/health`; const pingUrl = `http://localhost:${port}/ping`; console.log(`${prefix} �� ${serverType} running on: \x1b[32m\u001B[4m${serverUrl}\u001B[0m\x1b[0m`); console.log("\nTest endpoints:"); console.log(`• Health check: \u001B[4m${healthUrl}\u001B[0m`); console.log(`• Ping test: \u001B[4m${pingUrl}\u001B[0m`); } /** * Log cleanup information */ export function cleanup(message) { console.log(`${prefix} �� ${message}`); } /** * Logger object for backward compatibility */ export const Logger = { info, warn, error, success, serverStartup, cleanup, }; --- build/utils/mermaidUrl.js (bundled) --- import { deflateSync } from "node:zlib"; /** * Encodes mermaid text into the Base64URL deflated format used by mermaid.ink. */ function encodeMermaidToBase64Url(mermaid) { const compressed = deflateSync(mermaid, { level: 9 }); return compressed .toString("base64") .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=+$/g, ""); } /** * Creates a public mermaid.ink URL for the given mermaid definition. */ export function createMermaidInkUrl(mermaid, variant) { const encoded = encodeMermaidToBase64Url(mermaid); return `https://mermaid.ink/${variant}/pako:${encoded}`; } --- build/utils/render.js (bundled) --- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; import { createMermaidRenderer, } from "mermaid-isomorphic"; // Cache the renderer to avoid creating a new one every time. let renderer; /** * Ref: * - https://github.com/mermaid-js/mermaid-cli/blob/master/src/index.js * - https://github.com/remcohaszing/mermaid-isomorphic * @returns */ export function renderMermaid(mermaid_1) { return __awaiter(this, arguments, void 0, function* (mermaid, theme = "default", backgroundColor = "white") { if (!renderer) renderer = createMermaidRenderer(); const cssContent = `svg { background: ${backgroundColor}; }`; const cssTmpPath = path.join(os.tmpdir(), "mermaid-tmp-css.css"); fs.writeFileSync(cssTmpPath, cssContent); const r = yield renderer(
