PageSpeed Insights MCP

Installing PageSpeed Insights MCP.

Generated from the project README, so it matches the code  ·  source on GitHub  ·  buy me a coffee

Before you start

You need an API key from Google Cloud. It is free, it takes about a minute, and there is one decision to make, at step 3.

  1. Open console.cloud.google.com and pick your account.

  2. APIs & Services.

  3. API Library in the sidebar. Search for PageSpeed Insights API, select it, press Enable.

    Decide here, while you are on this screen. If you also want real-user data, meaning what actual Chrome visitors experienced and how it has moved over the last six months, search for Chrome UX Report API and enable that as well before moving on. Same key, no second credential, one more search on the screen you are already looking at. Skip it and everything else still works, you just get lab measurements only. Adding it later means coming back to this screen and to step 7, which is the only reason it is worth deciding now.

  4. Credentials, from the sidebar, not from the page you are on.

  5. Create credentials at the top, then API key.

  6. Give it a name.

  7. Under API restrictions, tick PageSpeed Insights API, and tick Chrome UX Report API too if you enabled it at step 3.

  8. Leave Authenticate API calls through a service account unticked.

  9. Leave Application restrictions on None.

  10. Create, then copy the key.

Step 4 is the one that defeats people, and it defeated me. There are two routes to a credentials screen and only one of them offers an API key. Reach it from the page you are already on and the button gives you OAuth clients and service accounts, no API key anywhere, and you reasonably conclude PageSpeed does not support them. It does. The sidebar entry is a different screen, and it sits just below API Library.

Step 9 looks wrong and is not. An HTTP-referrer restriction is the safe-looking choice on that form, and it makes the key unusable from a program like this one, because there is no referring web page. The API restriction at step 7 is the one that limits what the key can reach.

Install

Whichever route you take, clone it somewhere permanent, a folder in your home directory is right. Your assistant's config will point at that exact path, so a folder that later moves is a connection that breaks. Not Downloads, not a temp folder.

Both routes finish the same way. setup.py opens a small page in your browser, served from your own machine on a random port behind a single-use link. It shuts itself down when you are finished and it never writes down anything you type. Paste the key in. It checks the key against Google before saving a thing, and tells you whether real-user data is available.

Run it again any time. It notices you have set it up before, and a blank key field means keep the one you have.

Have an assistant do it

Paste this into an AI assistant that runs shell commands on this computer. Read what it proposes before you let it run.

Please install the PageSpeed Insights MCP server from
https://github.com/Considus/pagespeed-insights-mcp on this computer, following the
Install section of its README exactly. Clone it into a permanent folder in my home
directory, then run setup.py and tell me the local link it prints so I can finish
setup in my browser. Show me each command before you run it.

Or run the commands yourself

Python 3.9 or newer, which macOS and most Linux machines already have. On Windows, install it from python.org or run winget install Python.Python.3.12.

git clone https://github.com/Considus/pagespeed-insights-mcp.git
cd pagespeed-insights-mcp
python3 setup.py

Connect it to your assistant

When setup finishes it hands you a second prompt. Paste that into whichever assistant you want measuring your pages, Claude, Cursor, Windsurf, Zed, Codex CLI, VS Code Copilot, anything that speaks MCP. Restart the app afterwards, MCP servers load at startup.

It works this way round on purpose. Every client keeps its MCP config somewhere different, under a different key, and those locations move. An assistant already knows where its own config lives, so asking it beats shipping a list of paths that quietly rots. The prompt carries no key, only the name, the command and the path.

Tools

ToolWhat it does
check_pagespeedMedian of N distinct analyses with the spread. urls, strategy (mobile, desktop or both), runs (1-10, default 5). Collects rather than counts requests, so 5 takes roughly 150s.
field_dataReal-user data from the Chrome UX Report. urls, and history for the weekly p75 series.
diagnoseWhether the key works and whether the Chrome UX Report is reachable, without disclosing the key.

A 5-analysis check on 2 URLs takes several minutes, and asking harder will not speed it up. Google re-analyses a URL roughly once a minute whatever you do, so the time is spent waiting for genuinely new measurements rather than queuing requests. The server sends a progress update each time a new analysis lands, which is what stops a client giving up on it. If your assistant offers to use runs=1 to be quicker, the answer is no, that is the thing this exists to stop.

