Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.2.3] - 2026-02-01

- Harden MCP request handling

## [0.2.2] - 2026-02-01

- Fix MCP tool naming for Claude Desktop
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,22 +194,22 @@ Your code takes those arguments, calls mermkit, and sends the result back. The L

### MCP compatibility

mermkit ships an MCP server over stdio. Any MCP-compatible host (Claude Desktop, Cursor, etc.) can use it directly.
mermkit ships an MCP server over stdio. Any MCP-compatible host can use it directly.

**Claude Desktop configuration:**
**MCP host configuration (example):**

```json
{
"mcpServers": {
"mermkit": {
"command": "npx",
"args": ["@mermkit/cli", "mcp"]
"args": ["-y", "@mermkit/cli@0.2.3", "mcp"]
}
}
}
```

Once connected, the host gains access to the same five tools listed above. In MCP hosts that restrict tool names, they are exposed as `mermkit_render`, `mermkit_renderBatch`, `mermkit_extract`, `mermkit_term`, and `mermkit_schema`. The MCP server reuses the same rendering and extraction logic as `serve` — it is a format translation layer (JSON-RPC 2.0) with no additional dependencies.
Once connected, the host gains access to the same five tools listed above. For hosts that restrict tool names, they are exposed as `mermkit_render`, `mermkit_renderBatch`, `mermkit_extract`, `mermkit_term`, and `mermkit_schema`. The MCP server reuses the same rendering and extraction logic as `serve` — it is a format translation layer (JSON-RPC 2.0) with no additional dependencies.

## Preview server

Expand Down
2 changes: 1 addition & 1 deletion bindings/python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "mermkit"
version = "0.2.2"
version = "0.2.3"
description = "Python bindings for mermkit (Mermaid rendering toolkit)"
readme = "README.md"
requires-python = ">=3.8"
Expand Down
2 changes: 1 addition & 1 deletion bindings/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mermkit"
version = "0.2.2"
version = "0.2.3"
edition = "2021"
description = "Rust bindings for mermkit (Mermaid rendering toolkit)"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mermkit",
"private": true,
"version": "0.2.2",
"version": "0.2.3",
"repository": {
"type": "git",
"url": "https://github.com/MermaidKit/mermkit.git"
Expand Down
4 changes: 2 additions & 2 deletions packages/adapters/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mermkit/adapters",
"version": "0.2.2",
"version": "0.2.3",
"repository": {
"type": "git",
"url": "https://github.com/MermaidKit/mermkit.git"
Expand All @@ -18,6 +18,6 @@
"build": "tsc -b"
},
"dependencies": {
"@mermkit/core": "0.2.2"
"@mermkit/core": "0.2.3"
}
}
8 changes: 4 additions & 4 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mermkit/cli",
"version": "0.2.2",
"version": "0.2.3",
"repository": {
"type": "git",
"url": "https://github.com/MermaidKit/mermkit.git"
Expand All @@ -19,8 +19,8 @@
"build": "tsc -b"
},
"dependencies": {
"@mermkit/core": "0.2.2",
"@mermkit/render": "0.2.2",
"@mermkit/adapters": "0.2.2"
"@mermkit/core": "0.2.3",
"@mermkit/render": "0.2.3",
"@mermkit/adapters": "0.2.3"
}
}
30 changes: 24 additions & 6 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node

import { watch } from "node:fs";
import { readFileSync, watch } from "node:fs";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { spawn } from "node:child_process";
import { createServer } from "node:http";
Expand All @@ -12,6 +12,8 @@ import { createHash } from "node:crypto";
import { extractDiagrams, normalizeDiagram } from "@mermkit/core";
import { render, renderForTerminal } from "@mermkit/render";

const PACKAGE_VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version as string;

type Flags = Record<string, string | boolean>;

const [command, ...rest] = process.argv.slice(2);
Expand Down Expand Up @@ -753,11 +755,15 @@ function writeMcpResponse(response: JsonRpcResponse): void {
stdout.write(`${JSON.stringify(response)}\n`);
}

