@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
616 lines (615 loc) • 30.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerResources = registerResources;
/**
* Register useful resources for the Lighthouse MCP server
* Resources provide read-only reference data that can be useful for analysis
*/
function registerResources(server) {
// Performance thresholds and guidelines
server.resource("core-web-vitals-thresholds", "lighthouse://performance/core-web-vitals-thresholds", async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
lcp: {
good: { max: 2.5, unit: "seconds" },
needsImprovement: { min: 2.5, max: 4.0, unit: "seconds" },
poor: { min: 4.0, unit: "seconds" },
},
fid: {
good: { max: 100, unit: "milliseconds" },
needsImprovement: { min: 100, max: 300, unit: "milliseconds" },
poor: { min: 300, unit: "milliseconds" },
},
cls: {
good: { max: 0.1, unit: "score" },
needsImprovement: { min: 0.1, max: 0.25, unit: "score" },
poor: { min: 0.25, unit: "score" },
},
fcp: {
good: { max: 1.8, unit: "seconds" },
needsImprovement: { min: 1.8, max: 3.0, unit: "seconds" },
poor: { min: 3.0, unit: "seconds" },
},
tbt: {
good: { max: 200, unit: "milliseconds" },
needsImprovement: { min: 200, max: 600, unit: "milliseconds" },
poor: { min: 600, unit: "milliseconds" },
},
speedIndex: {
good: { max: 3.4, unit: "seconds" },
needsImprovement: { min: 3.4, max: 5.8, unit: "seconds" },
poor: { min: 5.8, unit: "seconds" },
},
}, null, 2),
},
],
}));
// Performance optimization techniques
server.resource("optimization-techniques", "lighthouse://performance/optimization-techniques", async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
images: {
techniques: [
{
name: "Use modern image formats (WebP, AVIF)",
impact: "high",
savings: "25-50% file size reduction",
difficulty: "medium",
},
{
name: "Implement lazy loading",
impact: "high",
savings: "Faster initial page load",
difficulty: "easy",
},
{
name: "Optimize image dimensions",
impact: "medium",
savings: "10-30% file size reduction",
difficulty: "easy",
},
{
name: "Use responsive images with srcset",
impact: "medium",
savings: "Improved mobile performance",
difficulty: "medium",
},
],
},
javascript: {
techniques: [
{
name: "Code splitting and lazy loading",
impact: "high",
savings: "Reduced initial bundle size",
difficulty: "medium",
},
{
name: "Tree shaking unused code",
impact: "medium",
savings: "10-40% bundle size reduction",
difficulty: "easy",
},
{
name: "Minification and compression",
impact: "medium",
savings: "20-30% file size reduction",
difficulty: "easy",
},
{
name: "Use modern bundling tools",
impact: "medium",
savings: "Better optimization",
difficulty: "medium",
},
],
},
css: {
techniques: [
{
name: "Remove unused CSS",
impact: "medium",
savings: "20-50% file size reduction",
difficulty: "medium",
},
{
name: "Critical CSS inlining",
impact: "high",
savings: "Faster first paint",
difficulty: "hard",
},
{
name: "CSS minification",
impact: "low",
savings: "10-20% file size reduction",
difficulty: "easy",
},
],
},
caching: {
techniques: [
{
name: "Implement proper cache headers",
impact: "high",
savings: "Eliminated repeat downloads",
difficulty: "medium",
},
{
name: "Use CDN for static assets",
impact: "high",
savings: "Reduced latency",
difficulty: "medium",
},
{
name: "Service worker caching",
impact: "high",
savings: "Offline capability",
difficulty: "hard",
},
],
},
}, null, 2),
},
],
}));
// Accessibility guidelines and WCAG compliance
server.resource("wcag-guidelines", "lighthouse://accessibility/wcag-guidelines", async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
principles: {
perceivable: {
description: "Information must be presentable in ways users can perceive",
guidelines: [
"Provide text alternatives for images",
"Provide captions for videos",
"Ensure sufficient color contrast",
"Make content adaptable to different presentations",
],
},
operable: {
description: "Interface components must be operable",
guidelines: [
"Make all functionality keyboard accessible",
"Give users enough time to read content",
"Don't use content that causes seizures",
"Help users navigate and find content",
],
},
understandable: {
description: "Information and UI operation must be understandable",
guidelines: [
"Make text readable and understandable",
"Make content appear and operate predictably",
"Help users avoid and correct mistakes",
],
},
robust: {
description: "Content must be robust enough for interpretation by assistive technologies",
guidelines: [
"Maximize compatibility with assistive technologies",
"Use valid, semantic HTML",
"Ensure content works across different browsers",
],
},
},
commonIssues: [
{
issue: "Missing alt text on images",
severity: "high",
impact: "Screen readers cannot describe images",
solution: "Add descriptive alt attributes to all images",
},
{
issue: "Insufficient color contrast",
severity: "medium",
impact: "Text may be hard to read for users with vision impairments",
solution: "Ensure contrast ratio of at least 4.5:1 for normal text",
},
{
issue: "Missing form labels",
severity: "high",
impact: "Users cannot understand form inputs",
solution: "Associate labels with form controls using for/id attributes",
},
{
issue: "No keyboard navigation",
severity: "high",
impact: "Users cannot navigate without a mouse",
solution: "Ensure all interactive elements are keyboard accessible",
},
],
}, null, 2),
},
],
}));
// SEO best practices and guidelines
server.resource("seo-best-practices", "lighthouse://seo/best-practices", async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
technical: {
meta: [
{
element: "title",
requirement: "50-60 characters",
importance: "critical",
description: "Primary heading displayed in search results",
},
{
element: "meta description",
requirement: "150-160 characters",
importance: "high",
description: "Summary displayed in search results",
},
{
element: "meta viewport",
requirement: "Must be present",
importance: "critical",
description: "Required for mobile-friendly pages",
},
],
structure: [
{
element: "heading hierarchy",
requirement: "Logical H1-H6 structure",
importance: "medium",
description: "Helps search engines understand content structure",
},
{
element: "semantic HTML",
requirement: "Use appropriate HTML5 elements",
importance: "medium",
description: "Improves content understanding",
},
],
performance: [
{
factor: "page speed",
impact: "high",
description: "Fast-loading pages rank higher",
},
{
factor: "mobile-friendliness",
impact: "critical",
description: "Mobile-first indexing is standard",
},
{
factor: "HTTPS",
impact: "medium",
description: "Security is a ranking factor",
},
],
},
content: {
optimization: [
{
technique: "Keyword optimization",
description: "Use relevant keywords naturally in content",
avoid: "Keyword stuffing or over-optimization",
},
{
technique: "Internal linking",
description: "Link to related content within your site",
benefit: "Helps search engines discover and understand content",
},
{
technique: "Schema markup",
description: "Add structured data to help search engines understand content",
benefit: "Can lead to rich snippets in search results",
},
],
},
}, null, 2),
},
],
}));
// Security best practices and common vulnerabilities
server.resource("security-best-practices", "lighthouse://security/best-practices", async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
https: {
importance: "critical",
description: "Encrypt all data in transit",
implementation: [
"Use TLS 1.2 or higher",
"Implement HTTP Strict Transport Security (HSTS)",
"Redirect all HTTP traffic to HTTPS",
"Use secure cookies with Secure flag",
],
},
headers: {
"Content-Security-Policy": {
purpose: "Prevent XSS attacks",
example: "default-src 'self'; script-src 'self' 'unsafe-inline'",
importance: "high",
},
"X-Frame-Options": {
purpose: "Prevent clickjacking",
example: "DENY or SAMEORIGIN",
importance: "medium",
},
"X-Content-Type-Options": {
purpose: "Prevent MIME type sniffing",
example: "nosniff",
importance: "medium",
},
"Referrer-Policy": {
purpose: "Control referrer information",
example: "strict-origin-when-cross-origin",
importance: "low",
},
},
commonVulnerabilities: [
{
name: "Cross-Site Scripting (XSS)",
prevention: "Sanitize user input, use CSP headers",
severity: "high",
},
{
name: "Cross-Site Request Forgery (CSRF)",
prevention: "Use CSRF tokens, SameSite cookies",
severity: "medium",
},
{
name: "Insecure Dependencies",
prevention: "Regular dependency updates, vulnerability scanning",
severity: "variable",
},
{
name: "Mixed Content",
prevention: "Ensure all resources load over HTTPS",
severity: "medium",
},
],
}, null, 2),
},
],
}));
// Performance budgets and monitoring guidelines
server.resource("budget-guidelines", "lighthouse://performance/budget-guidelines", async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
ecommerce: {
description: "Online stores and retail websites",
priorities: ["LCP", "CLS", "FID"],
budgets: {
lcp: { target: 2.0, warning: 2.5, critical: 4.0 },
cls: { target: 0.05, warning: 0.1, critical: 0.25 },
fid: { target: 50, warning: 100, critical: 300 },
totalPageSize: { target: 1000, warning: 1500, critical: 2000, unit: "KB" },
javascriptSize: { target: 300, warning: 500, critical: 800, unit: "KB" },
},
},
content: {
description: "Blogs, news sites, and content-heavy websites",
priorities: ["LCP", "FCP", "Speed Index"],
budgets: {
lcp: { target: 2.5, warning: 3.0, critical: 4.0 },
fcp: { target: 1.5, warning: 2.0, critical: 3.0 },
speedIndex: { target: 3.0, warning: 4.0, critical: 5.8 },
totalPageSize: { target: 800, warning: 1200, critical: 1800, unit: "KB" },
imageSize: { target: 400, warning: 600, critical: 1000, unit: "KB" },
},
},
application: {
description: "Web applications and SaaS platforms",
priorities: ["FID", "TBT", "CLS"],
budgets: {
fid: { target: 50, warning: 100, critical: 300 },
tbt: { target: 150, warning: 300, critical: 600 },
cls: { target: 0.05, warning: 0.1, critical: 0.25 },
javascriptSize: { target: 500, warning: 800, critical: 1200, unit: "KB" },
bundleCount: { target: 5, warning: 10, critical: 15 },
},
},
mobile: {
description: "Mobile-first or mobile-only experiences",
priorities: ["LCP", "FID", "CLS"],
budgets: {
lcp: { target: 2.0, warning: 2.5, critical: 4.0 },
fid: { target: 50, warning: 100, critical: 300 },
cls: { target: 0.05, warning: 0.1, critical: 0.25 },
totalPageSize: { target: 500, warning: 800, critical: 1200, unit: "KB" },
imageSize: { target: 200, warning: 400, critical: 600, unit: "KB" },
},
},
}, null, 2),
},
],
}));
// Lighthouse audit categories and scoring
server.resource("categories-scoring", "lighthouse://audits/categories-scoring", async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
categories: {
performance: {
description: "How fast your page loads and renders",
weight: "Most important for user experience",
keyMetrics: [
{ name: "First Contentful Paint", weight: 10, description: "Time to first visual content" },
{ name: "Largest Contentful Paint", weight: 25, description: "Time to largest visual element" },
{ name: "First Input Delay", weight: 25, description: "Time to first user interaction" },
{ name: "Cumulative Layout Shift", weight: 25, description: "Visual stability during loading" },
{ name: "Total Blocking Time", weight: 10, description: "Time blocked from user interaction" },
{ name: "Speed Index", weight: 5, description: "How quickly page contents are visually populated" },
],
},
accessibility: {
description: "How accessible your page is to users with disabilities",
weight: "Critical for inclusive design",
keyAreas: [
"Color contrast and readability",
"Keyboard navigation support",
"Screen reader compatibility",
"Focus management",
"ARIA attributes and semantic HTML",
],
},
seo: {
description: "How well your page can be discovered and crawled",
weight: "Important for visibility",
keyAreas: [
"Meta tags and structured data",
"Mobile-friendliness",
"Page load performance",
"Content structure and hierarchy",
"Crawlability and indexability",
],
},
bestPractices: {
description: "General web development best practices",
weight: "Foundation for good websites",
keyAreas: [
"Security (HTTPS, CSP)",
"Modern web standards",
"Browser compatibility",
"Error handling",
"Development practices",
],
},
pwa: {
description: "Progressive Web App capabilities",
weight: "Enhanced user experience",
keyAreas: [
"Service worker implementation",
"Offline functionality",
"App manifest",
"Installability",
"Mobile app-like experience",
],
},
},
scoring: {
methodology: "Lighthouse uses a weighted scoring system",
ranges: {
good: { min: 90, max: 100, color: "green" },
needsImprovement: { min: 50, max: 89, color: "orange" },
poor: { min: 0, max: 49, color: "red" },
},
interpretation: {
"90-100": "Excellent - page follows best practices",
"50-89": "Room for improvement - some issues to address",
"0-49": "Poor - significant issues need attention",
},
},
}, null, 2),
},
],
}));
// Common web technologies and frameworks optimization
server.resource("framework-guides", "lighthouse://frameworks/optimization-guides", async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
react: {
name: "React",
optimizations: [
{
technique: "Code splitting with React.lazy()",
impact: "Reduces initial bundle size",
implementation: "Use dynamic imports and Suspense",
},
{
technique: "Optimize re-renders with useMemo/useCallback",
impact: "Improves runtime performance",
implementation: "Memoize expensive calculations and callbacks",
},
{
technique: "Use React.memo for component optimization",
impact: "Prevents unnecessary re-renders",
implementation: "Wrap components that receive stable props",
},
{
technique: "Server-side rendering (Next.js)",
impact: "Faster initial page load",
implementation: "Use Next.js or similar SSR framework",
},
],
},
vue: {
name: "Vue.js",
optimizations: [
{
technique: "Async components and code splitting",
impact: "Reduces initial bundle size",
implementation: "Use dynamic imports in route definitions",
},
{
technique: "Vue 3 Composition API",
impact: "Better tree-shaking and performance",
implementation: "Migrate to Composition API where beneficial",
},
{
technique: "Nuxt.js for SSR/SSG",
impact: "Improved SEO and initial load",
implementation: "Use Nuxt.js for static site generation",
},
],
},
angular: {
name: "Angular",
optimizations: [
{
technique: "Lazy loading modules",
impact: "Smaller initial bundles",
implementation: "Use loadChildren in route configuration",
},
{
technique: "OnPush change detection",
impact: "Improved runtime performance",
implementation: "Use OnPush strategy where appropriate",
},
{
technique: "Angular Universal for SSR",
impact: "Better SEO and initial load",
implementation: "Implement server-side rendering",
},
],
},
vanilla: {
name: "Vanilla JavaScript",
optimizations: [
{
technique: "Modern ES modules",
impact: "Better tree-shaking",
implementation: "Use import/export syntax",
},
{
technique: "Web Components",
impact: "Reusable, encapsulated components",
implementation: "Use Custom Elements API",
},
{
technique: "Service Workers",
impact: "Offline functionality and caching",
implementation: "Implement caching strategies",
},
],
},
}, null, 2),
},
],
}));
}