Skip to main content
Glama
intelligent-composer-mcp-server.ts23.4 kB
#!/usr/bin/env node /** * Intelligent EuConquisto Composer MCP Server * Advanced AI-driven content creation with educational best practices */ 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 { readFileSync } from 'fs'; import { resolve, dirname } from 'path'; import { fileURLToPath } from 'url'; import { StableBrowserManager, BrowserSession } from './stable-browser-manager.js'; import { EnhancedNLPWidgetParser, ParseOptions, EnhancedParsingResult } from './enhanced-nlp-widget-parser.js'; import { registerAllPlugins } from './content-element-plugins/index.js'; const currentDir = dirname(fileURLToPath(import.meta.url)); interface CompositionData { version: string; metadata: { title: string; description: string; thumb?: string | null; tags: string[]; }; interface: { content_language: string; index_option: string; font_family: string; show_summary: string; finish_btn: string; }; structure: any[]; assets: any[]; } class IntelligentComposerMCPServer { private server: Server; private jwtToken!: string; private baseURL = 'https://composer.euconquisto.com/#/embed'; private orgId = '36c92686-c494-ec11-a22a-dc984041c95d'; private browserManager: StableBrowserManager; private intelligentParser: EnhancedNLPWidgetParser; constructor() { this.server = new Server( { name: 'intelligent-composer-mcp-server', version: '2.0.0', }, { capabilities: { tools: {}, }, } ); this.browserManager = new StableBrowserManager(); this.intelligentParser = new EnhancedNLPWidgetParser(); // Register all content element plugins registerAllPlugins(); this.loadJWTToken(); this.setupToolHandlers(); } private loadJWTToken() { try { const tokenPath = resolve(currentDir, '..', 'correct-jwt-new.txt'); this.jwtToken = readFileSync(tokenPath, 'utf8').trim(); } catch (error) { throw new Error('Failed to load JWT token: ' + error); } } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'create-intelligent-composition', description: 'Create a composition using AI-driven content analysis and educational best practices', inputSchema: { type: 'object', properties: { prompt: { type: 'string', description: 'Natural language description of the educational content to create', }, title: { type: 'string', description: 'Title for the composition', }, description: { type: 'string', description: 'Optional description for the composition', default: '', }, targetAudience: { type: 'string', enum: ['elementary', 'middle', 'high', 'college', 'adult', 'professional'], description: 'Target audience for the content', default: 'adult', }, complexity: { type: 'string', enum: ['basic', 'intermediate', 'advanced'], description: 'Content complexity level', default: 'intermediate', }, engagementLevel: { type: 'string', enum: ['low', 'medium', 'high'], description: 'Desired level of interactivity and engagement', default: 'medium', }, maxDuration: { type: 'number', description: 'Maximum learning duration in minutes', default: 30, }, prioritizeInteractivity: { type: 'boolean', description: 'Whether to prioritize interactive elements', default: false, }, }, required: ['prompt', 'title'], }, }, { name: 'analyze-content-structure', description: 'Analyze content and provide educational insights without creating a composition', inputSchema: { type: 'object', properties: { content: { type: 'string', description: 'Content to analyze', }, title: { type: 'string', description: 'Optional title for context', }, }, required: ['content'], }, }, { name: 'suggest-content-improvements', description: 'Analyze existing content and suggest improvements based on educational best practices', inputSchema: { type: 'object', properties: { currentContent: { type: 'string', description: 'Current content to improve', }, learningObjectives: { type: 'array', items: { type: 'string' }, description: 'Specific learning objectives to meet', }, targetAudience: { type: 'string', enum: ['elementary', 'middle', 'high', 'college', 'adult', 'professional'], description: 'Target audience', default: 'adult', }, }, required: ['currentContent'], }, }, { name: 'preview-composition', description: 'Preview a composition by URL with intelligent analysis', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'The composition URL to preview and analyze', }, }, required: ['url'], }, }, ], })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'create-intelligent-composition': return await this.createIntelligentComposition(args); case 'analyze-content-structure': return await this.analyzeContentStructure(args); case 'suggest-content-improvements': return await this.suggestContentImprovements(args); case 'preview-composition': return await this.previewComposition(args); default: throw new McpError(ErrorCode.MethodNotFound, `Tool ${name} not found`); } } catch (error) { throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error}`); } }); } private async createIntelligentComposition(args: any) { const { prompt, title, description = '', targetAudience = 'adult', complexity = 'intermediate', engagementLevel = 'medium', maxDuration = 30, prioritizeInteractivity = false, } = args; console.log('🧠 Creating intelligent composition...'); console.log(`📋 Parameters: - Title: ${title} - Target Audience: ${targetAudience} - Complexity: ${complexity} - Engagement Level: ${engagementLevel} - Max Duration: ${maxDuration} minutes - Prioritize Interactivity: ${prioritizeInteractivity}`); // Step 1: Intelligent content analysis const parseOptions: ParseOptions = { enablePluginSystem: true, applyEducationalBestPractices: true, optimizeEngagement: engagementLevel === 'high', maxWidgets: Math.ceil(maxDuration / 2), // ~2 minutes per widget targetComplexity: complexity as any, prioritizeInteractivity, }; const analysisResult = this.intelligentParser.parseContentIntelligently( prompt, title, parseOptions ); console.log(`📊 Analysis Complete: - Generated ${analysisResult.widgets.length} widgets - Educational insights: ${analysisResult.contentInsights.length} - Recommendations: ${analysisResult.recommendations.length}`); // Step 2: Create composition with browser automation return await this.browserManager.withStableSession(async (session: BrowserSession) => { // Navigate and create new composition await this.browserManager.safeClick(session.page, 'button:has-text("Nova Composição")'); await session.page.waitForTimeout(2000); // Create composition data with intelligent widgets const compositionData: CompositionData = { version: '1.1', metadata: { title, description: description || `AI-generated educational content about ${title}`, thumb: null, tags: this.extractTags(analysisResult), }, interface: { content_language: 'pt_br', index_option: 'buttons', font_family: 'Lato', show_summary: analysisResult.educationalContext.estimatedDuration > 20 ? 'enabled' : 'disabled', finish_btn: 'enabled', }, structure: analysisResult.widgets, assets: [], }; // Set composition in localStorage await session.page.evaluate((data: any) => { // @ts-ignore - localStorage exists in browser context localStorage.setItem('rdp-composer-data', JSON.stringify(data)); }, compositionData); // Save the composition await this.browserManager.safeClick(session.page, 'button:has-text("Salvar")'); await session.page.waitForTimeout(3000); const savedURL = await session.page.url(); // Format comprehensive response return { content: [ { type: 'text', text: this.formatIntelligentCompositionResult(analysisResult, savedURL, title), }, ], }; }, { headless: false, slowMo: 200 }); } private async analyzeContentStructure(args: any) { const { content, title } = args; console.log('🔍 Analyzing content structure...'); const analysisResult = this.intelligentParser.parseContentIntelligently( content, title, { enablePluginSystem: true, applyEducationalBestPractices: true, optimizeEngagement: true, } ); return { content: [ { type: 'text', text: this.formatContentAnalysis(analysisResult), }, ], }; } private async suggestContentImprovements(args: any) { const { currentContent, learningObjectives = [], targetAudience = 'adult' } = args; console.log('💡 Generating content improvement suggestions...'); // Analyze current content const analysisResult = this.intelligentParser.parseContentIntelligently( currentContent, undefined, { enablePluginSystem: true, applyEducationalBestPractices: true, optimizeEngagement: true, targetComplexity: 'intermediate', } ); // Generate specific improvement suggestions const improvements = this.generateImprovementSuggestions( analysisResult, learningObjectives, targetAudience ); return { content: [ { type: 'text', text: this.formatImprovementSuggestions(improvements, analysisResult), }, ], }; } private async previewComposition(args: any) { const { url } = args; try { // Extract composition data from URL const urlData = this.extractCompositionFromURL(url); if (!urlData) { throw new Error('Could not extract composition data from URL'); } const composition = JSON.parse(urlData); // Analyze the composition structure const widgets = composition.structure || []; const analysis = this.analyzeExistingComposition(widgets); return { content: [ { type: 'text', text: this.formatCompositionPreview(composition, analysis), }, ], }; } catch (error) { throw new Error(`Failed to preview composition: ${error}`); } } // Helper methods for formatting and analysis private formatIntelligentCompositionResult( result: EnhancedParsingResult, url: string, title: string ): string { const widgetSummary = this.summarizeWidgets(result.widgets); const insightsSummary = this.summarizeInsights(result.contentInsights); return `🎯 **Intelligent Composition Created Successfully!** 🔗 **Composition URL:** ${url} 📋 **Composition Details:** • **Title:** ${title} • **Widgets Generated:** ${result.widgets.length} • **Estimated Duration:** ${result.educationalContext.estimatedDuration} minutes • **Target Audience:** ${result.educationalContext.targetAudience} • **Complexity Level:** ${result.educationalContext.contentComplexity} • **Engagement Level:** ${result.educationalContext.engagementLevel} 🧩 **Widget Breakdown:** ${widgetSummary} 🧠 **Educational Insights:** ${insightsSummary} 💡 **Recommendations:** ${result.recommendations.map(rec => `• ${rec}`).join('\n')} 📚 **Learning Objectives:** ${result.educationalContext.learningObjectives.map(obj => `• ${obj}`).join('\n') || '• Interactive learning experience'} ⭐ **Quality Score:** ${this.calculateQualityScore(result)}/100`; } private formatContentAnalysis(result: EnhancedParsingResult): string { return `📊 **Content Structure Analysis** 🎯 **Educational Context:** • **Target Audience:** ${result.educationalContext.targetAudience} • **Complexity:** ${result.educationalContext.contentComplexity} • **Estimated Duration:** ${result.educationalContext.estimatedDuration} minutes • **Engagement Level:** ${result.educationalContext.engagementLevel} 🧩 **Recommended Content Structure:** ${this.summarizeWidgets(result.widgets)} 🔍 **Content Insights:** ${this.summarizeInsights(result.contentInsights)} 📈 **Recommended Learning Sequence:** ${result.learningSequence.map((type, i) => `${i + 1}. ${this.getWidgetDescription(type)}`).join('\n')} 💡 **Optimization Suggestions:** ${result.recommendations.map(rec => `• ${rec}`).join('\n')}`; } private formatImprovementSuggestions(improvements: any[], result: EnhancedParsingResult): string { return `💡 **Content Improvement Suggestions** 🔍 **Current Analysis:** • **Quality Score:** ${this.calculateQualityScore(result)}/100 • **Widget Count:** ${result.widgets.length} • **Engagement Elements:** ${result.widgets.filter(w => ['quiz-1', 'flashcards-1', 'hotspot-1'].includes(w.type)).length} 🚀 **Priority Improvements:** ${improvements.filter(imp => imp.priority === 'high').map(imp => `• **${imp.title}:** ${imp.description}`).join('\n')} 📈 **Additional Enhancements:** ${improvements.filter(imp => imp.priority === 'medium').map(imp => `• ${imp.title}: ${imp.description}`).join('\n')} ⚡ **Quick Wins:** ${improvements.filter(imp => imp.priority === 'low').map(imp => `• ${imp.description}`).join('\n')} 🎯 **Educational Impact:** ${result.contentInsights.filter(insight => insight.impact === 'high').map(insight => `• ${insight.message}`).join('\n')}`; } private formatCompositionPreview(composition: any, analysis: any): string { return `👀 **Composition Preview & Analysis** 📋 **Basic Information:** • **Title:** ${composition.metadata?.title || 'Untitled'} • **Description:** ${composition.metadata?.description || 'No description'} • **Version:** ${composition.version || 'Unknown'} 🧩 **Structure Analysis:** • **Total Widgets:** ${composition.structure?.length || 0} • **Widget Types:** ${analysis.widgetTypes.join(', ')} • **Interactive Elements:** ${analysis.interactiveCount} • **Engagement Score:** ${analysis.engagementScore}/100 📊 **Educational Assessment:** • **Complexity Level:** ${analysis.complexityLevel} • **Learning Patterns:** ${analysis.learningPatterns.join(', ')} • **Content Distribution:** ${analysis.contentDistribution} 💡 **Improvement Opportunities:** ${analysis.suggestions.map((suggestion: string) => `• ${suggestion}`).join('\n')}`; } // Analysis helper methods private summarizeWidgets(widgets: any[]): string { const widgetCounts: Record<string, number> = {}; widgets.forEach(widget => { const type = widget.type; widgetCounts[type] = (widgetCounts[type] || 0) + 1; }); return Object.entries(widgetCounts) .map(([type, count]) => `• ${this.getWidgetDescription(type)}: ${count}`) .join('\n'); } private summarizeInsights(insights: any[]): string { if (insights.length === 0) return '• All educational best practices are being followed'; return insights .map(insight => `• **${insight.type.toUpperCase()}:** ${insight.message}`) .join('\n'); } private getWidgetDescription(type: string): string { const descriptions: Record<string, string> = { 'head-1': 'Header Section', 'text-1': 'Text Content', 'quiz-1': 'Multiple Choice Quiz', 'video-1': 'Video Content', 'flashcards-1': 'Interactive Flashcards', 'hotspot-1': 'Interactive Hotspot', 'gallery-1': 'Image Gallery', 'list-1': 'Structured List', }; return descriptions[type] || type.replace('-', ' ').replace(/\b\w/g, l => l.toUpperCase()); } private calculateQualityScore(result: EnhancedParsingResult): number { let score = 70; // Base score // Widget variety bonus const uniqueTypes = new Set(result.widgets.map(w => w.type)); score += Math.min(uniqueTypes.size * 5, 15); // Interactive content bonus const interactiveCount = result.widgets.filter(w => ['quiz-1', 'flashcards-1', 'hotspot-1'].includes(w.type) ).length; score += Math.min(interactiveCount * 3, 10); // Educational insights penalty const highImpactIssues = result.contentInsights.filter(insight => insight.impact === 'high').length; score -= highImpactIssues * 5; return Math.max(0, Math.min(100, score)); } private extractTags(result: EnhancedParsingResult): string[] { const tags = []; // Add audience tag tags.push(result.educationalContext.targetAudience); // Add complexity tag tags.push(result.educationalContext.contentComplexity); // Add widget type tags const uniqueTypes = new Set(result.widgets.map(w => w.type)); if (uniqueTypes.has('quiz-1')) tags.push('assessment'); if (uniqueTypes.has('video-1')) tags.push('multimedia'); if (uniqueTypes.has('flashcards-1')) tags.push('memorization'); // Add AI-generated tag tags.push('ai-generated'); return tags; } private generateImprovementSuggestions( result: EnhancedParsingResult, objectives: string[], audience: string ): any[] { const improvements = []; // Check for missing assessments const hasQuiz = result.widgets.some(w => w.type === 'quiz-1'); if (!hasQuiz && objectives.length > 0) { improvements.push({ title: 'Add Knowledge Assessment', description: 'Include quiz questions to verify learning objectives are met', priority: 'high', }); } // Check for multimedia content const hasVideo = result.widgets.some(w => w.type === 'video-1'); if (!hasVideo && result.educationalContext.contentComplexity !== 'basic') { improvements.push({ title: 'Add Visual Content', description: 'Include video explanations for complex concepts', priority: 'medium', }); } // Check for interactive elements const interactiveCount = result.widgets.filter(w => ['quiz-1', 'flashcards-1', 'hotspot-1'].includes(w.type) ).length; if (interactiveCount < 2) { improvements.push({ title: 'Increase Interactivity', description: 'Add more interactive elements to maintain engagement', priority: 'medium', }); } return improvements; } private analyzeExistingComposition(widgets: any[]): any { const widgetTypes = widgets.map(w => w.type); const uniqueTypes = [...new Set(widgetTypes)]; const interactiveCount = widgets.filter(w => ['quiz-1', 'flashcards-1', 'hotspot-1'].includes(w.type) ).length; return { widgetTypes: uniqueTypes, interactiveCount, engagementScore: Math.min(100, (interactiveCount / widgets.length) * 100 + 50), complexityLevel: widgets.length > 10 ? 'advanced' : widgets.length > 5 ? 'intermediate' : 'basic', learningPatterns: this.identifyLearningPatterns(widgets), contentDistribution: this.analyzeContentDistribution(widgets), suggestions: this.generateCompositionSuggestions(widgets), }; } private identifyLearningPatterns(widgets: any[]): string[] { const patterns = []; if (widgets.some(w => w.type === 'quiz-1')) patterns.push('Assessment'); if (widgets.some(w => w.type === 'video-1')) patterns.push('Visual Learning'); if (widgets.some(w => w.type === 'flashcards-1')) patterns.push('Memorization'); if (widgets.some(w => w.type === 'text-1')) patterns.push('Reading'); return patterns; } private analyzeContentDistribution(widgets: any[]): string { const total = widgets.length; const interactive = widgets.filter(w => ['quiz-1', 'flashcards-1', 'hotspot-1'].includes(w.type)).length; const content = widgets.filter(w => ['text-1', 'video-1'].includes(w.type)).length; const interactivePercent = Math.round((interactive / total) * 100); const contentPercent = Math.round((content / total) * 100); return `${interactivePercent}% Interactive, ${contentPercent}% Content`; } private generateCompositionSuggestions(widgets: any[]): string[] { const suggestions = []; const hasQuiz = widgets.some(w => w.type === 'quiz-1'); if (!hasQuiz) suggestions.push('Add knowledge check questions'); const hasVideo = widgets.some(w => w.type === 'video-1'); if (!hasVideo) suggestions.push('Consider adding video explanations'); const textCount = widgets.filter(w => w.type === 'text-1').length; if (textCount > widgets.length * 0.7) suggestions.push('Break up long text sections with interactive elements'); return suggestions; } private extractCompositionFromURL(url: string): string | null { try { const match = url.match(/\/composer\/([A-Za-z0-9+/=]+)$/); if (!match) return null; const encodedData = match[1]; const buffer = Buffer.from(encodedData, 'base64'); const { gunzipSync } = require('zlib'); const decompressed = gunzipSync(buffer); return decompressed.toString(); } catch (error) { return null; } } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); } } const server = new IntelligentComposerMCPServer(); server.run().catch(console.error);

Latest Blog Posts

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/rkm097git/euconquisto-composer-mcp-poc'

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