Start with diagnose if anything looks wrong. It separates a configuration problem from a slow page in about 2 seconds.

From a terminal

From the folder you cloned into it runs as a module, python3 -m pagespeed_insights. For a pagespeed command that works from anywhere, link the launcher onto your PATH.

mkdir -p ~/.local/bin
ln -s "$PWD/pagespeed" ~/.local/bin/pagespeed

If ~/.local/bin is not on your PATH, add export PATH="$HOME/.local/bin:$PATH" to your shell profile. On Windows there is no symlink step, add the folder to your PATH or keep using python -m pagespeed_insights from inside it.

Setup will show you that command but will not run it. It writes nothing outside its own settings, and a tool that quietly drops executables into a PATH directory is the thing that rule exists to prevent.

pagespeed https://example.com/
pagespeed --runs 3 --strategy both https://example.com/
pagespeed --field --history https://example.com/
pagespeed --json https://example.com/

With no URL it uses whatever you saved during setup.

Exit codes are split by what went wrong, so something running this in CI can tell a broken site apart from a bad afternoon at Google.

CodeMeaning
0fine
1something else went wrong
2bad arguments
3quota exhausted, infrastructure rather than the site
4credential rejected, configuration rather than the site
5page unreachable, this one is the site
6could not reach Google, network rather than the site

Only 5 means the page is at fault. Failing a build on 3 or 6 is failing it because Google was busy.

Where things live

Your key and your saved URLs go in settings.json in your platform's config directory.

  • macOS, ~/Library/Application Support/pagespeed-insights-mcp/

  • Linux, ~/.config/pagespeed-insights-mcp/

  • Windows, %APPDATA%\pagespeed-insights-mcp\

To add, change or remove those saved URLs, run python3 setup.py again from the folder you cloned into. The box comes back with whatever you saved last time, so edit the list and submit it. Empty it and you are back to naming a site every time. Your key is kept unless you type a new one over it. There is no CLI flag for this. The file is small and plain, so editing it by hand works just as well, and deleting it removes the key from the machine.

On macOS and Linux settings.json is written owner-readable only. On Windows it is not, because Windows has no POSIX file modes and the request is quietly ignored. There the protection comes from %APPDATA% living inside your user profile, which is restricted to you by default. That is a real protection, but it is inherited rather than set by this tool, so it is worth knowing which one you are relying on.

PAGESPEED_CONFIG_DIR moves that wherever you like, and PAGESPEED_API_KEY overrides the stored key for anyone who would rather keep it in their own secret manager.

The key is a file rather than an entry in your system keychain, and that is a deliberate trade worth being straight about. Reaching the keychain portably needs an installed package, an installed package needs a virtual environment and a pip step, and that is the entire "clone it and run it" advantage gone for the people who need it most. Weigh that against what the credential actually is. A PageSpeed key, restricted as step 7 instructs, is read-only access to public measurements of public web pages. It holds no personal data, unlocks no account, and cannot be billed against, because the API is free. The worst a stolen one does is spend a quota of 25,000 calls a day, and you fix that by deleting the key. That is a nuisance, not a breach.

Tests

python3 -m unittest discover -s tests -v

No API key and no network needed. CI runs them on Python 3.9 and 3.12 across Linux, macOS and Windows.

Two of the tests pin bugs found while building this, both of which produced a plausible number rather than an error. PageSpeed reports its embedded CLS multiplied by 100, so a healthy 0.08 reads as 8.0 if you take it at face value, which is a catastrophic score on the one metric where those two numbers are the whole story. And the Chrome UX Report returns CLS as a string while every other metric is a number, so the obvious comparison raises a TypeError. Neither announces itself. In a measurement tool, a wrong number that looks right is the only kind of bug that matters.

Support

This is free and stays that way. Apache 2.0 means you can take it, build on it, and ship it commercially without owing anything back, which is deliberate.

If it stopped you chasing a regression that was never there, then consider buying me a coffee. If it didn't, telling me what it got wrong is worth more than the coffee, and a bug report about a number that looked plausible and wasn't is the most useful thing anyone can send.

Licence

Apache 2.0. See LICENSE and NOTICE.

Unofficial community project. Not affiliated with, endorsed by, or sponsored by Google LLC. It calls two public Google APIs, bundles no Google code, and redistributes no Google data.

Something not working?

Open an issue on GitHub, or send us a message here.

Back to the overview  ·  Questions