#
tokens: 3358/50000 6/6 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── index.ts
├── LICENSE
├── package copy.json
├── package-lock.json
├── package.json
├── README.md
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | node_modules/
2 | build/
3 | .env
4 | *.log
5 | .DS_Store
6 | .idea/
7 | .vscode/
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # @enemyrr/mcp-server-pagespeed
 2 | 
 3 | A Model Context Protocol server that provides Google PageSpeed Insights analysis. This server enables AI models to analyze webpage performance through a standardized interface.
 4 | 
 5 | <a href="https://glama.ai/mcp/servers/wes81w8il2"><img width="380" height="200" src="https://glama.ai/mcp/servers/wes81w8il2/badge" alt="Server Pagespeed MCP server" /></a>
 6 | 
 7 | ## Installation & Setup for Cursor IDE
 8 | 
 9 | 1. Clone and build the project:
10 | ```bash
11 | git clone https://github.com/enemyrr/mcp-server-pagespeed.git
12 | cd mcp-server-pagespeed
13 | npm install
14 | npm run build
15 | ```
16 | 
17 | 2. Add the server in Cursor IDE settings:
18 |    - Open Command Palette (Cmd/Ctrl + Shift + P)
19 |    - Search for "MCP: Add Server"
20 |    - Fill in the fields:
21 |      - Name: `pagespeed`
22 |      - Type: `command`
23 |      - Command: `node /absolute/path/to/mcp-server-pagespeed/build/index.js`
24 | 
25 | > **Note**: Replace `/absolute/path/to/` with the actual path where you cloned and built the project.
26 | 
27 | ## Command-line Usage
28 | 
29 | Just run:
30 | 
31 | ```bash
32 | npx mcp-server-pagespeed
33 | ```
34 | 
35 | ## Available Tools
36 | 
37 | ### analyze_pagespeed
38 | Analyze a webpage using Google PageSpeed Insights API.
39 | 
40 | ```typescript
41 | use_mcp_tool({
42 |   server_name: "pagespeed",
43 |   tool_name: "analyze_pagespeed",
44 |   arguments: {
45 |     url: "https://example.com"
46 |   }
47 | });
48 | ```
49 | 
50 | The tool returns:
51 | - Overall performance score (0-100)
52 | - Loading experience metrics
53 |   - First Contentful Paint
54 |   - First Input Delay
55 | - Top 5 improvement suggestions with:
56 |   - Title
57 |   - Description
58 |   - Potential impact
59 |   - Current value
60 | 
61 | ## Features
62 | 
63 | - Real-time webpage performance analysis
64 | - Detailed loading experience metrics
65 | - Prioritized improvement suggestions
66 | - Comprehensive error handling
67 | - TypeScript support
68 | 
69 | ## Error Handling
70 | 
71 | The server provides detailed error messages for:
72 | - Invalid URLs
73 | - API request failures
74 | - Connection issues
75 | - Invalid tool calls
76 | 
77 | ## Contributing
78 | 
79 | Contributions are welcome! Please feel free to submit a Pull Request to https://github.com/enemyrr/mcp-server-pagespeed
80 | 
81 | ## License
82 | 
83 | MIT 
84 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "es2020",
 4 |     "module": "es2020",
 5 |     "moduleResolution": "node",
 6 |     "outDir": "./build",
 7 |     "rootDir": "./",
 8 |     "strict": true,
 9 |     "esModuleInterop": true,
