Skip to content

Commit bc7655b

Browse files
authored
Merge pull request #33 from kk-michael/bugfix/issue-32-json-schema-draft-2020-12
fix: emit JSON Schema draft 2020-12 for tools/list (#32)
2 parents 0a1eed9 + 0a8a6de commit bc7655b

3 files changed

Lines changed: 53 additions & 1 deletion

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
"@modelcontextprotocol/sdk": "^1.12.0",
5151
"axios": "^1.9.0",
5252
"hono": "^4.12.12",
53-
"zod": "^3.25.28"
53+
"zod": "^3.25.28",
54+
"zod-to-json-schema": "^3.25.2"
5455
},
5556
"devDependencies": {
5657
"@biomejs/biome": "^2.4.10",

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/server.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2+
import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
3+
import type { ZodObject, ZodRawShape } from "zod";
4+
import { zodToJsonSchema } from "zod-to-json-schema";
25
import { generatedTools } from "./generated/tools.js";
36
import { createHandler } from "./handler.js";
47
import { createLogger } from "./utils/logger.js";
58

69
const logger = createLogger("MCP-Server");
710

11+
const JSON_SCHEMA_2020_12 = "https://json-schema.org/draft/2020-12/schema";
12+
813
function getEnabledTools() {
914
const enabledTags = process.env.DOKPLOY_ENABLED_TAGS;
1015

@@ -30,6 +35,38 @@ function getEnabledTools() {
3035
return filtered;
3136
}
3237

38+
function stripNestedSchemaKeys(value: unknown): void {
39+
if (value === null || typeof value !== "object") return;
40+
if (Array.isArray(value)) {
41+
for (const item of value) stripNestedSchemaKeys(item);
42+
return;
43+
}
44+
const record = value as Record<string, unknown>;
45+
for (const key of Object.keys(record)) {
46+
if (key === "$schema") {
47+
delete record[key];
48+
} else {
49+
stripNestedSchemaKeys(record[key]);
50+
}
51+
}
52+
}
53+
54+
// Claude's API requires JSON Schema draft 2020-12. The MCP SDK's built-in
55+
// Zod→JSON Schema converter emits draft-07 by default, which causes a 400
56+
// error on tools/list. We bypass the SDK's auto-generated handler by
57+
// registering our own with pre-converted draft-2020-12 schemas.
58+
// See https://github.com/Dokploy/mcp/issues/32
59+
function toDraft2020_12JsonSchema(schema: ZodObject<ZodRawShape>): Record<string, unknown> {
60+
const result = zodToJsonSchema(schema, {
61+
target: "jsonSchema2019-09",
62+
strictUnions: true,
63+
}) as Record<string, unknown>;
64+
65+
stripNestedSchemaKeys(result);
66+
result.$schema = JSON_SCHEMA_2020_12;
67+
return result;
68+
}
69+
3370
export function createServer() {
3471
const server = new McpServer({
3572
name: "dokploy",
@@ -48,5 +85,16 @@ export function createServer() {
4885
);
4986
}
5087

88+
const toolList = tools.map((tool) => ({
89+
name: tool.name,
90+
description: tool.description,
91+
inputSchema: toDraft2020_12JsonSchema(tool.schema),
92+
annotations: tool.annotations,
93+
}));
94+
95+
server.server.setRequestHandler(ListToolsRequestSchema, async () => ({
96+
tools: toolList,
97+
}));
98+
5199
return server;
52100
}

0 commit comments

Comments
 (0)