drawio-mcp-server 2.1.0 → 2.1.1
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 +1 -1
- package/build/index.js +1 -1
- package/build/register-tool.js +72 -20
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ Experimental: integration with the **draw.io desktop (Electron) app** is in prog
|
|
|
41
41
|
|
|
42
42
|
## Requirements
|
|
43
43
|
|
|
44
|
-
- **Node.js** (
|
|
44
|
+
- **Node.js** (v22 or higher; tested against v22 LTS and v24 LTS) - Runtime environment for the MCP server
|
|
45
45
|
- **MCP client** - Claude Desktop, Claude Code, Zed, Codex, OpenCode, or any MCP-compatible host
|
|
46
46
|
|
|
47
47
|
### For Built-in Editor
|
package/build/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { join } from "node:path";
|
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { readFileSync, existsSync, statSync, readdirSync, realpathSync, } from "node:fs";
|
|
14
14
|
import { WebSocket, WebSocketServer } from "ws";
|
|
15
|
-
const VERSION = process.env.npm_package_version ?? "2.1.
|
|
15
|
+
const VERSION = process.env.npm_package_version ?? "2.1.1";
|
|
16
16
|
import { buildConfig, defaultConfig, hasFlag, shouldShowHelp, getHttpFeatureConfig, } from "./config.js";
|
|
17
17
|
import { installDesktopPlugin } from "./install-desktop-plugin.js";
|
|
18
18
|
import { bus_reply_stream, bus_request_stream, } from "./types.js";
|
package/build/register-tool.js
CHANGED
|
@@ -1,31 +1,83 @@
|
|
|
1
1
|
import { stripSchemaRecursively } from "./strip-schema.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* `$schema` from
|
|
3
|
+
* Wraps an `McpServer` so that the `tools/list` response strips the
|
|
4
|
+
* `$schema` key from every tool's `inputSchema` and `outputSchema`.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* drop tools because the `$` character fails Anthropic's validation regex
|
|
8
|
-
* `^[a-zA-Z0-9_.-]{1,64}$`.
|
|
6
|
+
* Why this is necessary:
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* - The MCP SDK lazily serializes tool schemas: it stores the Zod object
|
|
9
|
+
* verbatim at registration time and only converts to JSON Schema
|
|
10
|
+
* (via `zodToJsonSchema` / `z4mini.toJSONSchema`) when a client issues
|
|
11
|
+
* `tools/list`. The conversion emits a top-level
|
|
12
|
+
* `"$schema": "http://json-schema.org/draft-07/schema#"` field.
|
|
13
|
+
*
|
|
14
|
+
* - Claude Code's MCP ingest layer can't tolerate the `$` character in
|
|
15
|
+
* schema keys (Anthropic's parameter-name regex is
|
|
16
|
+
* `^[a-zA-Z0-9_.-]{1,64}$`). When `$schema` is present, the entire
|
|
17
|
+
* `properties` object is dropped, leaving tools effectively schemaless.
|
|
18
|
+
* The user can see the tool name but the LLM has no way to know which
|
|
19
|
+
* arguments to send, so calls fail at runtime.
|
|
20
|
+
*
|
|
21
|
+
* - A previous attempt at this fix tried to strip `$schema` from the
|
|
22
|
+
* stored `_registeredTools[name].inputSchema` via `setTimeout(0)`.
|
|
23
|
+
* That was a no-op against modern SDKs because the stored value at
|
|
24
|
+
* that point is the raw Zod object (which doesn't have `$schema`).
|
|
25
|
+
* The `$schema` key is added by `toJsonSchemaCompat` at serialization
|
|
26
|
+
* time, not at registration time.
|
|
27
|
+
*
|
|
28
|
+
* The fix here intercepts the `tools/list` request handler that the SDK
|
|
29
|
+
* registers internally on the first `server.tool(...)` call, and wraps
|
|
30
|
+
* it so the response runs through `stripSchemaRecursively` before being
|
|
31
|
+
* returned to the client.
|
|
12
32
|
*/
|
|
13
33
|
export function createServerWithSchemaStripping(server) {
|
|
14
|
-
// Store original tool method
|
|
15
34
|
const originalTool = server.tool.bind(server);
|
|
16
|
-
|
|
17
|
-
server.tool = function tool(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
}, 0);
|
|
35
|
+
let patched = false;
|
|
36
|
+
server.tool = function tool(...args) {
|
|
37
|
+
const result = originalTool(...args);
|
|
38
|
+
// The SDK lazily registers the tools/list handler on the first
|
|
39
|
+
// server.tool() call (via setToolRequestHandlers). After that call
|
|
40
|
+
// returns, the handler is in place and we can wrap it.
|
|
41
|
+
if (!patched) {
|
|
42
|
+
patched = true;
|
|
43
|
+
patchToolsListHandler(server);
|
|
44
|
+
}
|
|
28
45
|
return result;
|
|
29
46
|
};
|
|
30
47
|
return server;
|
|
31
48
|
}
|
|
49
|
+
function patchToolsListHandler(server) {
|
|
50
|
+
// The McpServer wraps an inner Server (the low-level protocol layer).
|
|
51
|
+
// Its `_requestHandlers` Map stores per-method handlers keyed by the
|
|
52
|
+
// method name literal (e.g. "tools/list").
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
const innerServer = server.server;
|
|
55
|
+
const handlers = innerServer?._requestHandlers;
|
|
56
|
+
if (!handlers) {
|
|
57
|
+
// SDK internals changed; fail closed (no-op) so we don't crash the
|
|
58
|
+
// server. The tools will still work but schemas will leak `$schema`.
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const original = handlers.get("tools/list");
|
|
62
|
+
if (!original) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
handlers.set("tools/list", async (...callArgs) => {
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
+
const response = await original(...callArgs);
|
|
68
|
+
if (response && Array.isArray(response.tools)) {
|
|
69
|
+
response.tools = response.tools.map(stripToolSchemas);
|
|
70
|
+
}
|
|
71
|
+
return response;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function stripToolSchemas(tool) {
|
|
75
|
+
const next = { ...tool };
|
|
76
|
+
if (next.inputSchema) {
|
|
77
|
+
next.inputSchema = stripSchemaRecursively(next.inputSchema);
|
|
78
|
+
}
|
|
79
|
+
if (next.outputSchema) {
|
|
80
|
+
next.outputSchema = stripSchemaRecursively(next.outputSchema);
|
|
81
|
+
}
|
|
82
|
+
return next;
|
|
83
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drawio-mcp-server",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Provides Draw.io services to MCP Clients",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
"@hono/node-server": "1.19.13",
|
|
35
35
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
36
36
|
"cachedir": "2.4.0",
|
|
37
|
-
"hono": "4.12.
|
|
37
|
+
"hono": "4.12.23",
|
|
38
38
|
"nanoid": "5.1.6",
|
|
39
39
|
"node-forge": "1.4.0",
|
|
40
40
|
"unzipper": "0.12.3",
|
|
41
|
-
"ws": "8.
|
|
41
|
+
"ws": "8.21.0",
|
|
42
42
|
"zod": "4.2.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|