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
JavaScript
/**
* 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);