UNPKG

universal-documents-converter

Version:

Universal MCP Server for Multi-Rendering PDF Quality Assurance System with AI-powered optimization

474 lines (428 loc) โ€ข 13.5 kB
#!/usr/bin/env node /** * Universal Documents Converter MCP Server * Multi-Rendering PDF Quality Assurance System * * This MCP server provides advanced PDF generation capabilities with: * - Multi-rendering PDF generation (7 variants) * - AI-powered quality assessment using deepseek model * - Learning system with performance tracking * - Professional PDF output with quality reports */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { spawn } from 'child_process'; import { promises as fs } from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); class UniversalDocumentsConverter { constructor() { this.server = new Server( { name: 'universal-documents-converter', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.setupToolHandlers(); this.setupErrorHandling(); } setupErrorHandling() { this.server.onerror = (error) => { console.error('[MCP Error]', error); }; process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'convert_markdown_to_pdf', description: 'Convert markdown to professional PDF using enhanced conversion system', inputSchema: { type: 'object', properties: { input_file: { type: 'string', description: 'Path to the markdown file to convert', }, output_file: { type: 'string', description: 'Optional output PDF file path', }, document_type: { type: 'string', enum: ['academic', 'business', 'technical', 'documentation', 'general'], description: 'Type of document for optimized formatting', default: 'general', }, }, required: ['input_file'], }, }, { name: 'multi_render_pdf_optimal', description: 'Generate optimal PDF using multi-rendering quality assurance system with AI analysis', inputSchema: { type: 'object', properties: { input_file: { type: 'string', description: 'Path to the markdown file to convert', }, output_file: { type: 'string', description: 'Optional output PDF file path', }, document_type: { type: 'string', enum: ['academic', 'business', 'technical', 'documentation', 'general'], description: 'Type of document for optimized rendering', default: 'general', }, enable_learning: { type: 'boolean', description: 'Enable learning system to improve future conversions', default: true, }, }, required: ['input_file'], }, }, { name: 'test_multi_rendering_system', description: 'Run comprehensive tests on the multi-rendering PDF system', inputSchema: { type: 'object', properties: { test_type: { type: 'string', enum: ['quick', 'comprehensive', 'performance'], description: 'Type of test to run', default: 'quick', }, }, }, }, { name: 'get_quality_report', description: 'Get quality analysis report for a previously generated PDF', inputSchema: { type: 'object', properties: { document_name: { type: 'string', description: 'Name of the document to get quality report for', }, }, required: ['document_name'], }, }, { name: 'get_learning_insights', description: 'Get insights from the learning system about PDF generation patterns', inputSchema: { type: 'object', properties: { document_type: { type: 'string', enum: ['academic', 'business', 'technical', 'documentation', 'general', 'all'], description: 'Document type to get insights for', default: 'all', }, }, }, }, { name: 'configure_openrouter_keys', description: 'Configure OpenRouter API keys for AI-powered quality assessment', inputSchema: { type: 'object', properties: { api_keys: { type: 'array', items: { type: 'string' }, description: 'Array of OpenRouter API keys', }, model: { type: 'string', description: 'AI model to use for quality assessment', default: 'deepseek/deepseek-r1-0528:free', }, }, required: ['api_keys'], }, }, ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'convert_markdown_to_pdf': return await this.convertMarkdownToPdf(args); case 'multi_render_pdf_optimal': return await this.multiRenderPdfOptimal(args); case 'test_multi_rendering_system': return await this.testMultiRenderingSystem(args); case 'get_quality_report': return await this.getQualityReport(args); case 'get_learning_insights': return await this.getLearningInsights(args); case 'configure_openrouter_keys': return await this.configureOpenRouterKeys(args); default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${name}` ); } } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${error.message}` ); } }); } async convertMarkdownToPdf(args) { const { input_file, output_file, document_type = 'general' } = args; try { const pythonScript = path.join(__dirname, 'src', 'enhanced_universal_document_converter.py'); const result = await this.runPythonScript(pythonScript, [ '--input', input_file, '--output', output_file || '', '--type', document_type, '--mode', 'standard' ]); return { content: [ { type: 'text', text: `โœ… PDF conversion completed successfully!\n\n${result.stdout}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `โŒ PDF conversion failed: ${error.message}`, }, ], isError: true, }; } } async multiRenderPdfOptimal(args) { const { input_file, output_file, document_type = 'general', enable_learning = true } = args; try { const pythonScript = path.join(__dirname, 'src', 'multi_rendering_pdf_system.py'); const result = await this.runPythonScript(pythonScript, [ '--input', input_file, '--output', output_file || '', '--type', document_type, '--learning', enable_learning.toString(), '--mode', 'multi-render' ]); return { content: [ { type: 'text', text: `๐ŸŽฏ Multi-rendering PDF generation completed!\n\n${result.stdout}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `โŒ Multi-rendering failed: ${error.message}`, }, ], isError: true, }; } } async testMultiRenderingSystem(args) { const { test_type = 'quick' } = args; try { const pythonScript = path.join(__dirname, 'src', 'test_multi_rendering_system.py'); const result = await this.runPythonScript(pythonScript, [ '--test-type', test_type ]); return { content: [ { type: 'text', text: `๐Ÿงช Multi-rendering system tests completed!\n\n${result.stdout}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `โŒ Testing failed: ${error.message}`, }, ], isError: true, }; } } async getQualityReport(args) { const { document_name } = args; try { const reportsDir = path.join(__dirname, 'quality_reports'); const files = await fs.readdir(reportsDir); const reportFile = files.find(file => file.includes(document_name) && file.endsWith('.md') ); if (!reportFile) { return { content: [ { type: 'text', text: `โŒ No quality report found for document: ${document_name}`, }, ], isError: true, }; } const reportPath = path.join(reportsDir, reportFile); const reportContent = await fs.readFile(reportPath, 'utf-8'); return { content: [ { type: 'text', text: `๐Ÿ“‹ Quality Report for ${document_name}:\n\n${reportContent}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `โŒ Failed to get quality report: ${error.message}`, }, ], isError: true, }; } } async getLearningInsights(args) { const { document_type = 'all' } = args; try { const pythonScript = path.join(__dirname, 'src', 'multi_rendering_pdf_system.py'); const result = await this.runPythonScript(pythonScript, [ '--mode', 'learning-insights', '--type', document_type ]); return { content: [ { type: 'text', text: `๐Ÿ“š Learning System Insights:\n\n${result.stdout}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `โŒ Failed to get learning insights: ${error.message}`, }, ], isError: true, }; } } async configureOpenRouterKeys(args) { const { api_keys, model = 'deepseek/deepseek-r1-0528:free' } = args; try { const configPath = path.join(__dirname, 'config', 'openrouter_keys.json'); const config = { api_keys, model, updated_at: new Date().toISOString(), }; await fs.writeFile(configPath, JSON.stringify(config, null, 2)); return { content: [ { type: 'text', text: `โœ… OpenRouter configuration updated successfully!\n\nConfigured ${api_keys.length} API keys for model: ${model}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `โŒ Failed to configure OpenRouter keys: ${error.message}`, }, ], isError: true, }; } } async runPythonScript(scriptPath, args = []) { return new Promise((resolve, reject) => { const python = spawn('python', [scriptPath, ...args], { cwd: __dirname, stdio: ['pipe', 'pipe', 'pipe'], }); let stdout = ''; let stderr = ''; python.stdout.on('data', (data) => { stdout += data.toString(); }); python.stderr.on('data', (data) => { stderr += data.toString(); }); python.on('close', (code) => { if (code === 0) { resolve({ stdout, stderr }); } else { reject(new Error(`Python script failed with code ${code}: ${stderr}`)); } }); python.on('error', (error) => { reject(new Error(`Failed to start Python script: ${error.message}`)); }); }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Universal Documents Converter MCP server running on stdio'); } } const server = new UniversalDocumentsConverter(); server.run().catch(console.error);