@danielsogl/lighthouse-mcp
Version:
A comprehensive Model Context Protocol (MCP) server that provides web performance auditing, accessibility testing, SEO analysis, security assessment, and Core Web Vitals monitoring using Google Lighthouse. Enables LLMs and AI agents to perform detailed we
250 lines (249 loc) • 10.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerPerformanceTools = registerPerformanceTools;
const schemas_1 = require("../schemas");
const lighthouse_performance_1 = require("../lighthouse-performance");
function createStructuredPerformance(type, url, device, data, recommendations) {
return {
summary: `${type} analysis for ${url} on ${device}`,
data,
...(recommendations && { recommendations }),
};
}
function registerPerformanceTools(server) {
server.tool("get_performance_score", "Get the performance score for a website", schemas_1.basicAuditSchema, async ({ url, device }) => {
try {
const result = await (0, lighthouse_performance_1.getPerformanceScore)(url, device);
const structuredResult = createStructuredPerformance("Performance Score", result.url, result.device, {
performanceScore: result.performanceScore,
metrics: Object.fromEntries(Object.entries(result.metrics).map(([key, metric]) => [
key,
{
title: metric.title,
value: metric.displayValue,
score: metric.score,
},
])),
fetchTime: result.fetchTime,
}, [
"Focus on Core Web Vitals improvements",
"Optimize largest contentful paint for better user experience",
"Reduce total blocking time to improve interactivity",
]);
return {
content: [
{
type: "text",
text: JSON.stringify(structuredResult, null, 2),
},
],
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Performance analysis failed",
url,
device: device || "desktop",
message: errorMessage,
}, null, 2),
},
],
isError: true,
};
}
});
server.tool("get_core_web_vitals", "Get Core Web Vitals metrics for a website", schemas_1.coreWebVitalsSchema, async ({ url, device, includeDetails, threshold }) => {
try {
const result = await (0, lighthouse_performance_1.getCoreWebVitals)(url, device, threshold);
const structuredResult = createStructuredPerformance("Core Web Vitals", result.url, result.device, {
coreWebVitals: Object.fromEntries(Object.entries(result.coreWebVitals).map(([key, metric]) => [
key,
{
title: metric?.title || key.toUpperCase(),
value: metric?.displayValue || "N/A",
score: metric?.score,
},
])),
thresholdResults: result.thresholdResults || {},
fetchTime: result.fetchTime,
includeDetails,
}, [
"Optimize Largest Contentful Paint (LCP) < 2.5s",
"Minimize First Input Delay (FID) < 100ms",
"Reduce Cumulative Layout Shift (CLS) < 0.1",
]);
return {
content: [
{
type: "text",
text: JSON.stringify(structuredResult, null, 2),
},
],
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Core Web Vitals analysis failed",
url,
device: device || "desktop",
message: errorMessage,
}, null, 2),
},
],
isError: true,
};
}
});
server.tool("compare_mobile_desktop", "Compare website performance between mobile and desktop devices", schemas_1.compareDevicesSchema, async ({ url, categories, throttling, includeDetails }) => {
try {
const result = await (0, lighthouse_performance_1.compareMobileDesktop)(url, categories, throttling);
const structuredResult = createStructuredPerformance("Mobile vs Desktop Comparison", result.url, "mobile + desktop", {
differences: Object.fromEntries(Object.entries(result.differences).map(([category, diff]) => [
category,
{
mobile: diff.mobile,
desktop: diff.desktop,
difference: diff.difference,
better: diff.difference > 0 ? "desktop" : "mobile",
},
])),
includeDetails,
}, [
"Mobile performance typically requires more optimization",
"Focus on image optimization for mobile devices",
"Consider implementing responsive design best practices",
]);
return {
content: [
{
type: "text",
text: JSON.stringify(structuredResult, null, 2),
},
],
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Mobile vs Desktop comparison failed",
url,
message: errorMessage,
}, null, 2),
},
],
isError: true,
};
}
});
server.tool("check_performance_budget", "Check if website performance meets specified budget thresholds", schemas_1.performanceBudgetSchema, async ({ url, device, budget }) => {
try {
const result = await (0, lighthouse_performance_1.checkPerformanceBudget)(url, device, budget);
const structuredResult = createStructuredPerformance("Performance Budget Check", result.url, result.device, {
overallPassed: result.overallPassed,
results: Object.fromEntries(Object.entries(result.results).map(([metric, data]) => [
metric,
{
actual: data.actual,
budget: data.budget,
unit: data.unit,
passed: data.passed,
difference: typeof data.actual === "number" && typeof data.budget === "number"
? data.actual - data.budget
: null,
},
])),
fetchTime: result.fetchTime,
}, result.overallPassed
? ["Performance budget requirements met"]
: [
"Review failing metrics and optimize accordingly",
"Consider adjusting budget thresholds if realistic",
"Focus on the metrics with largest budget overruns",
]);
return {
content: [
{
type: "text",
text: JSON.stringify(structuredResult, null, 2),
},
],
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Performance budget check failed",
url,
device: device || "desktop",
message: errorMessage,
}, null, 2),
},
],
isError: true,
};
}
});
server.tool("get_lcp_opportunities", "Get LCP optimization opportunities for a website", schemas_1.lcpOpportunitiesSchema, async ({ url, device, threshold, includeDetails }) => {
try {
const result = await (0, lighthouse_performance_1.getLcpOpportunities)(url, device, threshold);
const structuredResult = createStructuredPerformance("LCP Optimization Opportunities", result.url, result.device, {
lcpValue: result.lcpValue,
threshold: result.threshold,
needsImprovement: result.needsImprovement,
opportunities: result.opportunities || [],
fetchTime: result.fetchTime,
includeDetails,
}, !result.needsImprovement
? ["LCP performance is within acceptable range"]
: [
"Optimize image loading and compression",
"Implement resource hints (preload, prefetch)",
"Reduce server response times",
"Minimize render-blocking resources",
]);
return {
content: [
{
type: "text",
text: JSON.stringify(structuredResult, null, 2),
},
],
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "LCP opportunities analysis failed",
url,
device: device || "desktop",
message: errorMessage,
}, null, 2),
},
],
isError: true,
};
}
});
}