10 |     "skipLibCheck": true,
11 |     "forceConsistentCasingInFileNames": true
12 |   },
13 |   "include": [
14 |     "*.ts"
15 |   ],
16 |   "exclude": [
17 |     "node_modules"
18 |   ]
19 | } 
```

--------------------------------------------------------------------------------
/package copy.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "@enemyrr/mcp-mysql-server",
 3 |   "version": "0.1.0",
 4 |   "description": "A Model Context Protocol server for MySQL database operations",
 5 |   "type": "module",
 6 |   "bin": {
 7 |     "mcp-mysql": "./build/index.js"
 8 |   },
 9 |   "files": [
10 |     "build"
11 |   ],
12 |   "scripts": {
13 |     "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
14 |     "prepare": "npm run build",
15 |     "watch": "tsc --watch",
16 |     "inspector": "npx @modelcontextprotocol/inspector build/index.js"
17 |   },
18 |   "keywords": [
19 |     "mcp",
20 |     "model-context-protocol",
21 |     "mysql",
22 |     "database",
23 |     "claude",
24 |     "anthropic"
25 |   ],
26 |   "author": "enemyrr",
27 |   "license": "MIT",
28 |   "dependencies": {
29 |     "@modelcontextprotocol/sdk": "0.6.0",
30 |     "dotenv": "^16.4.7",
31 |     "mysql2": "^3.11.5"
32 |   },
33 |   "devDependencies": {
34 |     "@types/node": "^20.11.24",
35 |     "typescript": "^5.3.3"
36 |   },
37 |   "repository": {
38 |     "type": "git",
39 |     "url": "https://github.com/enemyrr/mcp-mysql-server"
40 |   },
41 |   "publishConfig": {
42 |     "access": "public"
43 |   }
44 | }
45 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "mcp-server-pagespeed",
 3 |   "version": "1.0.0",
 4 |   "description": "A Model Context Protocol server for Google PageSpeed Insights",
 5 |   "type": "module",
 6 |   "bin": {
 7 |     "mcp-pagespeed": "./build/index.js"
 8 |   },
 9 |   "files": [
10 |     "build"
11 |   ],
12 |   "main": "build/index.js",
13 |   "scripts": {
14 |     "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
15 |     "prepare": "npm run build",
16 |     "watch": "tsc --watch",
17 |     "start": "node build/index.js",
18 |     "dev": "ts-node --esm index.ts",
19 |     "inspector": "npx @modelcontextprotocol/inspector build/index.js"
20 |   },
21 |   "keywords": [
22 |     "mcp",
23 |     "model-context-protocol",
24 |     "pagespeed",
25 |     "google",
26 |     "performance"
27 |   ],
28 |   "author": "@enemyrr",
29 |   "license": "MIT",
30 |   "dependencies": {
31 |     "@modelcontextprotocol/sdk": "0.6.0",
32 |     "axios": "^1.6.7"
33 |   },
34 |   "devDependencies": {
35 |     "@types/node": "^20.11.19",
36 |     "ts-node": "^10.9.2",
37 |     "typescript": "^5.3.3"
38 |   },
39 |   "repository": {
40 |     "type": "git",
41 |     "url": "https://github.com/enemyrr/mcp-server-pagespeed"
42 |   },
43 |   "publishConfig": {
44 |     "access": "public"
45 |   }
46 | }
47 | 
```

