UNPKG

openapi-mcp-generator

Version:

Generates MCP server code from OpenAPI specifications

139 lines 4.93 kB
import { sanitizeForTemplate } from './helpers.js'; /** * Generates the tool definition map code * * @param tools List of tool definitions * @param securitySchemes Security schemes from OpenAPI spec * @returns Generated code for the tool definition map */ export function generateToolDefinitionMap(tools, securitySchemes) { if (tools.length === 0) return ''; return tools .map((tool) => { // Safely stringify complex objects let schemaString; try { schemaString = JSON.stringify(tool.inputSchema); } catch (e) { schemaString = '{}'; console.warn(`Failed to stringify schema for tool ${tool.name}: ${e}`); } let execParamsString; try { execParamsString = JSON.stringify(tool.executionParameters); } catch (e) { execParamsString = '[]'; console.warn(`Failed to stringify execution parameters for tool ${tool.name}: ${e}`); } let securityReqsString; try { securityReqsString = JSON.stringify(tool.securityRequirements); } catch (e) { securityReqsString = '[]'; console.warn(`Failed to stringify security requirements for tool ${tool.name}: ${e}`); } // Sanitize description for template literal const escapedDescription = sanitizeForTemplate(tool.description); // Build the tool definition entry return ` ["${tool.name}", { name: "${tool.name}", description: \`${escapedDescription}\`, inputSchema: ${schemaString}, method: "${tool.method}", pathTemplate: "${tool.pathTemplate}", executionParameters: ${execParamsString}, requestBodyContentType: ${tool.requestBodyContentType ? `"${tool.requestBodyContentType}"` : 'undefined'}, securityRequirements: ${securityReqsString} }],`; }) .join(''); } /** * Generates the list tools handler code * * @returns Generated code for the list tools handler */ export function generateListToolsHandler() { return ` server.setRequestHandler(ListToolsRequestSchema, async () => { const toolsForClient: Tool[] = Array.from(toolDefinitionMap.values()).map(def => ({ name: def.name, description: def.description, inputSchema: def.inputSchema })); return { tools: toolsForClient }; }); `; } /** * Generates the call tool handler code * * @returns Generated code for the call tool handler */ export function generateCallToolHandler() { return ` server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest): Promise<CallToolResult> => { const { name: toolName, arguments: toolArgs } = request.params; const toolDefinition = toolDefinitionMap.get(toolName); if (!toolDefinition) { console.error(\`Error: Unknown tool requested: \${toolName}\`); return { content: [{ type: "text", text: \`Error: Unknown tool requested: \${toolName}\` }] }; } return await executeApiTool(toolName, toolDefinition, toolArgs ?? {}, securitySchemes); }); `; } /** * Convert a string to title case * * @param str String to convert * @returns Title case string */ export function titleCase(str) { // Converts snake_case, kebab-case, or path/parts to TitleCase return str .toLowerCase() .replace(/[-_\/](.)/g, (_, char) => char.toUpperCase()) // Handle separators .replace(/^{/, '') // Remove leading { from path params .replace(/}$/, '') // Remove trailing } from path params .replace(/^./, (char) => char.toUpperCase()); // Capitalize first letter } /** * Generates an operation ID from method and path * * @param method HTTP method * @param path API path * @returns Generated operation ID */ export function generateOperationId(method, path) { // Generator: get /users/{userId}/posts -> GetUsersPostsByUserId const parts = path.split('/').filter((p) => p); // Split and remove empty parts let name = method.toLowerCase(); // Start with method name parts.forEach((part, index) => { if (part.startsWith('{') && part.endsWith('}')) { // Append 'By' + ParamName only for the *last* path parameter segment if (index === parts.length - 1) { name += 'By' + titleCase(part); } // Potentially include non-terminal params differently if needed, e.g.: // else { name += 'With' + titleCase(part); } } else { // Append the static path part in TitleCase name += titleCase(part); } }); // Simple fallback if name is just the method (e.g., GET /) if (name === method.toLowerCase()) { name += 'Root'; } // Ensure first letter is uppercase after potential lowercase method start name = name.charAt(0).toUpperCase() + name.slice(1); return name; } //# sourceMappingURL=code-gen.js.map