index.tsā¢10.9 kB
#!/usr/bin/env node
// Debug logging setup - Log process start
console.error(`š [DEBUG] Process starting - PID: ${process.pid}, Node: ${process.version}, Platform: ${process.platform}`);
console.error(`š [DEBUG] Working directory: ${process.cwd()}`);
console.error(`š [DEBUG] Command args: ${process.argv.join(' ')}`);
import { Server } from '@modelContextProtocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelContextProtocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ListPromptsRequestSchema,
InitializeRequestSchema,
} from '@modelContextProtocol/sdk/types.js';
console.error('š [DEBUG] MCP SDK imports completed successfully');
// Import extracted modules
console.error('š [DEBUG] Loading tool definitions...');
import { TOOLS, SERVER_INFO, CAPABILITIES, TOOL_NAMES, NavigateArgs, ClickArgs, TypeArgs, WaitArgs, SolveCaptchaArgs, FindSelectorArgs, SaveContentAsMarkdownArgs } from './tool-definitions.js';
console.error('š [DEBUG] Loading system utils...');
import { withErrorHandling } from './system-utils.js';
console.error('š [DEBUG] Loading browser manager...');
import { closeBrowser, forceKillAllChromeProcesses } from './browser-manager.js';
console.error('š [DEBUG] Loading core infrastructure...');
import { setupProcessCleanup, MCP_SERVER_CONFIG } from './core-infrastructure.js';
// Import handlers
console.error('š [DEBUG] Loading handlers...');
import { handleBrowserInit, handleBrowserClose } from './handlers/browser-handlers.js';
import { handleNavigate, handleWait } from './handlers/navigation-handlers.js';
import { handleClick, handleType, handleSolveCaptcha, handleRandomScroll } from './handlers/interaction-handlers.js';
import { handleGetContent, handleFindSelector } from './handlers/content-handlers.js';
import { handleSaveContentAsMarkdown } from './handlers/file-handlers.js';
console.error('š [DEBUG] All modules loaded successfully');
console.error(`š [DEBUG] Server info: ${JSON.stringify(SERVER_INFO)}`);
console.error(`š [DEBUG] Available tools: ${TOOLS.length} tools loaded`);
// Initialize MCP server
console.error('š [DEBUG] Creating MCP server instance...');
const server = new Server(SERVER_INFO, { capabilities: CAPABILITIES });
console.error('š [DEBUG] MCP server instance created successfully');
// Register initialize handler (CRITICAL - missing handler can cause crash)
console.error('š [DEBUG] Registering initialize handler...');
server.setRequestHandler(InitializeRequestSchema, async (request) => {
console.error(`š [DEBUG] Initialize request received: ${JSON.stringify(request)}`);
// Use the client's protocol version to ensure compatibility
const clientProtocolVersion = request.params.protocolVersion;
console.error(`š [DEBUG] Client protocol version: ${clientProtocolVersion}`);
const response = {
protocolVersion: clientProtocolVersion, // Match client version for compatibility
capabilities: CAPABILITIES,
serverInfo: SERVER_INFO,
};
console.error(`š [DEBUG] Sending initialize response: ${JSON.stringify(response)}`);
// Add a small delay to see if there are any immediate errors after response
setTimeout(() => {
console.error(`š [DEBUG] 1 second after initialize response - server still alive`);
}, 1000);
setTimeout(() => {
console.error(`š [DEBUG] 5 seconds after initialize response - server still alive`);
}, 5000);
return response;
});
// Register tool handlers
console.error('š [DEBUG] Registering tools handler...');
server.setRequestHandler(ListToolsRequestSchema, async () => {
console.error('š [DEBUG] Tools list requested');
return { tools: TOOLS };
});
// Register resource handlers (placeholder)
console.error('š [DEBUG] Registering resources handler...');
server.setRequestHandler(ListResourcesRequestSchema, async () => {
console.error('š [DEBUG] Resources list requested');
return { resources: [] };
});
// Register prompt handlers (placeholder)
console.error('š [DEBUG] Registering prompts handler...');
server.setRequestHandler(ListPromptsRequestSchema, async () => {
console.error('š [DEBUG] Prompts list requested');
return { prompts: [] };
});
// Main tool call handler
console.error('š [DEBUG] Registering tool call handler...');
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
console.error(`š [DEBUG] Tool call received: ${name} with args: ${JSON.stringify(args)}`);
try {
switch (name) {
case TOOL_NAMES.BROWSER_INIT:
return await handleBrowserInit(args || {});
case TOOL_NAMES.NAVIGATE:
return await handleNavigate(args as unknown as NavigateArgs);
case TOOL_NAMES.GET_CONTENT:
return await handleGetContent(args || {});
case TOOL_NAMES.CLICK:
return await handleClick(args as unknown as ClickArgs);
case TOOL_NAMES.TYPE:
return await handleType(args as unknown as TypeArgs);
case TOOL_NAMES.WAIT:
return await handleWait(args as unknown as WaitArgs);
case TOOL_NAMES.BROWSER_CLOSE:
return await handleBrowserClose();
case TOOL_NAMES.SOLVE_CAPTCHA:
return await handleSolveCaptcha(args as unknown as SolveCaptchaArgs);
case TOOL_NAMES.RANDOM_SCROLL:
return await handleRandomScroll();
case TOOL_NAMES.FIND_SELECTOR:
return await handleFindSelector(args as unknown as FindSelectorArgs);
case TOOL_NAMES.SAVE_CONTENT_AS_MARKDOWN:
return await handleSaveContentAsMarkdown(args as unknown as SaveContentAsMarkdownArgs);
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`Tool ${name} failed:`, errorMessage);
return {
content: [
{
type: 'text',
text: `ā Tool execution failed: ${errorMessage}`,
},
],
isError: true,
};
}
});
// Main function to start the server
async function main(): Promise<void> {
console.error('š [DEBUG] Main function starting...');
// Setup process cleanup handlers
console.error('š [DEBUG] Setting up process cleanup handlers...');
setupProcessCleanup(async () => {
console.error('š [DEBUG] Process cleanup triggered');
await closeBrowser();
await forceKillAllChromeProcesses();
});
// Create and start the server transport
console.error('š [DEBUG] Creating StdioServerTransport...');
const transport = new StdioServerTransport();
console.error('š [DEBUG] StdioServerTransport created successfully');
await withErrorHandling(async () => {
console.error('š [DEBUG] Attempting to connect server to transport...');
await server.connect(transport);
console.error('š [DEBUG] Server connected to transport successfully');
console.error('š Puppeteer Real Browser MCP Server started successfully');
console.error('š Available tools:', TOOLS.map(t => t.name).join(', '));
console.error('š§ Workflow validation: Active');
console.error('š” Content priority mode: Enabled (use get_content for better reliability)');
console.error('š [DEBUG] Server is now ready and waiting for requests...');
// Keep the process alive by maintaining the connection
console.error('š [DEBUG] Maintaining process alive - server will wait for requests');
// Add a heartbeat to confirm the process is still running
const heartbeat = setInterval(() => {
console.error(`š [DEBUG] Heartbeat - Server alive at ${new Date().toISOString()}`);
}, 30000); // Every 30 seconds
// Cleanup heartbeat on process exit
process.on('exit', () => {
console.error('š [DEBUG] Process exiting - clearing heartbeat');
clearInterval(heartbeat);
});
}, 'Failed to start MCP server');
console.error('š [DEBUG] Main function completed - server should be running');
}
// Enhanced error handling with debug info
console.error('š [DEBUG] Setting up error handlers...');
process.on('uncaughtException', (error) => {
console.error(`š [DEBUG] Uncaught exception at ${new Date().toISOString()}`);
console.error('ā Uncaught exception:', error);
console.error(`š [DEBUG] Stack trace:`, error.stack);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error(`š [DEBUG] Unhandled rejection at ${new Date().toISOString()}`);
console.error('ā Unhandled rejection:', reason);
console.error(`š [DEBUG] Promise:`, promise);
process.exit(1);
});
// Process lifecycle debugging
process.on('exit', (code) => {
console.error(`š [DEBUG] Process exiting with code: ${code} at ${new Date().toISOString()}`);
});
process.on('beforeExit', (code) => {
console.error(`š [DEBUG] Before exit event with code: ${code} at ${new Date().toISOString()}`);
});
process.on('SIGTERM', () => {
console.error(`š [DEBUG] SIGTERM received at ${new Date().toISOString()}`);
});
process.on('SIGINT', () => {
console.error(`š [DEBUG] SIGINT received at ${new Date().toISOString()}`);
});
console.error('š [DEBUG] All error handlers registered');
// Start the server
console.error('š [DEBUG] Checking if module is main...');
console.error(`š [DEBUG] import.meta.url: ${import.meta.url}`);
console.error(`š [DEBUG] process.argv[1]: ${process.argv[1]}`);
console.error(`š [DEBUG] process.argv[0]: ${process.argv[0]}`);
// Enhanced main module detection for npx compatibility
const isMain = import.meta.url === `file://${process.argv[1]}` ||
process.argv[1].includes('puppeteer-real-browser-mcp-server') ||
process.argv[1].endsWith('.bin/puppeteer-real-browser-mcp-server') ||
process.argv.some(arg => arg.includes('puppeteer-real-browser-mcp-server'));
console.error(`š [DEBUG] Enhanced main detection result: ${isMain}`);
if (isMain) {
console.error('š [DEBUG] Module is main - starting server...');
main().catch((error) => {
console.error(`š [DEBUG] Main function failed at ${new Date().toISOString()}`);
console.error('ā Failed to start server:', error);
console.error(`š [DEBUG] Error stack:`, error.stack);
process.exit(1);
});
} else {
console.error('š [DEBUG] Module is not main - not starting server');
console.error('š [DEBUG] FORCE STARTING - This is likely an npx execution');
main().catch((error) => {
console.error(`š [DEBUG] Forced main function failed at ${new Date().toISOString()}`);
console.error('ā Failed to start server:', error);
console.error(`š [DEBUG] Error stack:`, error.stack);
process.exit(1);
});
}