--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
  3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
  4 | import { ErrorCode, ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
  5 | import axios from 'axios';
  6 | 
  7 | interface PageSpeedInsight {
  8 |     score: number;
  9 |     title: string;
 10 |     description: string;
 11 |     displayValue?: string;
 12 | }
 13 | 
 14 | interface ProcessedPageSpeedResult {
 15 |     performanceScore: number;
 16 |     insights: PageSpeedInsight[];
 17 |     loadingExperience: {
 18 |         firstContentfulPaint: {
 19 |             category: string;
 20 |             percentile: number;
 21 |         };
 22 |         firstInputDelay: {
 23 |             category: string;
 24 |             percentile: number;
 25 |         };
 26 |     };
 27 | }
 28 | 
 29 | class PageSpeedServer {
 30 |     private server: Server;
 31 | 
 32 |     constructor() {
 33 |         this.server = new Server(
 34 |             {
 35 |                 name: 'pagespeed-server',
 36 |                 version: '1.0.0',
 37 |             },
 38 |             {
 39 |                 capabilities: {
 40 |                     tools: {},
 41 |                 },
 42 |             }
 43 |         );
 44 | 
 45 |         this.setupToolHandlers();
 46 |         this.server.onerror = (error) => console.error('[MCP Error]', error);
 47 |     }
 48 | 
 49 |     private setupToolHandlers() {
 50 |         this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
 51 |             tools: [
 52 |                 {
 53 |                     name: 'analyze_pagespeed',
 54 |                     description: 'Analyzes a webpage using Google PageSpeed Insights API',
 55 |                     inputSchema: {
 56 |                         type: 'object',
 57 |                         properties: {
 58 |                             url: {
 59 |                                 type: 'string',
 60 |                                 description: 'The URL to analyze'
 61 |                             }
 62 |                         },
 63 |                         required: ['url']
 64 |                     }
 65 |                 }
 66 |             ]
 67 |         }));
 68 | 
 69 |         this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
 70 |             if (request.params.name === 'analyze_pagespeed') {
 71 |                 const { url } = request.params.arguments as { url: string };
 72 | 
 73 |                 try {
 74 |                     const response = await axios.get<any>(
 75 |                         `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodeURIComponent(url)}`
 76 |                     );
 77 | 
 78 |                     const result = response.data;
 79 |                     const processedResult: ProcessedPageSpeedResult = {
 80 |                         performanceScore: Math.round(result.lighthouseResult.categories.performance.score * 100),
 81 |                         insights: [],
 82 |                         loadingExperience: {
 83 |                             firstContentfulPaint: {
 84 |                                 category: result.loadingExperience?.metrics?.FIRST_CONTENTFUL_PAINT_MS?.category || 'N/A',
 85 |                                 percentile: result.loadingExperience?.metrics?.FIRST_CONTENTFUL_PAINT_MS?.percentile || 0
 86 |                             },
 87 |                             firstInputDelay: {
 88 |                                 category: result.loadingExperience?.metrics?.FIRST_INPUT_DELAY_MS?.category || 'N/A',
 89 |                                 percentile: result.loadingExperience?.metrics?.FIRST_INPUT_DELAY_MS?.percentile || 0
 90 |                             }
 91 |                         }
 92 |                     };
 93 | 
 94 |                     // Process audits and extract insights
 95 |                     const audits = result.lighthouseResult.audits;
 96 |                     for (const [key, audit] of Object.entries(audits)) {
 97 |                         const typedAudit = audit as any;
 98 |                         if (typedAudit.score !== null && typedAudit.score < 1) {
 99 |                             processedResult.insights.push({
100 |                                 score: typedAudit.score,
101 |                                 title: typedAudit.title,
102 |                                 description: typedAudit.description,
103 |                                 displayValue: typedAudit.displayValue
104 |                             });
105 |                         }
106 |                     }
107 | 
108 |                     // Sort insights by score (lowest first)
109 |                     processedResult.insights.sort((a, b) => a.score - b.score);
110 | 
111 |                     return {
112 |                         content: [
113 |                             {
114 |                                 type: 'text',
115 |                                 text: JSON.stringify({
116 |                                     summary: `Your page performance score is ${processedResult.performanceScore}/100`,
117 |                                     loadingExperience: {
118 |                                         firstContentfulPaint: `${processedResult.loadingExperience.firstContentfulPaint.category} (${processedResult.loadingExperience.firstContentfulPaint.percentile}ms)`,
119 |                                         firstInputDelay: `${processedResult.loadingExperience.firstInputDelay.category} (${processedResult.loadingExperience.firstInputDelay.percentile}ms)`
120 |                                     },
121 |                                     topImprovements: processedResult.insights.slice(0, 5).map(insight => ({
122 |                                         title: insight.title,
123 |                                         description: insight.description,
124 |                                         impact: Math.round((1 - insight.score) * 100) + '% improvement possible',
125 |                                         currentValue: insight.displayValue
126 |                                     }))
127 |                                 }, null, 2)
128 |                             }
129 |                         ]
130 |                     };
131 |                 } catch (error) {
132 |                     console.error('Error analyzing URL:', error);
133 |                     throw {
134 |                         code: ErrorCode.InternalError,
135 |                         message: 'Failed to analyze URL',
136 |                         details: error instanceof Error ? error.message : 'Unknown error'
137 |                     };
138 |                 }
139 |             }
140 | 
141 |             throw {
142 |                 code: ErrorCode.MethodNotFound,
143 |                 message: `Unknown tool: ${request.params.name}`
144 |             };
145 |         });
146 |     }
147 | 
148 |     async run() {
149 |         const transport = new StdioServerTransport();
150 |         await this.server.connect(transport);
151 |         console.error('PageSpeed MCP server running on stdio');
152 |     }
153 | }
154 | 
155 | const server = new PageSpeedServer();
156 | server.run().catch(console.error); 
```