Major refactor: Multi-user Chrome MCP extension with remote server architecture

This commit is contained in:
nasir@endelospay.com
2025-08-21 20:09:57 +05:00
parent d97cad1736
commit 5d869f6a7c
125 changed files with 16249 additions and 11906 deletions

View File

@@ -0,0 +1,306 @@
/**
* Complete Integration Test for Multi-User Chrome MCP System
*
* This test demonstrates the complete flow:
* 1. Multiple Chrome extensions connect with unique user IDs
* 2. Remote server automatically spawns LiveKit agents for each user
* 3. Voice commands are routed to the correct Chrome extension
* 4. Session isolation is maintained
*/
import WebSocket from 'ws';
import fetch from 'node-fetch';
const CHROME_WS_URL = 'ws://localhost:3001/chrome';
const MCP_HTTP_URL = 'http://localhost:3001/mcp';
class IntegratedUser {
constructor(userNumber) {
this.userNumber = userNumber;
this.chromeUserId = `user_${Date.now()}_${userNumber}_${Math.random().toString(36).substring(2, 10)}`;
this.ws = null;
this.sessionInfo = null;
this.receivedCommands = [];
this.liveKitAgentExpected = false;
}
async connectChromeExtension() {
return new Promise((resolve, reject) => {
console.log(`\n🔌 [User ${this.userNumber}] Connecting Chrome extension...`);
console.log(` Generated User ID: ${this.chromeUserId}`);
this.ws = new WebSocket(CHROME_WS_URL);
this.ws.on('open', () => {
console.log(`✅ [User ${this.userNumber}] Chrome WebSocket connected`);
// Send connection info with unique user ID
const connectionInfo = {
type: 'connection_info',
userId: this.chromeUserId,
userAgent: `IntegratedTestUser-${this.userNumber}`,
timestamp: Date.now(),
extensionId: `integrated-test-${this.userNumber}`
};
this.ws.send(JSON.stringify(connectionInfo));
});
this.ws.on('message', (data) => {
try {
const message = JSON.parse(data.toString());
if (message.type === 'session_info') {
this.sessionInfo = message.sessionInfo;
console.log(`🎯 [User ${this.userNumber}] Session established:`);
console.log(` Session ID: ${this.sessionInfo.sessionId}`);
console.log(` User ID: ${this.sessionInfo.userId}`);
console.log(` Expected LiveKit Room: mcp-chrome-user-${this.sessionInfo.userId}`);
this.liveKitAgentExpected = true;
resolve();
}
// Handle voice commands from LiveKit agent
if (message.action === 'callTool') {
this.receivedCommands.push({
...message,
receivedAt: Date.now()
});
console.log(`🎤 [User ${this.userNumber}] Received voice command: ${message.params.name}`);
console.log(` Arguments:`, message.params.arguments);
// Simulate Chrome extension executing the command
const response = {
id: message.id,
success: true,
result: {
message: `Command ${message.params.name} executed successfully`,
executedBy: `ChromeExtension-User${this.userNumber}`,
userId: this.chromeUserId,
timestamp: Date.now()
}
};
this.ws.send(JSON.stringify(response));
console.log(`📤 [User ${this.userNumber}] Sent command response`);
}
} catch (error) {
console.error(`❌ [User ${this.userNumber}] Error parsing message:`, error);
}
});
this.ws.on('error', (error) => {
console.error(`❌ [User ${this.userNumber}] WebSocket error:`, error);
reject(error);
});
setTimeout(() => {
if (!this.sessionInfo) {
reject(new Error(`[User ${this.userNumber}] Timeout waiting for session info`));
}
}, 10000);
});
}
async simulateVoiceCommand(toolName, args) {
console.log(`🎙️ [User ${this.userNumber}] Simulating voice command: ${toolName}`);
const payload = {
jsonrpc: '2.0',
id: `voice_${this.userNumber}_${Date.now()}`,
method: 'tools/call',
params: {
name: toolName,
arguments: {
...args,
userContext: this.chromeUserId
}
}
};
const headers = {
'Content-Type': 'application/json',
'chrome-user-id': this.chromeUserId, // Route to this specific user
'user-agent': `LiveKitAgent-User${this.userNumber}`
};
try {
const response = await fetch(MCP_HTTP_URL, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
const result = await response.json();
console.log(`📨 [User ${this.userNumber}] Voice command response:`, result.result || result.error);
return result;
} catch (error) {
console.error(`❌ [User ${this.userNumber}] Error sending voice command:`, error);
throw error;
}
}
getStatus() {
return {
userNumber: this.userNumber,
chromeUserId: this.chromeUserId,
sessionId: this.sessionInfo?.sessionId,
liveKitAgentExpected: this.liveKitAgentExpected,
commandsReceived: this.receivedCommands.length,
lastCommand: this.receivedCommands[this.receivedCommands.length - 1]?.params?.name
};
}
disconnect() {
if (this.ws) {
console.log(`👋 [User ${this.userNumber}] Disconnecting Chrome extension`);
this.ws.close();
}
}
}
async function runCompleteIntegrationTest() {
console.log('🚀 COMPLETE INTEGRATION TEST FOR MULTI-USER CHROME MCP SYSTEM');
console.log('=' .repeat(80));
console.log('This test demonstrates:');
console.log('✓ Multiple Chrome extensions with unique user IDs');
console.log('✓ Automatic LiveKit agent spawning');
console.log('✓ Voice command routing with session isolation');
console.log('✓ End-to-end user experience');
console.log('=' .repeat(80));
const users = [];
const NUM_USERS = 3;
try {
// Phase 1: Connect Chrome Extensions
console.log('\n📋 PHASE 1: Chrome Extension Connections');
console.log('-' .repeat(50));
for (let i = 1; i <= NUM_USERS; i++) {
const user = new IntegratedUser(i);
users.push(user);
await user.connectChromeExtension();
console.log(`✅ User ${i} Chrome extension connected successfully`);
// Wait between connections to see the flow clearly
await new Promise(resolve => setTimeout(resolve, 2000));
}
// Phase 2: Verify LiveKit Agent Spawning
console.log('\n📋 PHASE 2: LiveKit Agent Verification');
console.log('-' .repeat(50));
console.log('⏳ Waiting for LiveKit agents to start...');
await new Promise(resolve => setTimeout(resolve, 5000));
users.forEach(user => {
const status = user.getStatus();
console.log(`🤖 User ${status.userNumber}: LiveKit agent expected for room mcp-chrome-user-${status.chromeUserId}`);
});
// Phase 3: Test Voice Commands
console.log('\n📋 PHASE 3: Voice Command Testing');
console.log('-' .repeat(50));
const voiceCommands = [
{ tool: 'chrome_navigate', args: { url: 'https://www.google.com' } },
{ tool: 'chrome_click_element', args: { selector: '#search-button' } },
{ tool: 'chrome_get_web_content', args: { selector: 'body' } }
];
for (let i = 0; i < users.length; i++) {
const user = users[i];
const command = voiceCommands[i % voiceCommands.length];
console.log(`\n🎤 Testing voice command for User ${user.userNumber}:`);
console.log(` Command: ${command.tool}`);
console.log(` Target: ${command.args.url || command.args.selector || 'page content'}`);
await user.simulateVoiceCommand(command.tool, command.args);
// Wait for command to be processed
await new Promise(resolve => setTimeout(resolve, 3000));
}
// Phase 4: Test Session Isolation
console.log('\n📋 PHASE 4: Session Isolation Testing');
console.log('-' .repeat(50));
console.log('🔒 Testing that commands only go to the intended user...');
// Send a command from User 1 that should only affect User 1
await users[0].simulateVoiceCommand('chrome_navigate', {
url: 'https://example.com/isolation-test',
testId: 'isolation-test-user-1'
});
await new Promise(resolve => setTimeout(resolve, 2000));
// Phase 5: Results Analysis
console.log('\n📋 PHASE 5: Results Analysis');
console.log('-' .repeat(50));
let totalCommandsSent = NUM_USERS + 1; // 3 initial commands + 1 isolation test
let totalCommandsReceived = 0;
let isolationSuccess = true;
console.log('\n📊 USER STATUS REPORT:');
users.forEach(user => {
const status = user.getStatus();
totalCommandsReceived += status.commandsReceived;
console.log(`\n👤 User ${status.userNumber}:`);
console.log(` Chrome User ID: ${status.chromeUserId}`);
console.log(` Session ID: ${status.sessionId}`);
console.log(` Commands Received: ${status.commandsReceived}`);
console.log(` Last Command: ${status.lastCommand || 'None'}`);
console.log(` LiveKit Agent Expected: ${status.liveKitAgentExpected ? '✅' : '❌'}`);
});
// Check isolation: User 1 should have received 2 commands, others should have 1 each
const user1Commands = users[0].getStatus().commandsReceived;
const user2Commands = users[1].getStatus().commandsReceived;
const user3Commands = users[2].getStatus().commandsReceived;
if (user1Commands !== 2 || user2Commands !== 1 || user3Commands !== 1) {
isolationSuccess = false;
}
// Final Results
console.log('\n🎯 FINAL RESULTS:');
console.log('=' .repeat(50));
console.log(`📤 Total Commands Sent: ${totalCommandsSent}`);
console.log(`📥 Total Commands Received: ${totalCommandsReceived}`);
console.log(`🎯 Command Routing: ${totalCommandsReceived === totalCommandsSent ? '✅ SUCCESS' : '❌ FAILED'}`);
console.log(`🔒 Session Isolation: ${isolationSuccess ? '✅ SUCCESS' : '❌ FAILED'}`);
console.log(`🤖 LiveKit Agents: ${users.every(u => u.liveKitAgentExpected) ? '✅ ALL EXPECTED' : '❌ SOME MISSING'}`);
if (totalCommandsReceived === totalCommandsSent && isolationSuccess) {
console.log('\n🎉 COMPLETE INTEGRATION TEST PASSED! 🎉');
console.log('✅ Multi-user Chrome MCP system is working correctly');
} else {
console.log('\n❌ COMPLETE INTEGRATION TEST FAILED!');
console.log('❌ Issues detected in multi-user system');
}
} catch (error) {
console.error('\n❌ Integration test failed:', error);
} finally {
// Cleanup
console.log('\n🧹 Cleaning up test connections...');
users.forEach(user => user.disconnect());
setTimeout(() => {
console.log('✅ Integration test completed');
process.exit(0);
}, 3000);
}
}
// Run the complete integration test
runCompleteIntegrationTest().catch(console.error);