claude-computer-use-mcp
Version:
MCP server providing browser automation capabilities to Claude Code
279 lines • 10.2 kB
JavaScript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
import { BrowserController } from './browser-controller.js';
import { createTools } from './tools.js';
import { monitoring } from './monitoring.js';
import { ClaudeIntegration } from './claude-integration.js';
import { EnterpriseManager } from './enterprise.js';
import { SECURITY_CONFIG, ENTERPRISE_CONFIG } from './config.js';
import * as fs from 'fs/promises';
// Version will be set from package.json
let version = '1.2.0';
// Initialize version from package.json
try {
const packageJson = JSON.parse(await fs.readFile('./package.json', 'utf8'));
version = packageJson.version;
}
catch {
// Use default version if package.json not found
}
// Initialize core components
const browserController = new BrowserController(SECURITY_CONFIG);
const tools = createTools(browserController);
const claudeIntegration = new ClaudeIntegration(browserController);
const enterpriseManager = new EnterpriseManager(ENTERPRISE_CONFIG, browserController);
// Initialize monitoring
monitoring.enableMetrics = SECURITY_CONFIG.enableMetrics;
monitoring.enableAuditLogging = SECURITY_CONFIG.enableAuditLogging;
const server = new Server({
name: 'claude-computer-use',
version,
}, {
capabilities: {
tools: {},
},
});
// Handle list tools request
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: Object.entries(tools).map(([name, tool]) => ({
name,
description: tool.description,
inputSchema: tool.inputSchema,
})),
};
});
// Handle tool calls with enhanced error handling and monitoring
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const startTime = Date.now();
// Track request for enterprise metrics
enterpriseManager.incrementRequestCount();
const tool = tools[name];
if (!tool) {
enterpriseManager.incrementErrorCount();
monitoring.auditLog({
level: 'error',
action: 'unknown_tool',
details: { toolName: name, availableTools: Object.keys(tools) }
});
return {
content: [
{
type: 'text',
text: `Error: Unknown tool: ${name}. Available tools: ${Object.keys(tools).join(', ')}`,
},
],
isError: true,
};
}
try {
// Enhanced error handling for specific Claude Code integration
const result = await tool.handler(args || {});
const duration = Date.now() - startTime;
// Record successful operation
monitoring.recordMetric({
sessionId: args?.sessionId || 'unknown',
action: name,
duration,
success: true
});
// Record workflow step if session provided
if (args?.sessionId) {
await claudeIntegration.recordWorkflowStep(args.sessionId, name, {
selector: args.selector,
value: args.value || args.text || args.url
});
}
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
}
catch (error) {
const duration = Date.now() - startTime;
enterpriseManager.incrementErrorCount();
// Record failed operation
monitoring.recordMetric({
sessionId: args?.sessionId || 'unknown',
action: name,
duration,
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
});
// Get enhanced error handling suggestions
let errorResponse = `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
if (args?.sessionId) {
try {
const errorAnalysis = await claudeIntegration.handleError(args.sessionId, error instanceof Error ? error : new Error('Unknown error'), { tool: name, args });
errorResponse = `Error: ${errorAnalysis.error}\n\nSuggestions:\n${errorAnalysis.suggestions.map(s => `• ${s}`).join('\n')}`;
if (errorAnalysis.recoveryActions.length > 0) {
errorResponse += `\n\nRecovery Actions:\n${errorAnalysis.recoveryActions.map(a => `• ${a.action}: ${a.description}`).join('\n')}`;
}
}
catch (analysisError) {
// If error analysis fails, use simple error message
}
}
return {
content: [
{
type: 'text',
text: errorResponse,
},
],
isError: true,
};
}
});
// Enhanced cleanup with enterprise features
process.on('SIGINT', async () => {
console.error('Received SIGINT, gracefully shutting down...');
try {
await browserController.closeAllSessions();
await enterpriseManager.cleanup();
await monitoring.cleanup();
monitoring.auditLog({
level: 'info',
action: 'server_shutdown',
details: { signal: 'SIGINT', graceful: true }
});
}
catch (error) {
console.error('Error during graceful shutdown:', error);
}
finally {
process.exit(0);
}
});
process.on('SIGTERM', async () => {
console.error('Received SIGTERM, gracefully shutting down...');
try {
await browserController.closeAllSessions();
await enterpriseManager.cleanup();
await monitoring.cleanup();
monitoring.auditLog({
level: 'info',
action: 'server_shutdown',
details: { signal: 'SIGTERM', graceful: true }
});
}
catch (error) {
console.error('Error during graceful shutdown:', error);
}
finally {
process.exit(0);
}
});
// Handle uncaught exceptions with better logging
process.on('uncaughtException', async (error) => {
console.error('Uncaught exception:', error);
monitoring.auditLog({
level: 'error',
action: 'uncaught_exception',
details: {
error: error.message,
stack: error.stack,
timestamp: Date.now()
}
});
try {
await browserController.closeAllSessions();
await enterpriseManager.cleanup();
await monitoring.cleanup();
}
catch (closeError) {
console.error('Error during emergency cleanup:', closeError);
}
process.exit(1);
});
// Handle unhandled promise rejections with better logging
process.on('unhandledRejection', async (reason, promise) => {
console.error('Unhandled rejection at:', promise, 'reason:', reason);
monitoring.auditLog({
level: 'error',
action: 'unhandled_rejection',
details: {
reason: reason instanceof Error ? reason.message : String(reason),
stack: reason instanceof Error ? reason.stack : undefined,
timestamp: Date.now()
}
});
try {
await browserController.closeAllSessions();
await enterpriseManager.cleanup();
await monitoring.cleanup();
}
catch (error) {
console.error('Error during emergency cleanup:', error);
}
process.exit(1);
});
// Start the server with enhanced initialization
async function main() {
try {
const transport = new StdioServerTransport();
await server.connect(transport);
// Log startup with all enabled features
const features = {
security: {
contentSecurityPolicy: SECURITY_CONFIG.enableContentSecurityPolicy,
requestInterception: SECURITY_CONFIG.enableRequestInterception,
auditLogging: SECURITY_CONFIG.enableAuditLogging,
sandboxMode: SECURITY_CONFIG.sandboxMode
},
enterprise: {
healthChecks: ENTERPRISE_CONFIG.enableHealthChecks,
metrics: ENTERPRISE_CONFIG.enableMetrics,
tracing: ENTERPRISE_CONFIG.enableTracing
},
claude: {
smartWaiting: true,
workflowRecording: true,
semanticSelectors: true
}
};
monitoring.auditLog({
level: 'info',
action: 'server_started',
details: {
version,
features,
toolsAvailable: Object.keys(tools).length,
pid: process.pid,
nodeVersion: process.version
}
});
console.error(`🚀 Claude Computer Use MCP Server v${version} started successfully!`);
console.error(`📊 Features: ${Object.keys(tools).length} tools, Health checks: ${ENTERPRISE_CONFIG.enableHealthChecks ? 'enabled' : 'disabled'}, Metrics: ${ENTERPRISE_CONFIG.enableMetrics ? 'enabled' : 'disabled'}`);
if (ENTERPRISE_CONFIG.enableHealthChecks) {
console.error(`🏥 Health checks available at: http://localhost:${ENTERPRISE_CONFIG.healthCheckPort}/health`);
}
if (ENTERPRISE_CONFIG.enableMetrics) {
console.error(`📈 Metrics available at: http://localhost:${ENTERPRISE_CONFIG.metricsPort}/metrics`);
}
}
catch (error) {
console.error('Failed to connect transport:', error);
monitoring.auditLog({
level: 'error',
action: 'server_start_failed',
details: {
error: error instanceof Error ? error.message : 'Unknown error',
stack: error instanceof Error ? error.stack : undefined
}
});
throw error;
}
}
main().catch((error) => {
console.error('Failed to start server:', error);
process.exit(1);
});
//# sourceMappingURL=index.js.map