UNPKG

@bschauer/webtools-mcp-server

Version:

MCP server providing web analysis tools including screenshot, debug, performance, security, accessibility, SEO, and asset optimization capabilities

227 lines (201 loc) 6.56 kB
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, ListPromptsRequestSchema, GetPromptRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { logInfo, logError } from "./utils/logging.js"; import { checkSiteAvailability } from "./utils/html.js"; import { fetchWithRetry } from "./utils/fetch.js"; // Import tool handlers import { getHtml, readPage } from "./tools/html.js"; import { screenshot } from "./tools/screenshot.js"; import { debug } from "./tools/debug.js"; import { runLighthouse } from "./tools/lighthouse.js"; import { performanceTrace } from "./tools/performance/trace/index.js"; import { runCoverageAnalysis } from "./tools/performance/coverage/index.js"; import { runWebVitalsAnalysis } from "./tools/performance/web_vitals/index.js"; import { runNetworkMonitor } from "./tools/performance/network/index.js"; import { runPerformanceTest } from "./tools/performance/test_framework/index.js"; // Import configurations import { TOOL_DEFINITIONS } from "./config/tool-definitions.js"; import { SERVER_CAPABILITIES } from "./config/capabilities.js"; // Import prompts import { PROMPT_DEFINITIONS, PROMPT_HANDLERS } from "./prompts/index.js"; /** * Create and configure the MCP server * @returns {Server} The configured server instance */ export function createServer() { // Create server instance const server = new Server( { name: "webtools-server", version: "1.6.1", }, { capabilities: { ...SERVER_CAPABILITIES, prompts: {}, }, } ); // Set up request handlers setupRequestHandlers(server); // Set up error handling server.onerror = (error) => { logError("server", "Server error", error); }; return server; } /** * Set up request handlers for the server * @param {Server} server - The server instance */ function setupRequestHandlers(server) { // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: TOOL_DEFINITIONS, }; }); // List available prompts server.setRequestHandler(ListPromptsRequestSchema, async () => { return { prompts: PROMPT_DEFINITIONS, }; }); // Get prompt by name server.setRequestHandler(GetPromptRequestSchema, async (request) => { const { name, arguments: args } = request.params; // Find the appropriate handler for this prompt const promptHandler = PROMPT_HANDLERS[name]; if (!promptHandler) { throw new McpError(ErrorCode.MethodNotFound, `Unknown prompt: ${name}`); } try { return promptHandler(args); } catch (error) { throw new McpError(ErrorCode.InvalidParams, error.message); } }); // Handle tool execution server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; logInfo("tool", "Tool execution started", { tool: name, arguments: args, }); try { // Check site availability first if URL is provided if (args.url) { const availability = await checkSiteAvailability(args.url, { ignoreSSLErrors: args.ignoreSSLErrors }, fetchWithRetry); if (!availability.available) { const response = { content: [ { type: "text", text: JSON.stringify( { error: "Site unavailable", details: availability.error, recommendation: availability.recommendation, retryable: true, url: args.url, }, null, 2 ), }, ], }; logError("tool", "Site unavailable", null, { tool: name, url: args.url, availability, }); return response; } } // Execute the appropriate tool let result; switch (name) { case "webtool_gethtml": result = await getHtml(args); break; case "webtool_readpage": result = await readPage(args); break; case "webtool_screenshot": result = await screenshot(args); break; case "webtool_debug": result = await debug(args); break; case "webtool_lighthouse": result = await runLighthouse(args); break; case "webtool_performance_trace": result = await performanceTrace(args); break; case "webtool_coverage_analysis": result = await runCoverageAnalysis(args); break; case "webtool_web_vitals": result = await runWebVitalsAnalysis(args); break; case "webtool_network_monitor": result = await runNetworkMonitor(args); break; case "webtool_performance_test": result = await runPerformanceTest(args); break; default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } logInfo("tool", "Tool execution completed successfully", { tool: name, url: args.url, }); return result; } catch (error) { logError("tool", "Tool execution failed", error, { tool: name, arguments: args, }); return { content: [ { type: "text", text: JSON.stringify( { error: "Operation failed", details: error.message, recommendation: "Please try again later or with different parameters", retryable: true, url: args.url, }, null, 2 ), }, ], }; } }); } /** * Start the server with the specified transport * @param {Server} server - The server instance * @returns {Promise<void>} A promise that resolves when the server is started */ export async function startServer(server) { const transport = new StdioServerTransport(); await server.connect(transport); logInfo("server", "Server running", { transport: "stdio", }); // Handle graceful shutdown process.on("SIGINT", async () => { logInfo("server", "Shutting down gracefully"); await server.close(); process.exit(0); }); }