Skip to main content
Glama
professional-report-handler.ts15.1 kB
/** * Professional Report Generator * Creates comprehensive reports in multiple formats (PPT, Word, HTML, PDF) */ import { Client } from '@microsoft/microsoft-graph-client'; import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import { ProfessionalReportArgs, DataQuery, PowerPointSlide, WordSection, HTMLSection, ChartData, TableData } from '../types/document-generation-types.js'; import { handlePowerPointPresentations } from './powerpoint-handler.js'; import { handleWordDocuments } from './word-document-handler.js'; import { handleHTMLReports } from './html-report-handler.js'; /** * Generate professional reports in multiple formats */ export async function handleProfessionalReports( args: ProfessionalReportArgs, graphClient: Client ): Promise<string> { try { // Step 1: Collect data from specified sources const collectedData = await collectReportData(args.dataQueries || [], graphClient); // Step 2: Generate report content based on report type const reportContent = generateReportContent(args, collectedData); // Step 3: Create documents in requested formats const createdFiles = []; for (const format of args.outputFormats) { let result; switch (format) { case 'pptx': result = await createPowerPointReport(args, reportContent, graphClient); createdFiles.push({ format: 'pptx', ...result }); break; case 'docx': result = await createWordReport(args, reportContent, graphClient); createdFiles.push({ format: 'docx', ...result }); break; case 'html': result = await createHTMLReport(args, reportContent, graphClient); createdFiles.push({ format: 'html', ...result }); break; case 'pdf': // PDF is generated by first creating Word and then converting result = await createPDFReport(args, reportContent, graphClient); createdFiles.push({ format: 'pdf', ...result }); break; } } return JSON.stringify({ success: true, reportType: args.reportType, title: args.title, filesCreated: createdFiles.length, files: createdFiles, dataSourcesQueried: args.dataQueries?.length || 0, message: `Professional report "${args.title}" generated successfully in ${createdFiles.length} format(s)` }, null, 2); } catch (error) { if (error instanceof McpError) throw error; throw new McpError( ErrorCode.InternalError, `Professional report generation failed: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } /** * Collect data from Microsoft Graph API sources */ async function collectReportData( queries: DataQuery[], graphClient: Client ): Promise<Map<string, any>> { const dataMap = new Map<string, any>(); for (const query of queries) { try { let response = await graphClient.api(query.endpoint).get(); // Apply filter if specified if (query.filter) { response = await graphClient.api(query.endpoint).filter(query.filter).get(); } // Apply select if specified if (query.select && query.select.length > 0) { response = await graphClient.api(query.endpoint).select(query.select).get(); } // Apply transformation if specified let transformedData = response.value || response; if (query.transform) { transformedData = applyTransformation(transformedData, query.transform); } dataMap.set(query.label, transformedData); } catch (error) { console.error(`Failed to query ${query.endpoint}:`, error); dataMap.set(query.label, { error: 'Failed to fetch data' }); } } return dataMap; } /** * Apply data transformation */ function applyTransformation(data: any[], transform: string): any { switch (transform) { case 'count': return { count: Array.isArray(data) ? data.length : 0 }; case 'group-by': // Group by first property if (!Array.isArray(data) || data.length === 0) return {}; const groupKey = Object.keys(data[0])[0]; return data.reduce((acc: any, item) => { const key = item[groupKey]; acc[key] = (acc[key] || 0) + 1; return acc; }, {}); case 'aggregate': // Sum numeric values if (!Array.isArray(data)) return data; return data.reduce((acc, item) => { Object.keys(item).forEach(key => { if (typeof item[key] === 'number') { acc[key] = (acc[key] || 0) + item[key]; } }); return acc; }, {}); case 'trend': // Create time-series data (simplified) return Array.isArray(data) ? { dataPoints: data.length, trend: 'stable' } : data; default: return data; } } /** * Generate report content structure */ function generateReportContent( args: ProfessionalReportArgs, collectedData: Map<string, any> ): ReportContent { const content: ReportContent = { title: args.title, description: args.description || '', sections: [], charts: [], tables: [], summary: '' }; // Generate executive summary if requested if (args.includeSummary) { content.summary = generateExecutiveSummary(args, collectedData); } // Generate sections based on report type switch (args.reportType) { case 'security-analysis': content.sections = generateSecurityAnalysisSections(collectedData); break; case 'compliance-audit': content.sections = generateComplianceAuditSections(collectedData); break; case 'user-activity': content.sections = generateUserActivitySections(collectedData); break; case 'device-health': content.sections = generateDeviceHealthSections(collectedData); break; case 'custom': content.sections = generateCustomSections(collectedData); break; } // Extract charts and tables if requested if (args.includeCharts) { content.charts = extractChartsFromData(collectedData); } if (args.includeTables) { content.tables = extractTablesFromData(collectedData); } return content; } /** * Generate executive summary */ function generateExecutiveSummary( args: ProfessionalReportArgs, data: Map<string, any> ): string { let summary = `This ${args.reportType} report provides a comprehensive analysis of `; summary += `the current state based on data collected from ${data.size} sources. `; summary += `Key findings and recommendations are detailed in the following sections.`; return summary; } /** * Generate sections for security analysis report */ function generateSecurityAnalysisSections(data: Map<string, any>): ReportSection[] { const sections: ReportSection[] = [ { title: 'Security Overview', content: 'Analysis of current security posture and identified risks.', type: 'overview' }, { title: 'Threat Detection', content: 'Summary of detected threats and security incidents.', type: 'analysis' }, { title: 'Recommendations', content: 'Recommended actions to improve security.', type: 'recommendations' } ]; return sections; } /** * Generate sections for compliance audit report */ function generateComplianceAuditSections(data: Map<string, any>): ReportSection[] { return [ { title: 'Compliance Status', content: 'Current compliance state', type: 'overview' }, { title: 'Policy Violations', content: 'Identified policy violations', type: 'findings' }, { title: 'Remediation Plan', content: 'Steps to achieve compliance', type: 'action-plan' } ]; } /** * Generate sections for user activity report */ function generateUserActivitySections(data: Map<string, any>): ReportSection[] { return [ { title: 'User Activity Summary', content: 'Overview of user activities', type: 'overview' }, { title: 'Top Users', content: 'Most active users', type: 'analysis' }, { title: 'Activity Trends', content: 'Usage trends over time', type: 'trends' } ]; } /** * Generate sections for device health report */ function generateDeviceHealthSections(data: Map<string, any>): ReportSection[] { return [ { title: 'Device Inventory', content: 'Current device inventory', type: 'overview' }, { title: 'Health Status', content: 'Device health metrics', type: 'analysis' }, { title: 'Issues & Recommendations', content: 'Identified issues', type: 'recommendations' } ]; } /** * Generate custom sections */ function generateCustomSections(data: Map<string, any>): ReportSection[] { const sections: ReportSection[] = []; data.forEach((value, key) => { sections.push({ title: key, content: JSON.stringify(value, null, 2), type: 'data' }); }); return sections; } /** * Extract charts from collected data */ function extractChartsFromData(data: Map<string, any>): ChartData[] { const charts: ChartData[] = []; data.forEach((value, label) => { if (typeof value === 'object' && value !== null) { // Try to create a chart from the data const keys = Object.keys(value); if (keys.length > 0 && typeof value[keys[0]] === 'number') { charts.push({ type: 'bar', title: label, categories: keys, series: [{ name: label, values: keys.map(k => value[k]) }] }); } } }); return charts; } /** * Extract tables from collected data */ function extractTablesFromData(data: Map<string, any>): TableData[] { const tables: TableData[] = []; data.forEach((value, label) => { if (Array.isArray(value) && value.length > 0) { const firstItem = value[0]; if (typeof firstItem === 'object') { const headers = Object.keys(firstItem); const rows = value.map(item => headers.map(h => String(item[h] || ''))); tables.push({ headers: headers, rows: rows }); } } }); return tables; } /** * Create PowerPoint report */ async function createPowerPointReport( args: ProfessionalReportArgs, content: ReportContent, graphClient: Client ): Promise<any> { const slides: PowerPointSlide[] = [ // Title slide { type: 'title', title: content.title, subtitle: content.description }, // Summary slide ...(content.summary ? [{ type: 'title-content' as const, title: 'Executive Summary', content: [content.summary] }] : []), // Content slides ...content.sections.map(section => ({ type: 'title-content' as const, title: section.title, content: [section.content] })), // Chart slides ...content.charts.map(chart => ({ type: 'chart' as const, title: chart.title, chartData: chart })) ]; const fileName = `${args.fileNamePrefix || args.title}_${Date.now()}.pptx`; const result = await handlePowerPointPresentations( { action: 'create', fileName: fileName, driveId: args.driveId, folderId: args.folderId, slides: slides, template: { theme: 'professional', companyName: args.template?.companyName, companyLogo: args.template?.companyLogo } }, graphClient ); return JSON.parse(result); } /** * Create Word report */ async function createWordReport( args: ProfessionalReportArgs, content: ReportContent, graphClient: Client ): Promise<any> { const sections: WordSection[] = [ // Title { type: 'heading1' as const, content: content.title }, ...(content.description ? [{ type: 'paragraph' as const, content: content.description }] : []), // Summary ...(content.summary ? [ { type: 'heading2' as const, content: 'Executive Summary' }, { type: 'paragraph' as const, content: content.summary } ] : []), // Content sections ...content.sections.flatMap(section => [ { type: 'heading2' as const, content: section.title }, { type: 'paragraph' as const, content: section.content } ]), // Tables ...content.tables.map(table => ({ type: 'table' as const, tableData: table })) ]; const fileName = `${args.fileNamePrefix || args.title}_${Date.now()}.docx`; const result = await handleWordDocuments( { action: 'create', fileName: fileName, driveId: args.driveId, folderId: args.folderId, sections: sections, template: { style: 'report', header: args.template?.companyName, footer: `${content.title} - Page `, pageNumbers: true, tableOfContents: true } }, graphClient ); return JSON.parse(result); } /** * Create HTML report */ async function createHTMLReport( args: ProfessionalReportArgs, content: ReportContent, graphClient: Client ): Promise<any> { const htmlSections: HTMLSection[] = [ // Summary ...(content.summary ? [{ type: 'alert' as const, alertType: 'info' as const, heading: 'Executive Summary', content: content.summary }] : []), // Content sections ...content.sections.map(section => ({ type: 'heading' as const, level: 2 as const, heading: section.title, content: section.content })), // Charts ...content.charts.map(chart => ({ type: 'chart' as const, chartData: chart })), // Tables ...content.tables.map(table => ({ type: 'table' as const, tableData: table })) ]; const fileName = `${args.fileNamePrefix || args.title}_${Date.now()}.html`; const result = await handleHTMLReports( { action: 'create', fileName: fileName, driveId: args.driveId, folderId: args.folderId, template: { title: content.title, description: content.description, theme: 'modern', companyName: args.template?.companyName, companyLogo: args.template?.companyLogo, includeBootstrap: true, includeChartJS: true }, sections: htmlSections, includeCharts: true }, graphClient ); return JSON.parse(result); } /** * Create PDF report (by converting Word document) */ async function createPDFReport( args: ProfessionalReportArgs, content: ReportContent, graphClient: Client ): Promise<any> { // First create Word document const wordResult = await createWordReport(args, content, graphClient); // Then export to PDF const pdfFileName = `${args.fileNamePrefix || args.title}_${Date.now()}.pdf`; // Note: Actual PDF conversion would use Word's export API return { ...wordResult, format: 'pdf', fileName: pdfFileName, note: 'PDF generated from Word document via export API' }; } // Type definitions for report content interface ReportContent { title: string; description: string; sections: ReportSection[]; charts: ChartData[]; tables: TableData[]; summary: string; } interface ReportSection { title: string; content: string; type: string; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DynamicEndpoints/m365-core-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server