browser-automation-mcp-server.ts•9.59 kB
#!/usr/bin/env node
/**
* Browser Automation MCP Server for EuConquisto Composer
* Production-ready implementation using browser automation workflow
* @version 2.0.0
* @created 2025-07-02
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ErrorCode,
McpError
} from '@modelcontextprotocol/sdk/types.js';
import { chromium, Browser, Page } from 'playwright';
import { readFileSync } from 'fs';
import { join } from 'path';
import { cwd } from 'process';
// Import our custom modules
import { ComposerUIAutomation } from './browser-automation/composer-ui-automation.js';
import { EnhancedCompositionGenerator } from './browser-automation/enhanced-composition-generator.js';
// Use process.cwd() as base directory
const __dirname = cwd();
class BrowserAutomationMCPServer {
private server: Server;
private uiAutomation: ComposerUIAutomation;
private compositionGenerator: EnhancedCompositionGenerator;
private browser: Browser | null = null;
private jwtToken: string | null = null;
constructor() {
this.server = new Server(
{
name: 'euconquisto-composer-browser-automation',
version: '2.0.0',
},
{
capabilities: {
tools: {},
},
}
);
this.uiAutomation = new ComposerUIAutomation();
this.compositionGenerator = new EnhancedCompositionGenerator();
this.setupToolHandlers();
this.loadJwtToken();
}
private loadJwtToken() {
try {
const tokenPath = join(__dirname, 'archive/authentication/correct-jwt-new.txt');
this.jwtToken = readFileSync(tokenPath, 'utf-8').trim();
console.log('✅ JWT token loaded successfully');
} catch (error) {
console.error('⚠️ Failed to load JWT token:', error);
console.error('Please ensure JWT token exists at: archive/authentication/correct-jwt-new.txt');
}
}
private setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'create_educational_composition',
description: `Create and save educational compositions using browser automation.
This tool bypasses API restrictions by using browser automation with localStorage injection.
Workflow:
1. Generates educational composition JSON from your prompt
2. Authenticates via JWT redirect server (localhost:8080)
3. Creates blank composition for persistence foundation
4. Enriches composition with generated content
5. Saves and returns shareable URL
Features:
- Intelligent content generation based on Brazilian educational standards
- Rich element types (text, quiz, flashcards, videos, etc.)
- Automatic subject and grade level detection
- Interactive learning elements`,
inputSchema: {
type: 'object',
properties: {
prompt: {
type: 'string',
description: 'Educational content prompt describing what you want to create'
},
subject: {
type: 'string',
description: 'Subject area (e.g., "Ciências", "Matemática", "História", "Português", "Geografia")',
enum: ['Ciências', 'Matemática', 'História', 'Português', 'Geografia', 'Geral']
},
gradeLevel: {
type: 'string',
description: 'Grade level (e.g., "7º ano", "fundamental-2", "médio")'
},
title: {
type: 'string',
description: 'Optional custom title for the composition'
}
},
required: ['prompt']
}
},
{
name: 'check_jwt_server_status',
description: 'Check if the JWT redirect server is running on localhost:8080',
inputSchema: {
type: 'object',
properties: {}
}
}
]
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
switch (request.params.name) {
case 'create_educational_composition':
return this.handleCreateComposition(request.params.arguments);
case 'check_jwt_server_status':
return this.handleCheckJwtServer();
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name}`
);
}
});
}
private async handleCreateComposition(args: any) {
const {
prompt,
subject = 'Ciências',
gradeLevel = '7º ano',
title
} = args;
// Validate JWT token
if (!this.jwtToken) {
return {
content: [
{
type: 'text',
text: `❌ JWT token not found!
Please ensure you have a valid JWT token at:
archive/authentication/correct-jwt-new.txt
The token is required for authentication with EuConquisto Composer.`
}
],
isError: true
};
}
try {
console.log('🎯 Starting browser automation workflow...');
console.log(` - Prompt: ${prompt}`);
console.log(` - Subject: ${subject}`);
console.log(` - Grade: ${gradeLevel}`);
// Step 1: Generate composition
const compositionData = this.compositionGenerator.generateComposition(
prompt,
subject,
gradeLevel,
title
);
console.log(`📚 Generated composition: ${compositionData.composition.title}`);
console.log(`🧩 Elements: ${compositionData.composition.elements.length}`);
// Step 2: Launch browser and execute workflow
this.browser = await chromium.launch({
headless: false,
slowMo: 100,
args: ['--disable-blink-features=AutomationControlled']
});
const context = await this.browser.newContext({
viewport: { width: 1920, height: 1080 },
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
});
const page = await context.newPage();
// Step 3: Execute complete workflow
const result = await this.uiAutomation.executeCompleteWorkflow(
page,
compositionData
);
// Close browser
await this.browser.close();
this.browser = null;
if (result.success && result.url) {
return {
content: [
{
type: 'text',
text: `✅ **Composition created successfully!**
📎 **URL**: ${result.url}
📚 **Title**: ${compositionData.composition.title}
📖 **Description**: ${compositionData.composition.description}
🎓 **Subject**: ${subject} - ${gradeLevel}
🧩 **Elements**: ${compositionData.composition.elements.length}
⏱️ **Duration**: ${compositionData.composition.metadata?.duracao_estimada || '45 minutos'}
**Tags**: ${compositionData.composition.metadata?.tags.join(', ') || 'N/A'}
The composition has been saved and is ready to use. You can share the URL above to access it.`
}
]
};
} else {
throw new Error(result.error || 'Unknown error occurred');
}
} catch (error: any) {
// Cleanup browser if still open
if (this.browser) {
await this.browser.close();
this.browser = null;
}
console.error('❌ Error in browser automation workflow:', error);
return {
content: [
{
type: 'text',
text: `❌ **Failed to create composition**
**Error**: ${error.message}
**Troubleshooting**:
1. ✓ Ensure JWT redirect server is running on localhost:8080
2. ✓ Check if JWT token is valid (expires: 2025-07-28)
3. ✓ Verify EuConquisto Composer is accessible
4. ✓ Check browser permissions and Playwright installation
**Error Details**:
\`\`\`
${error.stack}
\`\`\``
}
],
isError: true
};
}
}
private async handleCheckJwtServer() {
try {
const response = await fetch('http://localhost:8080/health', {
method: 'GET',
signal: AbortSignal.timeout(5000)
});
if (response.ok) {
const data = await response.json();
return {
content: [
{
type: 'text',
text: `✅ **JWT Redirect Server Status: ONLINE**
**Server Details**:
- URL: http://localhost:8080
- Status: ${data.status || 'healthy'}
- JWT Valid: ${data.jwtValid !== false ? 'Yes' : 'No'}
- Ready for browser automation: Yes
The server is ready to handle authentication requests.`
}
]
};
}
} catch (error: any) {
return {
content: [
{
type: 'text',
text: `⚠️ **JWT Redirect Server Status: OFFLINE**
The JWT redirect server is not responding on localhost:8080.
**To start the server**:
\`\`\`bash
node tools/servers/jwt-redirect-server-v1.0.2.js
\`\`\`
**Error**: ${error.message}`
}
],
isError: true
};
}
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.log('🚀 Browser Automation MCP Server v2.0.0 running');
console.log('📚 Ready to create educational compositions');
}
}
// Run the server if this file is executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
const server = new BrowserAutomationMCPServer();
server.run().catch(console.error);
}
export { BrowserAutomationMCPServer };