Skip to main content
Glama

MCP Manual Generator

by haixyeh
server.js13.2 kB
#!/usr/bin/env node const { Server } = require('@modelcontextprotocol/sdk/server/index.js'); const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js'); const fs = require('fs-extra'); const path = require('path'); const ScreenshotTool = require('./tools/screenshot'); const ManualGenerator = require('./tools/manual'); const ConfigLoader = require('./utils/config-loader'); const logger = require('./utils/logger'); class MCPManualServer { constructor() { this.server = new Server( { name: 'mcp-manual-generator', version: '1.0.0', description: 'Automated screenshot capture and user manual generation MCP server' }, { capabilities: { tools: {}, resources: {} } } ); this.screenshotTool = null; this.configLoader = new ConfigLoader(); this.setupHandlers(); } setupHandlers() { this.server.setRequestHandler('tools/list', async () => { return { tools: [ { name: 'capture_screenshots', description: 'Capture screenshots based on configuration', inputSchema: { type: 'object', properties: { configPath: { type: 'string', description: 'Path to configuration file' }, outputDir: { type: 'string', description: 'Output directory for screenshots' }, screenshots: { type: 'array', description: 'Array of screenshot configurations (overrides config file)', items: { type: 'object', properties: { name: { type: 'string' }, url: { type: 'string' }, description: { type: 'string' }, waitFor: { type: 'string' }, folder: { type: 'string' }, requireAuth: { type: 'boolean' } }, required: ['name', 'url'] } }, options: { type: 'object', description: 'Runtime options', properties: { headless: { type: 'boolean' }, viewport: { type: 'object', properties: { width: { type: 'number' }, height: { type: 'number' } } } } } } } }, { name: 'generate_manual', description: 'Generate user manual from screenshots', inputSchema: { type: 'object', properties: { screenshotDir: { type: 'string', description: 'Directory containing screenshots' }, template: { type: 'string', description: 'Template name or path' }, outputPath: { type: 'string', description: 'Output file path for the manual' }, metadata: { type: 'object', description: 'Additional metadata for manual generation', properties: { projectName: { type: 'string' }, version: { type: 'string' }, author: { type: 'string' } } } }, required: ['screenshotDir'] } }, { name: 'load_config', description: 'Load and validate configuration file', inputSchema: { type: 'object', properties: { configPath: { type: 'string', description: 'Path to configuration file' } }, required: ['configPath'] } }, { name: 'capture_single', description: 'Capture a single screenshot', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL to capture' }, outputPath: { type: 'string', description: 'Output file path' }, waitFor: { type: 'string', description: 'Selector to wait for' }, fullPage: { type: 'boolean', description: 'Capture full page', default: true } }, required: ['url', 'outputPath'] } } ] }; }); this.server.setRequestHandler('tools/execute', async (request) => { const { name, arguments: args } = request.params; try { logger.info(`Executing tool: ${name}`); switch (name) { case 'capture_screenshots': return await this.handleCaptureScreenshots(args); case 'generate_manual': return await this.handleGenerateManual(args); case 'load_config': return await this.handleLoadConfig(args); case 'capture_single': return await this.handleCaptureSingle(args); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { logger.error(`Tool execution failed: ${name}`, error); return { content: [ { type: 'text', text: `Error: ${error.message}` } ], isError: true }; } }); } async handleCaptureScreenshots(args) { let config = {}; // Load configuration if path provided if (args.configPath) { config = await this.configLoader.load(args.configPath); } // Override with runtime arguments if (args.outputDir) { config.outputDir = args.outputDir; } if (args.screenshots) { config.screenshots = args.screenshots; } if (args.options) { config.browser = { ...config.browser, ...args.options }; } // Validate we have screenshots to capture if (!config.screenshots || config.screenshots.length === 0) { throw new Error('No screenshots configured'); } // Initialize screenshot tool this.screenshotTool = new ScreenshotTool(config); await this.screenshotTool.initialize(); try { // Capture all screenshots const results = await this.screenshotTool.captureAll(config.screenshots); // Generate report const report = await this.screenshotTool.generateReport(); return { content: [ { type: 'text', text: `Screenshots captured successfully!\n\nSummary:\n- Total: ${report.summary.total}\n- Successful: ${report.summary.successful}\n- Failed: ${report.summary.failed}\n\nReport saved to: ${config.outputDir}/screenshot_report.json` } ] }; } finally { await this.screenshotTool.cleanup(); } } async handleGenerateManual(args) { const generator = new ManualGenerator({ screenshotDir: args.screenshotDir, template: args.template, outputPath: args.outputPath, metadata: args.metadata }); const result = await generator.generate(); return { content: [ { type: 'text', text: `Manual generated successfully!\n\nOutput: ${result.outputPath}\nScreenshots: ${result.screenshotCount}\nSize: ${result.size}` } ] }; } async handleLoadConfig(args) { const config = await this.configLoader.load(args.configPath); const validation = await this.configLoader.validate(config); return { content: [ { type: 'text', text: `Configuration loaded successfully!\n\nProject: ${config.project?.name || 'Unknown'}\nScreenshots: ${config.screenshots?.length || 0}\nValidation: ${validation.valid ? 'Passed' : 'Failed'}\n${validation.errors ? `Errors: ${validation.errors.join(', ')}` : ''}` } ] }; } async handleCaptureSingle(args) { const tool = new ScreenshotTool({ browser: { headless: true } }); await tool.initialize(); try { const screenshotConfig = { name: path.basename(args.outputPath, path.extname(args.outputPath)), url: args.url, waitFor: args.waitFor, fullPage: args.fullPage }; await tool.captureScreenshot(screenshotConfig); // Ensure output directory exists await fs.ensureDir(path.dirname(args.outputPath)); // Move screenshot to desired location const tempPath = tool.getResults()[0]?.path; if (tempPath && tempPath !== args.outputPath) { await fs.move(tempPath, args.outputPath, { overwrite: true }); } return { content: [ { type: 'text', text: `Screenshot captured successfully!\nOutput: ${args.outputPath}` } ] }; } finally { await tool.cleanup(); } } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); logger.info('MCP Manual Generator Server started'); // Cleanup on exit process.on('SIGINT', async () => { logger.info('Shutting down server...'); if (this.screenshotTool) { await this.screenshotTool.cleanup(); } process.exit(0); }); } } // Run server if executed directly if (require.main === module) { const server = new MCPManualServer(); server.run().catch(error => { logger.error('Server failed to start:', error); process.exit(1); }); } module.exports = MCPManualServer;

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/haixyeh/mcp-manual-generator'

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