UNPKG

@softeria/ms-365-mcp-server

Version:

A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API

68 lines (54 loc) 3.06 kB
import fs from 'fs'; import path from 'path'; import { execSync } from 'child_process'; export function generateMcpTools(openApiSpec, outputDir) { try { console.log('Generating client code from OpenAPI spec using openapi-zod-client...'); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); console.log(`Created directory: ${outputDir}`); } const rootDir = path.resolve(outputDir, '../..'); const openapiDir = path.join(rootDir, 'openapi'); const openapiTrimmedFile = path.join(openapiDir, 'openapi-trimmed.yaml'); const clientFilePath = path.join(outputDir, 'client.ts'); execSync( `npx -y openapi-zod-client "${openapiTrimmedFile}" -o "${clientFilePath}" --with-description --strict-objects --additional-props-default-value=false`, { stdio: 'inherit', } ); console.log(`Generated client code at: ${clientFilePath}`); let clientCode = fs.readFileSync(clientFilePath, 'utf-8'); clientCode = clientCode.replace(/'@zodios\/core';/, "'./hack.js';"); clientCode = clientCode.replace(/\.strict\(\)/g, '.passthrough()'); console.log('Stripping unused errors arrays from endpoint definitions...'); // I didn't make up this crazy regex myself; you know who did. It seems works though. clientCode = clientCode.replace(/,?\s*errors:\s*\[[\s\S]*?],?(?=\s*})/g, ''); console.log('Decoding HTML entities in path patterns...'); // openapi-zod-client HTML-encodes special characters in path patterns // This breaks Microsoft Graph function-style APIs like range(address='A1:G10') clientCode = clientCode.replace(/=/g, '='); // Decode = sign clientCode = clientCode.replace(/'/g, "'"); // Decode single quote clientCode = clientCode.replace(/(/g, '('); // Decode left paren clientCode = clientCode.replace(/)/g, ')'); // Decode right paren clientCode = clientCode.replace(/:/g, ':'); // Decode colon console.log('Fixing function-style API paths with template literals...'); // After HTML decoding, paths like range(address=':address') have nested single quotes // which cause TypeScript syntax errors. Convert the path string from single quotes // to backticks (template literal) so single quotes can remain inside. // Match: path: '/...range(param=':value')...', // Replace with: path: `/...range(param=':value')...`, clientCode = clientCode.replace(/(path:\s*)'(\/[^']*\([^)]*=':[\w]+'\)[^']*)'/g, '$1`$2`'); // openapi-zod-client emits z.instanceof(File) for `format: binary` bodies; MCP // transports JSON so no caller produces File. Body marshaller decodes the string. clientCode = clientCode.replace( /z\.instanceof\(File\)/g, "z.string().describe('Base64-encoded file content. The server decodes it and PUTs the raw bytes to Microsoft Graph.')" ); fs.writeFileSync(clientFilePath, clientCode); return true; } catch (error) { throw new Error(`Error generating client code: ${error.message}`); } }