#!/usr/bin/env node
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import express from 'express';
import { createServer } from './server.js';
import { closeBrowser } from './browser.js';
import { closeAllTabs } from './tabs.js';
/**
* Start the MCP server with stdio transport
*/
async function startStdioServer(): Promise<void> {
const server = createServer();
const transport = new StdioServerTransport();
await server.connect(transport);
// Log to stderr so it doesn't interfere with MCP protocol
console.error('[puppeteer-mcp] Server started with stdio transport');
}
/**
* Start the MCP server with HTTP transport
*/
async function startHttpServer(port: number): Promise<void> {
const { StreamableHTTPServerTransport } = await import(
'@modelcontextprotocol/sdk/server/streamableHttp.js'
);
const server = createServer();
const app = express();
app.use(express.json());
// Create a single transport for the server
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
await server.connect(transport);
// MCP endpoint
app.post('/mcp', async (req, res) => {
try {
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error('[puppeteer-mcp] Error handling request:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Health check endpoint
app.get('/health', (_req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
service: 'puppeteer-mcp',
});
});
app.listen(port, () => {
console.log(`[puppeteer-mcp] Server started with HTTP transport on port ${port}`);
console.log(`[puppeteer-mcp] MCP endpoint: http://localhost:${port}/mcp`);
console.log(`[puppeteer-mcp] Health check: http://localhost:${port}/health`);
});
}
/**
* Cleanup on exit
*/
async function cleanup(): Promise<void> {
console.error('[puppeteer-mcp] Shutting down...');
await closeAllTabs();
await closeBrowser();
console.error('[puppeteer-mcp] Cleanup complete');
}
/**
* Main entry point
*/
async function main(): Promise<void> {
// Parse command line arguments
const args = process.argv.slice(2);
const isHttpMode = args.includes('--http');
const portArg = args.find((arg) => arg.startsWith('--port='));
const port = portArg ? parseInt(portArg.split('=')[1] ?? '3000', 10) : parseInt(process.env.PORT ?? '3000', 10);
// Register cleanup handlers
process.on('SIGINT', async () => {
await cleanup();
process.exit(0);
});
process.on('SIGTERM', async () => {
await cleanup();
process.exit(0);
});
process.on('uncaughtException', async (error) => {
console.error('[puppeteer-mcp] Uncaught exception:', error);
await cleanup();
process.exit(1);
});
process.on('unhandledRejection', (reason) => {
console.error('[puppeteer-mcp] Unhandled rejection:', reason);
});
// Start server
try {
if (isHttpMode) {
await startHttpServer(port);
} else {
await startStdioServer();
}
} catch (error) {
console.error('[puppeteer-mcp] Failed to start server:', error);
await cleanup();
process.exit(1);
}
}
main().catch((error) => {
console.error('[puppeteer-mcp] Fatal error:', error);
process.exit(1);
});