function shouldRespondToId(id: unknown): id is string | number {
return typeof id === "string" || typeof id === "number";
}

function maybeWriteMcpResponse(
id: string | number | undefined,
id: unknown,
response: Omit<JsonRpcResponse, "id">
): void {
if (typeof id !== "string" && typeof id !== "number") return;
if (!shouldRespondToId(id)) return;
writeMcpResponse({ ...response, id });
}

Expand All @@ -776,7 +782,8 @@ function getMcpToolNameMap(): Map<string, string> {
return MCP_TOOL_NAME_MAP;
}

function resolveMcpToolName(name: string): string {
function resolveMcpToolName(name: unknown): string | undefined {
if (typeof name !== "string") return undefined;
if (name.includes(".")) return name;
return getMcpToolNameMap().get(name) ?? name;
}
Expand All @@ -799,18 +806,26 @@ async function cmdMcp(): Promise<void> {
try {
request = JSON.parse(trimmed) as JsonRpcRequest;
} catch (error) {
stderr.write(`mcp parse error: ${errorMessage(error)}\n`);
continue;
}

const id = request.id;
if (!request.method || typeof request.method !== "string") {
maybeWriteMcpResponse(id, {
jsonrpc: "2.0",
error: { code: -32600, message: "invalid request: missing method" }
});
continue;
}

if (request.method === "initialize") {
maybeWriteMcpResponse(id, {
jsonrpc: "2.0",
result: {
protocolVersion: "2024-11-05",
capabilities: { tools: {} },
serverInfo: { name: "mermkit", version: "0.1.0" }
serverInfo: { name: "mermkit", version: PACKAGE_VERSION }
}
});
continue;
Expand All @@ -826,10 +841,13 @@ async function cmdMcp(): Promise<void> {

if (request.method === "tools/call") {
const params = request.params ?? {};
const toolName = resolveMcpToolName(params.name as string);
const toolName = resolveMcpToolName((params as { name?: unknown }).name);
const toolInput = (params.input ?? {}) as Record<string, unknown>;

try {
if (!toolName) {
throw new Error("invalid request: tools/call requires a tool name");
}
const content = await executeMcpTool(toolName, toolInput);
maybeWriteMcpResponse(id, { jsonrpc: "2.0", result: { content } });
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mermkit/core",
"version": "0.2.2",
"version": "0.2.3",
"repository": {
"type": "git",
"url": "https://github.com/MermaidKit/mermkit.git"
Expand Down
4 changes: 2 additions & 2 deletions packages/render/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mermkit/render",
"version": "0.2.2",
"version": "0.2.3",
"repository": {
"type": "git",
"url": "https://github.com/MermaidKit/mermkit.git"
Expand All @@ -18,7 +18,7 @@
"build": "tsc -b"
},
"dependencies": {
"@mermkit/core": "0.2.2"
"@mermkit/core": "0.2.3"
},
"optionalDependencies": {
"dompurify": "^3.1.5",
Expand Down
14 changes: 11 additions & 3 deletions site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -493,19 +493,27 @@ <h2>MCP setup</h2>

<div class="install-block">
<div class="install-label">command</div>
<pre><code>npx @mermkit/cli mcp</code></pre>
<pre><code>npx -y @mermkit/cli@0.2.3 mcp</code></pre>
</div>
<div class="install-block">
<div class="install-label">Claude Desktop config</div>
<div class="install-label">MCP host config</div>
<pre><code>{
"mcpServers": {
"mermkit": {
"command": "npx",
"args": ["@mermkit/cli", "mcp"]
"args": ["-y", "@mermkit/cli@0.2.3", "mcp"]
}
}
}</code></pre>
</div>
<div class="install-block">
<div class="install-label">tool names</div>
<pre><code>mermkit_render
mermkit_renderBatch
mermkit_extract
mermkit_term
mermkit_schema</code></pre>
</div>
</div>
</section>

Expand Down
Loading