Skip to main content
Glama

analyze_resources

Audit website resources (images, JS, CSS, fonts) to identify optimization opportunities, improve performance, and streamline content delivery for specified devices.

Instructions

Analyze website resources (images, JS, CSS, fonts) for optimization opportunities

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
deviceNoDevice to emulate (default: desktop)desktop
minSizeNoMinimum resource size in KB to include
resourceTypesNoTypes of resources to analyze
urlYesURL to audit

Implementation Reference

  • Core handler function that runs Lighthouse audit, extracts network requests, categorizes resources by type, filters by size/types, computes summary statistics
    export async function analyzeResources( url: string, device: "desktop" | "mobile" = "desktop", resourceTypes?: string[], minSize = DEFAULTS.MIN_RESOURCE_SIZE_KB, ) { const runnerResult = await 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: Record<string, unknown>) => { const sizeKB = ((item.transferSize as number) || 0) / 1024; const resourceType = categorizeResourceType(item); return { url: item.url as string, resourceType, transferSize: (item.transferSize as number) || 0, resourceSize: (item.resourceSize as number) || 0, sizeKB: Math.round(sizeKB * 100) / 100, mimeType: item.mimeType as string, }; }) .filter((resource: { sizeKB: number; resourceType: string }) => { 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: Record<string, { count: number; totalSize: number }>, resource: { resourceType: string; transferSize: number }, ) => { 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, }; }
  • Input schema (Zod) for the analyze_resources tool defining parameters: url, device, resourceTypes, minSize
    export const resourceAnalysisSchema = { url: baseSchemas.url, device: baseSchemas.device, resourceTypes: z .array(z.enum(["images", "javascript", "css", "fonts", "other"])) .optional() .describe("Types of resources to analyze"), minSize: z.number().min(0).optional().describe("Minimum resource size in KB to include"), };
  • MCP tool registration for 'analyze_resources': sets name, description, schema, and inline handler that calls core analysis and returns formatted content with optimizations/recommendations
    server.tool( "analyze_resources", "Analyze website resources (images, JS, CSS, fonts) for optimization opportunities", resourceAnalysisSchema, async ({ url, device, resourceTypes, minSize }) => { try { const result = await analyzeResources(url, device, resourceTypes, minSize); // Create structured, AI-friendly response const analysisData = { url: result.url, device: result.device, timestamp: result.fetchTime, filters: { resourceTypes: resourceTypes || ["all"], minSizeKB: minSize || 0, }, summary: { totalResources: result.resources.length, totalSizeKB: Math.round((Object.values(result.summary).reduce((sum, data) => sum + data.totalSize, 0) / 1024) * 100) / 100, resourceCounts: Object.fromEntries( Object.entries(result.summary).map(([type, data]) => [ type, { count: data.count, sizeKB: Math.round((data.totalSize / 1024) * 100) / 100, }, ]), ), }, resources: result.resources.slice(0, 50).map((resource) => ({ filename: resource.url.split("/").pop() || resource.url, type: resource.resourceType, sizeKB: Math.round(resource.sizeKB * 100) / 100, mimeType: resource.mimeType || "unknown", url: resource.url, })), optimization: { recommendations: [] as string[], priorities: [] as string[], }, }; // Add type-specific recommendations if (result.summary.images) { analysisData.optimization.recommendations.push("Convert images to WebP/AVIF formats"); analysisData.optimization.recommendations.push("Implement lazy loading for images"); analysisData.optimization.priorities.push("images"); } if (result.summary.javascript) { analysisData.optimization.recommendations.push("Minify and compress JavaScript files"); analysisData.optimization.recommendations.push("Remove unused JavaScript code"); analysisData.optimization.priorities.push("javascript"); } if (result.summary.css) { analysisData.optimization.recommendations.push("Minify CSS and remove unused styles"); analysisData.optimization.priorities.push("css"); } if (result.summary.fonts) { analysisData.optimization.recommendations.push("Use font-display: swap for better loading"); analysisData.optimization.priorities.push("fonts"); } if (analysisData.optimization.recommendations.length === 0) { analysisData.optimization.recommendations.push("Resource usage appears optimized"); } // Add truncation info if needed if (result.resources.length > 50) { analysisData.resources.push({ filename: `... and ${result.resources.length - 50} more resources`, type: "truncated", sizeKB: 0, mimeType: "info", url: "", }); } const summary = `Analyzed ${result.resources.length} resources (${analysisData.summary.totalSizeKB}KB total) - ${analysisData.optimization.recommendations.length} optimization opportunities found`; return { content: createStructuredAnalysis("Resource Analysis", analysisData, summary), }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text" as const, text: `Analysis failed: ${errorMessage}`, }, { type: "text" as const, text: JSON.stringify( { error: true, url, device, resourceTypes, minSize, message: errorMessage, timestamp: new Date().toISOString(), }, null, 2, ), }, ], isError: true, }; } }, );
  • Helper function to categorize Lighthouse network resources into types: images, javascript, css, fonts, other based on resourceType or mimeType
    function categorizeResourceType(item: Record<string, unknown>): string { if (item.resourceType) { return (item.resourceType as string).toLowerCase(); } if (item.mimeType) { const mimeType = item.mimeType as string; 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"; }

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/danielsogl/lighthouse-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server