UNPKG

uns-mcp-server

Version:

Pure JavaScript MCP server for Unstructured.io - No Python required!

282 lines (240 loc) • 7.72 kB
#!/usr/bin/env node /** * Unstructured.io MCP Server - NPX Executable * Provides document processing capabilities via MCP protocol */ const { spawn } = require('child_process'); const path = require('path'); const fs = require('fs'); const which = require('which'); const yargs = require('yargs/yargs'); const { hideBin } = require('yargs/helpers'); // Simple console colors without external dependencies const chalk = { blue: (text) => `\x1b[34m${text}\x1b[0m`, green: (text) => `\x1b[32m${text}\x1b[0m`, yellow: (text) => `\x1b[33m${text}\x1b[0m`, red: (text) => `\x1b[31m${text}\x1b[0m`, gray: (text) => `\x1b[90m${text}\x1b[0m` }; // Parse command line arguments const argv = yargs(hideBin(process.argv)) .option('mode', { alias: 'm', description: 'Server mode (stdio or sse)', type: 'string', default: 'stdio' }) .option('port', { alias: 'p', description: 'Port for SSE mode', type: 'number', default: 8080 }) .option('api-key', { alias: 'k', description: 'Unstructured API key', type: 'string' }) .option('config', { alias: 'c', description: 'Path to config file', type: 'string' }) .option('debug', { alias: 'd', description: 'Enable debug mode', type: 'boolean', default: false }) .help() .alias('help', 'h') .argv; class UnstructuredMCPServer { constructor(options) { this.options = options; this.pythonCommand = null; this.serverProcess = null; } async findPython() { const pythonCommands = ['python3', 'python', 'python3.12', 'python3.11']; for (const cmd of pythonCommands) { try { const pythonPath = await which(cmd); // Verify it's Python 3.8+ const version = await this.getPythonVersion(pythonPath); if (version && this.isPythonVersionValid(version)) { return pythonPath; } } catch (e) { // Continue to next option } } throw new Error('Python 3.8+ not found. Please install Python.'); } getPythonVersion(pythonPath) { return new Promise((resolve, reject) => { const proc = spawn(pythonPath, ['--version']); let version = ''; proc.stdout.on('data', (data) => { version += data.toString(); }); proc.stderr.on('data', (data) => { version += data.toString(); }); proc.on('close', (code) => { if (code === 0) { const match = version.match(/Python (\d+)\.(\d+)\.(\d+)/); if (match) { resolve({ major: parseInt(match[1]), minor: parseInt(match[2]), patch: parseInt(match[3]) }); } else { reject(new Error('Could not parse Python version')); } } else { reject(new Error('Failed to get Python version')); } }); }); } isPythonVersionValid(version) { return version.major >= 3 && version.minor >= 8; } async checkUnsMcpInstalled() { return new Promise((resolve) => { const proc = spawn(this.pythonCommand, ['-c', 'import uns_mcp']); proc.on('close', (code) => { resolve(code === 0); }); }); } async installUnsMcp() { console.log(chalk.yellow('Installing uns_mcp Python package...')); // Try different installation methods const installCommands = [ ['-m', 'pip', 'install', '--user', 'uns_mcp'], ['-m', 'pip', 'install', '--user', '--break-system-packages', 'uns_mcp'], ['-m', 'pip', 'install', 'uns_mcp'] ]; for (const args of installCommands) { try { const result = await new Promise((resolve, reject) => { const proc = spawn(this.pythonCommand, args, { stdio: 'pipe' }); let stderr = ''; proc.stderr.on('data', (data) => { stderr += data.toString(); }); proc.on('close', (code) => { if (code === 0) { resolve(true); } else { reject(new Error(stderr)); } }); }); if (result) { console.log(chalk.green('āœ“ uns_mcp installed successfully')); return; } } catch (e) { // Try next method continue; } } throw new Error('Failed to install uns_mcp. Please install manually: pip install uns_mcp'); } setupEnvironment() { // Set up environment variables const env = { ...process.env }; // Load from .env file if it exists const envPath = path.join(process.cwd(), '.env'); if (fs.existsSync(envPath)) { require('dotenv').config({ path: envPath }); } // Override with command line options if (this.options.apiKey) { env.UNSTRUCTURED_API_KEY = this.options.apiKey; } if (this.options.debug) { env.DEBUG_API_REQUESTS = 'true'; env.LOG_LEVEL = 'DEBUG'; } // Check for required API key if (!env.UNSTRUCTURED_API_KEY) { console.warn(chalk.yellow('⚠ Warning: UNSTRUCTURED_API_KEY not set')); console.log(chalk.gray('Get your API key at: https://unstructured.io')); } return env; } async start() { try { console.log(chalk.blue('šŸš€ Starting Unstructured MCP Server...')); // Find Python this.pythonCommand = await this.findPython(); console.log(chalk.green(`āœ“ Found Python: ${this.pythonCommand}`)); // Check if uns_mcp is installed const isInstalled = await this.checkUnsMcpInstalled(); if (!isInstalled) { await this.installUnsMcp(); } // Set up environment const env = this.setupEnvironment(); // Prepare command arguments const args = ['-m', 'uns_mcp']; if (this.options.mode === 'sse') { args.push('--port', this.options.port.toString()); console.log(chalk.blue(`šŸ“” Server will run in SSE mode on port ${this.options.port}`)); } else { // STDIO is the default mode console.log(chalk.blue('šŸ“” Server will run in STDIO mode')); } // Start the Python MCP server this.serverProcess = spawn(this.pythonCommand, args, { env, stdio: this.options.mode === 'stdio' ? 'inherit' : ['ignore', 'inherit', 'inherit'] }); this.serverProcess.on('error', (err) => { console.error(chalk.red('āŒ Server error:'), err); }); this.serverProcess.on('close', (code) => { if (code !== 0) { console.log(chalk.yellow(`Server exited with code ${code}`)); } else { console.log(chalk.green('āœ“ Server stopped gracefully')); } }); // Handle graceful shutdown process.on('SIGINT', () => this.stop()); process.on('SIGTERM', () => this.stop()); if (this.options.mode === 'sse') { console.log(chalk.green(`āœ“ Server running at http://localhost:${this.options.port}/sse`)); } else { console.log(chalk.green('āœ“ Server running in STDIO mode')); } } catch (error) { console.error(chalk.red('āŒ Failed to start server:'), error.message); process.exit(1); } } stop() { console.log(chalk.yellow('\nā¹ Stopping server...')); if (this.serverProcess) { this.serverProcess.kill('SIGTERM'); } process.exit(0); } } // Main execution async function main() { const server = new UnstructuredMCPServer(argv); await server.start(); } main().catch(error => { console.error(chalk.red('Fatal error:'), error); process.exit(1); });