@@ -23,6 +23,7 @@ import {
2323 ServerOptions
2424} from './types.js' ;
2525import { Telemetry , TelemetryContext } from './telemetry.js' ;
26+ import { HttpSetup } from './httpSetup.js' ;
2627import { PACKAGE_NAME , PACKAGE_VERSION } from './package-info.js' ;
2728
2829export class MCPOpenAPIServer {
@@ -329,6 +330,14 @@ export class MCPOpenAPIServer {
329330 return this . generateShortToolName ( specId , pathPattern , method ) ;
330331 }
331332
333+ /**
334+ * Generates concise, human-readable MCP tool names from OpenAPI specs by combining
335+ * specId, HTTP method, and path components while staying under length limits for
336+ * MCP client compatibility (e.g., Cursor IDE's 60-character tool name restriction).
337+ *
338+ * MCP tool names are required as per the specs to register all the various MCP
339+ * capabilities (tools, resources, prompts). It also has to be unique.
340+ */
332341 private generateShortToolName ( specId : string , pathPattern : string , method : string ) : string {
333342 // Server name is "mcp-openapi" (11 chars), leaving ~49 chars for tool name to stay under 60
334343 // Reason for short tool name is because some MCP clients like Cursor IDE, as of this writing,
@@ -518,10 +527,11 @@ export class MCPOpenAPIServer {
518527 } ;
519528 }
520529
521- private sanitizePath ( pathPattern : string ) : string {
522- return pathPattern . replace ( / [ ^ a - z A - Z 0 - 9 ] / g, '_' ) ;
523- }
524-
530+ /**
531+ * Builds JSON Schema for MCP tool input parameters by extracting path parameters,
532+ * query parameters, and request body schema from OpenAPI operation definitions.
533+ * Used during tool generation to define the input validation schema for each MCP tool.
534+ */
525535 private buildInputSchema ( operation : any , pathPattern : string ) {
526536 const properties : any = { } ;
527537 const required : string [ ] = [ ] ;
@@ -570,6 +580,12 @@ export class MCPOpenAPIServer {
570580 } ;
571581 }
572582
583+ /**
584+ * Configures MCP protocol request handlers for stdio transport, mapping standard
585+ * MCP methods (tools/list, resources/read, etc.) to server implementation functions.
586+ * Sets up the bidirectional communication layer between MCP clients and this server.
587+ * Note: HTTP mode uses manual request routing instead of these handlers.
588+ */
573589 private setupRequestHandlers ( ) : void {
574590 // Set up MCP protocol handlers for stdio transport
575591 this . telemetry . debug ( '🔧 Setting up MCP request handlers...' ) ;
@@ -1136,147 +1152,20 @@ export class MCPOpenAPIServer {
11361152
11371153 const serverPort = port || this . options . port ! ;
11381154
1139- this . app . post ( '/mcp' , async ( req , res ) => {
1140- try {
1141- // Extract user context from request
1142- const userContext = this . extractUserContext ( req ) ;
1143-
1144- // Handle JSON-RPC 2.0 method calls
1145- const { method, params, id } = req . body ;
1146-
1147- this . telemetry . debug ( `MCP method call: ${ method } ` ) ;
1148-
1149- let result ;
1150-
1151- switch ( method ) {
1152- case 'initialize' :
1153- result = {
1154- message : "MCP server running" ,
1155- authMode : userContext . token ? "user-token" : "service-token" ,
1156- capabilities : {
1157- tools : { listChanged : true } ,
1158- resources : { listChanged : true , subscribe : false } ,
1159- prompts : { listChanged : true }
1160- }
1161- } ;
1162- break ;
1163-
1164- case 'tools/list' :
1165- result = {
1166- tools : this . tools . map ( tool => ( {
1167- name : tool . name ,
1168- description : tool . description ,
1169- inputSchema : tool . inputSchema
1170- } ) )
1171- } ;
1172- break ;
1173-
1174- case 'resources/list' :
1175- result = {
1176- resources : this . resources . map ( resource => ( {
1177- uri : resource . uri ,
1178- name : resource . name ,
1179- description : resource . description ,
1180- mimeType : resource . mimeType ,
1181- parameters : resource . parameters
1182- } ) )
1183- } ;
1184- break ;
1185-
1186- case 'prompts/list' :
1187- result = {
1188- prompts : Array . from ( this . prompts . entries ( ) ) . map ( ( [ name , spec ] ) => ( {
1189- name : name ,
1190- description : spec . description || `${ name } prompt template` ,
1191- arguments : spec . arguments || [ ]
1192- } ) )
1193- } ;
1194- break ;
1195-
1196- case 'tools/call' :
1197- const toolName = params ?. name ;
1198- const toolArgs = params ?. arguments || { } ;
1199- if ( ! toolName ) {
1200- throw new Error ( 'Tool name is required' ) ;
1201- }
1202- result = await this . executeTool ( toolName , toolArgs , userContext ) ;
1203- break ;
1204-
1205- case 'resources/read' :
1206- const resourceUri = params ?. uri ;
1207- if ( ! resourceUri ) {
1208- throw new Error ( 'Resource URI is required' ) ;
1209- }
1210- const resourceParams = params ?. parameters || { } ;
1211-
1212-
1213-
1214- result = await this . readResource ( resourceUri , userContext , resourceParams ) ;
1215- break ;
1216-
1217- case 'prompts/get' :
1218- const promptName = params ?. name ;
1219- const promptArgs = params ?. arguments || { } ;
1220- if ( ! promptName ) {
1221- throw new Error ( 'Prompt name is required' ) ;
1222- }
1223- result = await this . getPrompt ( promptName , promptArgs ) ;
1224- break ;
1225-
1226- default :
1227- throw new Error ( `Unknown method: ${ method } ` ) ;
1228- }
1229-
1230- res . json ( {
1231- jsonrpc : "2.0" ,
1232- result,
1233- id
1234- } ) ;
1235- } catch ( error ) {
1236- res . status ( 500 ) . json ( {
1237- jsonrpc : "2.0" ,
1238- error : { code : - 32603 , message : ( error as Error ) . message } ,
1239- id : req . body . id
1240- } ) ;
1241- }
1242- } ) ;
1243-
1244- this . app . get ( '/health' , ( req , res ) => {
1245- res . json ( {
1246- status : 'ok' ,
1247- specs : Array . from ( this . specs . keys ( ) ) ,
1248- tools : this . tools . length ,
1249- resources : this . resources . length ,
1250- prompts : this . prompts . size ,
1251- version : PACKAGE_VERSION
1252- } ) ;
1253- } ) ;
1254-
1255- this . app . get ( '/info' , ( req , res ) => {
1256- res . json ( {
1257- specs : Array . from ( this . specs . entries ( ) ) . map ( ( [ id , spec ] ) => ( {
1258- id,
1259- title : spec . info . title ,
1260- version : spec . info . version
1261- } ) ) ,
1262- tools : this . tools . map ( t => ( { name : t . name , description : t . description } ) ) ,
1263- resources : this . resources . map ( r => ( { uri : r . uri , name : r . name } ) ) ,
1264- prompts : Array . from ( this . prompts . keys ( ) )
1265- } ) ;
1266- } ) ;
1267-
1268- const server = this . app . listen ( serverPort , ( ) => {
1269- // HTTP server startup - use info level
1270- const address = server . address ( ) ;
1271- const host = typeof address === 'object' && address ?
1272- ( address . family === 'IPv6' ? `[${ address . address } ]` : address . address ) :
1273- 'localhost' ;
1274-
1275- this . telemetry . info ( `🚀 MCP OpenAPI Server running on port ${ serverPort } ` ) ;
1276- this . telemetry . info ( `📊 Health check: http://${ host } :${ serverPort } /health` ) ;
1277- this . telemetry . info ( `ℹ️ Server info: http://${ host } :${ serverPort } /info` ) ;
1278-
1279- this . telemetry . debug ( `📋 Loaded ${ this . specs . size } specs, ${ this . tools . length } tools, ${ this . resources . length } resources, ${ this . prompts . size } prompts` ) ;
1155+ const httpSetup = new HttpSetup ( {
1156+ app : this . app ,
1157+ specs : this . specs ,
1158+ tools : this . tools ,
1159+ resources : this . resources ,
1160+ prompts : this . prompts ,
1161+ telemetry : this . telemetry ,
1162+ executeTool : this . executeTool . bind ( this ) ,
1163+ readResource : this . readResource . bind ( this ) ,
1164+ getPrompt : this . getPrompt . bind ( this ) ,
1165+ extractUserContext : this . extractUserContext . bind ( this )
12801166 } ) ;
1167+
1168+ httpSetup . setupRoutes ( ) ;
1169+ await httpSetup . startServer ( serverPort ) ;
12811170 }
12821171}
0 commit comments