11import { 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" ;
25import { generatedTools } from "./generated/tools.js" ;
36import { createHandler } from "./handler.js" ;
47import { createLogger } from "./utils/logger.js" ;
58
69const logger = createLogger ( "MCP-Server" ) ;
710
11+ const JSON_SCHEMA_2020_12 = "https://json-schema.org/draft/2020-12/schema" ;
12+
813function 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+
3370export 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