Skip to main content
Glama

Dynamic Excel MCP Server

by nam090320251
index.tsโ€ข8.44 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { config } from 'dotenv'; import { SchemaValidator } from './validators/schema-validator.js'; import { BasicGenerator } from './generators/basic-generator.js'; import { ReportGenerator } from './generators/report-generator.js'; import { S3Storage } from './storage/s3-storage.js'; import { LocalStorage } from './storage/local-storage.js'; import { logger } from './utils/logger.js'; import { ExcelConfig } from './types/schema.js'; import { HttpTransportServer } from './transports/http-server.js'; // Load environment variables config(); // Initialize storage const storageType = process.env.STORAGE_TYPE || 'local'; const storage = storageType === 's3' ? new S3Storage() : new LocalStorage(); // Generator selection logic function selectGenerator(config: ExcelConfig): BasicGenerator | ReportGenerator { // Check if any sheet uses report layout const hasReportLayout = config.sheets.some(sheet => sheet.layout === 'report'); if (hasReportLayout) { return new ReportGenerator(); } return new BasicGenerator(); } // Factory function to create and configure MCP server function createMcpServer(): Server { // Create MCP server const server = new Server( { name: 'excel-generator', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'generate_excel', description: `Generate an Excel file from a structured JSON schema. Use this tool when the user wants to: - Create an Excel file - Export data to Excel - Generate a report/spreadsheet - Download data as .xlsx file The tool accepts a JSON schema describing the structure, data, and formatting of the Excel file. Supported features: - Multiple sheets - Custom column widths and formats - Cell styling (fonts, colors, borders, alignment) - Data validation - Conditional formatting - Formulas and totals - Charts and images - Page setup and printing options - Freeze panes, auto-filter - Merged cells - Grouped rows/columns - Sheet protection Layout types: - table: Simple data table (default) - report: Formatted report with headers and styling - form: Form-style layout - dashboard: Dashboard with charts - calendar: Calendar view`, inputSchema: { type: 'object', properties: { file_name: { type: 'string', description: 'Name of the Excel file (e.g., "report.xlsx")', }, sheets: { type: 'array', description: 'Array of sheet configurations', items: { type: 'object', properties: { name: { type: 'string', description: 'Sheet name', }, layout: { type: 'string', enum: ['table', 'report', 'form', 'dashboard', 'calendar'], description: 'Layout type for the sheet', }, columns: { type: 'array', description: 'Column definitions', }, data: { type: 'array', description: 'Array of data rows', }, formatting: { type: 'object', description: 'Formatting options', }, charts: { type: 'array', description: 'Charts to add to the sheet', }, }, required: ['name', 'columns', 'data'], }, }, metadata: { type: 'object', description: 'Workbook metadata', }, options: { type: 'object', description: 'Output options', }, }, required: ['sheets'], }, }, ], }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name !== 'generate_excel') { throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } try { logger.info('Received generate_excel request'); // Validate schema const validation = SchemaValidator.validate(request.params.arguments); if (!validation.success) { throw new McpError( ErrorCode.InvalidParams, `Schema validation failed: ${validation.errors.join(', ')}` ); } const config: ExcelConfig = validation.data; // Select appropriate generator const generator = selectGenerator(config); // Generate Excel file logger.info(`Generating Excel file: ${config.file_name}`); const buffer = await generator.generate(config); logger.info(`Excel file generated, size: ${buffer.length} bytes`); // Upload to storage const fileName = config.file_name || `report_${Date.now()}.xlsx`; const key = await storage.upload(buffer, fileName); // Get download URL let downloadUrl: string; if (storage instanceof S3Storage) { const expiresIn = parseInt(process.env.PRESIGNED_URL_EXPIRY || '3600'); downloadUrl = await storage.getPresignedUrl(key, expiresIn); } else { downloadUrl = await (storage as LocalStorage).getFileUrl(key); } logger.info('Excel file ready for download'); // Return result return { content: [ { type: 'text', text: JSON.stringify({ success: true, download_url: downloadUrl, file_name: fileName, file_size: buffer.length, sheets_count: config.sheets.length, message: 'Excel file generated successfully', expires_in: storageType === 's3' ? parseInt(process.env.PRESIGNED_URL_EXPIRY || '3600') : null, }, null, 2), }, ], }; } catch (error) { logger.error('Error generating Excel:', error); if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Failed to generate Excel file: ${error instanceof Error ? error.message : 'Unknown error'}` ); } }); return server; } // Start server with appropriate transport async function main() { const transportMode = process.env.TRANSPORT_MODE || 'stdio'; if (transportMode === 'http') { // HTTP/SSE mode for remote access const port = parseInt(process.env.HTTP_PORT || '3000'); const host = process.env.HTTP_HOST || '0.0.0.0'; const allowedOrigins = process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS.split(',').map(o => o.trim()) : ['*']; const apiKey = process.env.API_KEY; const httpServer = new HttpTransportServer({ port, host, allowedOrigins, apiKey, }); // Setup MCP endpoint with factory function // Each SSE connection will get its own server instance httpServer.setupMcpEndpoint({ createServer: createMcpServer, }); // Start HTTP server await httpServer.start(); logger.info('Excel MCP Server started in HTTP/SSE mode'); logger.info(`Storage type: ${storageType}`); logger.info(`Allowed origins: ${allowedOrigins.join(', ')}`); if (apiKey) { logger.info('API key authentication enabled'); } } else { // Stdio mode for local MCP client const server = createMcpServer(); const transport = new StdioServerTransport(); await server.connect(transport); logger.info('Excel MCP Server started in stdio mode'); logger.info(`Storage type: ${storageType}`); } } main().catch((error) => { logger.error('Fatal error in main():', error); process.exit(1); });

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/nam090320251/dynamic-excel-mcp-server'

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