@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
138 lines (137 loc) • 5.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.findUnusedJavaScript = findUnusedJavaScript;
exports.analyzeResources = analyzeResources;
exports.getSecurityAudit = getSecurityAudit;
const lighthouse_core_1 = require("./lighthouse-core");
const lighthouse_constants_1 = require("./lighthouse-constants");
// Helper function to find unused JavaScript
async function findUnusedJavaScript(url, device = "desktop", minBytes = lighthouse_constants_1.DEFAULTS.MIN_UNUSED_JS_BYTES) {
const runnerResult = await (0, lighthouse_core_1.runRawLighthouseAudit)(url, ["performance"], device);
const { lhr } = runnerResult;
const unusedJsAudit = lhr.audits["unused-javascript"];
if (!unusedJsAudit || !unusedJsAudit.details) {
return {
url: lhr.finalDisplayedUrl,
device,
totalUnusedBytes: 0,
items: [],
fetchTime: lhr.fetchTime,
};
}
// Filter items by minimum bytes
const items = (unusedJsAudit.details.items || [])
.filter((item) => item.wastedBytes >= minBytes)
.map((item) => ({
url: item.url,
totalBytes: item.totalBytes,
wastedBytes: item.wastedBytes,
wastedPercent: Math.round((item.wastedBytes / item.totalBytes) * 100),
}));
const totalUnusedBytes = items.reduce((sum, item) => sum + item.wastedBytes, 0);
return {
url: lhr.finalDisplayedUrl,
device,
totalUnusedBytes,
items,
fetchTime: lhr.fetchTime,
};
}
// Helper function to categorize resource type
function categorizeResourceType(item) {
if (item.resourceType) {
return item.resourceType.toLowerCase();
}
if (item.mimeType) {
const mimeType = item.mimeType;
if (mimeType.startsWith("image/"))
return "images";
if (mimeType.includes("javascript"))
return "javascript";
if (mimeType.includes("css"))
return "css";
if (mimeType.includes("font"))
return "fonts";
}
return "other";
}
// Helper function to analyze resources
async function analyzeResources(url, device = "desktop", resourceTypes, minSize = lighthouse_constants_1.DEFAULTS.MIN_RESOURCE_SIZE_KB) {
const runnerResult = await (0, lighthouse_core_1.runRawLighthouseAudit)(url, ["performance"], device);
const { lhr } = runnerResult;
// Get resource summary from network-requests audit
const networkAudit = lhr.audits["network-requests"];
if (!networkAudit || !networkAudit.details) {
return {
url: lhr.finalDisplayedUrl,
device,
resources: [],
summary: {},
fetchTime: lhr.fetchTime,
};
}
const resources = (networkAudit.details.items || [])
.map((item) => {
const sizeKB = (item.transferSize || 0) / 1024;
const resourceType = categorizeResourceType(item);
return {
url: item.url,
resourceType,
transferSize: item.transferSize || 0,
resourceSize: item.resourceSize || 0,
sizeKB: Math.round(sizeKB * 100) / 100,
mimeType: item.mimeType,
};
})
.filter((resource) => {
if (minSize && resource.sizeKB < minSize)
return false;
if (resourceTypes && !resourceTypes.includes(resource.resourceType))
return false;
return true;
});
// Create summary by resource type
const summary = resources.reduce((acc, resource) => {
if (!acc[resource.resourceType]) {
acc[resource.resourceType] = { count: 0, totalSize: 0 };
}
acc[resource.resourceType].count++;
acc[resource.resourceType].totalSize += resource.transferSize;
return acc;
}, {});
return {
url: lhr.finalDisplayedUrl,
device,
resources,
summary,
fetchTime: lhr.fetchTime,
};
}
// Helper function to get security audit
async function getSecurityAudit(url, device = "desktop", checks) {
const runnerResult = await (0, lighthouse_core_1.runRawLighthouseAudit)(url, ["best-practices"], device);
const { lhr } = runnerResult;
const auditResults = lighthouse_constants_1.SECURITY_AUDITS.map((auditId) => {
const audit = lhr.audits[auditId];
if (audit && (!checks || checks.some((check) => auditId.includes(check)))) {
return {
id: auditId,
title: audit.title,
description: audit.description,
score: audit.score,
scoreDisplayMode: audit.scoreDisplayMode,
displayValue: audit.displayValue,
};
}
return null;
}).filter(Boolean);
const overallScore = auditResults.reduce((sum, audit) => sum + (audit?.score || 0), 0) /
auditResults.length;
return {
url: lhr.finalDisplayedUrl,
device,
overallScore: Math.round(overallScore * 100),
audits: auditResults,
fetchTime: lhr.fetchTime,
};
}