Skip to main content
Glama
Considus

pagespeed-insights-mcp

PageSpeed Insights MCP

Measures a web page with Google PageSpeed Insights and reports the median of several runs with the spread beside it, so a number arrives with its uncertainty. Works from an assistant that speaks MCP, or from a terminal.

Standard library only. No virtual environment, no pip, no compiled dependency. Clone it and run it.

Docs · Install · FAQ

pagespeed-insights-mcp MCP server

Why another one

There are already several PageSpeed MCP servers. As far as we can tell, every one of them only runs Lighthouse once and hands the model the number.

A single Lighthouse run is noise. Total Blocking Time routinely swings threefold between runs on a page that has not changed, and the headline performance score is a weighted blend that inherits every bit of that. Ask once and you get a figure with no error bars, and no way to tell a real regression from the instrument moving. That is not a small imprecision, it is the difference between measuring something and guessing at it confidently.

There is a second problem underneath, and it is worse because it is invisible. PageSpeed caches its result per URL and replays it. Ask five times and you can be handed one analysis five times, which quietly turns a median into a vote for whatever Google happened to have cached. Minutes after a deploy on 30 July 2026 that is exactly what happened here, 2 of 3 runs came back with a byte-identical timestamp from before the deploy, dragging the average back to the pre-fix score while the one fresh run showed the fix working. A tool that reports one run would have reported the stale number as current, with nothing to indicate anything was wrong.

This one takes the median of 5 runs by default, prints the min-max spread next to every figure, drops runs that were cached replays, and tells you how many it dropped.

Related MCP server: MCP Server Pagespeed

The lab is not your users

Both of these are true, measured on the same page on the same afternoon.

LCP

Verdict

Lighthouse lab, mobile

21.36 s

performance 27 / 100

Real Chrome users, 28-day p75

1.20 s

FAST

That is the BBC home page. The lab says the site is catastrophically slow. Real people are getting it in 1.2 seconds.

Neither number is wrong, they answer different questions. Lighthouse is a simulation on throttled hardware, useful because it is repeatable and you can run it against a page nobody has visited yet. Field data is what actually happened to real Chrome users, and it is the only thing Google ranks on. A tool that shows you one and not the other is misleading you, whichever one it picks.

So this reports both, separately, and never merges them into a single figure.

What it does

  • Median of N distinct analyses, with the min-max spread beside every score and metric.

  • Keeps asking until it has N genuinely different analyses, because Google re-analyses a URL about once a minute and replays the cached result to everything that asks in between. Five requests in a row is one measurement five times over.

  • Says what that cost in calls and seconds, and reports fewer analyses honestly rather than padding the count when time runs out.

  • Real-user data from the Chrome UX Report, current and up to 6 months of weekly history, when Google has it.

  • Says plainly when Google has none, rather than letting a lab score stand in for evidence about real visitors.

  • Tells an exhausted quota apart from a broken page, and a misconfigured key apart from both.

Things to actually ask it

Four that exercise different parts of it, and none need you to know a tool name.

"How fast is considus.com on mobile, and how confident are you in that number?" The whole reason this exists. You get the median of several genuinely distinct analyses and the spread around it, not one run quoted as fact.

"Is there real user data for this site, or only lab numbers?" Reads the Chrome UX Report, which is the only evidence here about actual visitors and the only thing Google ranks on. Plenty of small sites have none, and it says so rather than letting a lab score stand in.

"What's actually holding up the LCP on that page?" Splits one number into the four phases it is made of, so you find out whether it is the server, the discovery, the download or the paint. Fast, one call, no averaging needed.

"Did that change help?" Records a baseline on the first call, then compares after you have made the change. It only calls a difference real when the two ranges do not overlap, which is what stops you celebrating the instrument moving.

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

Tool

What it does

analyse_page

Everything in one call, and the one most people want. Scores with their spread, what is failing ranked by what fixing it is worth, real-user data if Google has any, and a self-contained HTML page to save or forward. Only a fault that failed in every analysis counts, because audits are as noisy as scores. include picks the parts, scores, issues, field_data and html, if you don't want all four, and one measurement serves every part you ask for. Comes back as a job id.

get_field_data

Real-user data from the Chrome UX Report. One call, answers at once. urls, and history for the weekly p75 series.

explain_lcp

Which of four phases owns a slow Largest Contentful Paint: server response, the wait before the browser starts fetching the largest image, the download, then the wait before it is painted. One call, answers at once.

compare_to_baseline

Did the change actually help. Measures now and compares against a saved baseline, giving a verdict only where the two ranges do not overlap. Comes back as a job id.

get_job_result

Collects a measurement that was handed back as a job id. Answers immediately, every time. job_id, or nothing at all for the most recent one.

check_setup

Whether the key works, whether the Chrome UX Report is reachable, and which baselines are held, without disclosing the key.

Measurements that outlive the call

Every long check used to time out, and the server was fine the whole time. It carried on measuring, finished the job properly, and posted the answer into a pipe nobody was reading any more.

The client had given up at 60 seconds. That is a hard wall on a single tool call, and progress notifications don't move it, which is worth knowing because this server sends them and they look like they should. An honest measurement can't fit inside it. Google re-analyses a URL about once a minute, so five distinct analyses take about 150 seconds, and asking harder doesn't help, it just hands you the same cached analysis five times.

The work doesn't happen inside the call any more. analyse_page and compare_to_baseline start the measurement, hand back a job id, and keep going. Your assistant collects it with get_job_result, every 15 seconds or so, and get_job_result always answers straight away. When the measurement lands it returns exactly what the tool would have returned, so nothing downstream can tell which route it took.

You don't have to do anything. The assistant polls for you.

One measurement still answers on the spot, analyse_page with include set to ["scores"], one URL and runs set to 1. That is a single analysis with no spread, which is the noisy one-run number this whole package exists to refuse, and the output says so in as many words.

A finished job sits there for an hour, in case you want to read it twice. A running one dies if the server restarts, which clients do once they have been idle a while, and get_job_result tells you that happened rather than leaving you polling something that will never finish.

If your client waits longer than 60 seconds, tell the server and it will do more inside the call. Set inline_budget_seconds in settings.json, or the PAGESPEED_INLINE_BUDGET environment variable. It can't work this out on its own, the timeout isn't in the protocol and the handful of environment variables a server inherits doesn't carry it either.

Two things about explain_lcp are worth knowing before you read one, and both are printed in every answer. The four phases do not add up to the LCP, and the gap is not rounding. Across twelve real origins the sum missed by anything from 40ms under to 2.6 seconds over. Each phase is its own 75th percentile, and percentiles do not add. And the phases are measured only over visits where the largest element was an image, which on some sites is a small minority, so the answer always says what share of visits it is describing.

That second point is the useful part as often as it is the caveat. On one large site the LCP looks a comfortable 1.5 seconds, and the eighth of visits with an image LCP are waiting 3.3 seconds before the image so much as starts downloading. Nothing in the headline number shows that.

compare_to_baseline answers the question the findings leave open, which is whether the change you made did anything. The first call on a URL records a baseline and compares nothing, because there is nothing to compare against yet. Make the change, call it again, and it measures afresh and reports what moved.

It gives a verdict only where the two min-max ranges do not overlap at all. That is deliberately conservative and it will miss small real improvements. The reason is that the two possible mistakes are not equally bad. Telling you nothing moved when something did costs you a little confidence, while telling you something improved when it was noise is a claim you might repeat to a client. Where a change is real it reports both the difference in medians and the smaller figure the ranges actually guarantee, and the guaranteed one is the number to quote.

Baselines are held in baselines.json beside the settings, keyed by URL and strategy, and are never moved unless you ask. Field data is deliberately not compared, because a 28-day rolling window cannot show a change made this week.

The skill

skills/pagespeed-insights-read/ teaches an assistant how to read the results. The server refuses to state a number without its uncertainty. It cannot stop an assistant dropping that uncertainty on the way to an answer, and the most common way that goes wrong is adding four savings estimates together and promising nine seconds.

It is optional. Everything works without it, and the tools carry the same warnings in their own descriptions. The skill is what stops those warnings being paraphrased away on the journey to an answer.

Every client keeps skills somewhere different, some sync them from an account rather than reading your disk, and some have no such thing at all. So rather than printing one client's path, ask yours. Paste this in, with the folder path filled in from wherever you cloned this.

I have a skill folder on this computer and I would like you to install it into
whichever client you are running inside, if that client supports skills.

  Skill folder = /path/to/pagespeed-insights-mcp/skills/pagespeed-insights-read

Please:
1. Work out whether this client supports skills, and where it keeps them on this
   machine.
2. Install the folder there, keeping its folder name unchanged. Most clients take
   the name of the folder as the name of the skill, so renaming it renames the
   skill.
3. Prefer linking to the folder above rather than copying it, if this client
   follows links, so it updates when I next pull. Copy it if not.
4. If this client syncs skills from an account rather than reading this disk, do
   not guess at a path. Tell me where to add it instead.
5. Tell me what you did and whether I need to restart the app.

If this client has no concept of skills, say so plainly and do nothing.
Everything works without it.

Setup prints the same prompt with your actual path already in it.

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 check_setup 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 --lcp https://example.com/
pagespeed --findings https://example.com/
pagespeed --compare https://example.com/
pagespeed --baselines
pagespeed --field --report report.html https://example.com/
pagespeed --json https://example.com/

--report writes one self-contained HTML page with no scripts, no network and nothing external. It opens from a file and survives being emailed, which matters because the largest finding is often a hosting or third-party decision belonging to somebody other than whoever ran the check.

Your assistant can save the same page. Ask it for a report and it will ask where you want the file. The server writes only to a folder you name and only when you ask, it will not create one, and it never overwrites an existing report, because somebody comparing a before against an after wants both. Name no folder and it goes to a reports folder beside the settings, and the full path comes back so you can find it.

Saved files keep their embedded fonts and run about 150KB. The copy that comes back in conversation drops them, because 145KB of base64 is most of a context window spent on typography. Same renderer either way.

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.

Code

Meaning

0

fine

1

something else went wrong

2

bad arguments

3

quota exhausted, infrastructure rather than the site

4

credential rejected, configuration rather than the site

5

page unreachable, this one is the site

6

could 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.

When something goes wrong

Every failure carries the sentence that fixes it rather than a stack trace, and most of them are not about your site at all. Knowing which is which saves you changing something that was never broken.

A 429 and no key. You are on the shared anonymous pool, which everyone else is also using and which is routinely spent by the middle of the day. It says nothing whatsoever about the page you were measuring. Run setup and add a key.

A 429 with a key. Your own project quota is spent for the day. It resets at midnight Pacific time.

A 400 or 403 from PageSpeed Insights. The key is missing, wrong, or carrying an HTTP-referrer restriction. That last one is the usual answer, and it is step 9 of Before you start coming back to bite, because a program has no referring web page, so a key restricted that way is refused every single time. Use API restrictions instead, or none. While you are on that screen, check the PageSpeed Insights API is enabled on the project at all.

No field data for the origin. This is not a failure and the tool won't report it as one. Google publishes real-user data only for origins with enough traffic to stay anonymous, so a small, new or pre-launch site has none, and won't until it has visitors. What's left is the lab, which is a simulation and not evidence about anybody's real experience.

The Chrome UX Report says it has not been used in this project. Either the API isn't enabled, or you enabled it a minute ago and it hasn't finished propagating. Those two are indistinguishable from out here, and this one has already been retried before you see it, so wait a couple of minutes and run it again before changing anything.

The Chrome UX Report refuses the key. The key itself is fine, its API restrictions just don't include Chrome UX Report API. Add it in the Google Cloud console, under Credentials, editing the key's API restrictions.

Lighthouse could not load the page. Unlike everything above it, this one really is about your site. Check the URL serves a 200 to an anonymous visitor, with no login in the way and no geographic block.

It could not reach Google at all. Network, DNS or a timeout, already retried with backoff by the time it says so.

Your assistant says the request timed out. Nothing here should run long enough for that any more, because anything past about a minute comes back as a job id instead. If you see it, something is set to do more inside the call than your client will sit through, so look at inline_budget_seconds in settings.json and at the PAGESPEED_INLINE_BUDGET variable, and lower it or take it out.

A job says it is lost. The server was restarted while it was measuring, which is what a client does to a server that has been sitting idle. Start the measurement again and let the assistant keep polling while it runs.

When you can't tell which of these you have, check_setup answers it. Whether the key works, whether the Chrome UX Report is reachable, and which baselines are held, none of it disclosing the key.

Updating

There's no package and no installer, so there's nothing to download. The server runs as mcp_server.py out of the directory you cloned into, which makes an update a pull and a restart.

Your key and your saved URLs aren't in that directory, they sit in settings.json in your platform's config directory, so an update leaves them alone and you won't be asked for the key again.

The restart is the part that catches people out. A stdio MCP server is a long-running process, and it reads mcp_server.py once, when the app starts it. Changing the file underneath a server that's already running does nothing at all, so quit the app properly and open it again. Closing the window isn't enough on macOS, and neither is closing the last tab on Windows if it leaves the app in the tray.

Releases are tagged, and the releases page on GitHub says what changed in each one. pagespeed --version tells you which one you're on, and your assistant can read the same number out of the server's handshake. git pull puts you on the latest main, which is sometimes ahead of the newest tag.

Have an assistant do it

Paste this into an AI assistant that runs shell commands on this computer. It can do the pull, but it can't restart the app it's running inside, so the last step stays yours.

Please update my PageSpeed Insights MCP server. Find where it's installed by
reading the path out of this app's MCP config rather than guessing it, run git
pull in that folder, and tell me what changed. Don't run setup.py and don't edit
my settings.json, my key and saved URLs are already in it. Then remind me to quit
this app completely and open it again, because the server only reads
mcp_server.py at startup.

Or run the commands yourself

In the folder you cloned into.

cd pagespeed-insights-mcp
git pull

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.

Running measurements keep their state in a jobs folder beside the settings, one small file each, deleted an hour after they finish. That is what lets a poll find its answer when the client has quietly restarted the server underneath it.

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. PAGESPEED_INLINE_BUDGET says how many seconds of work to do inside a single tool call before handing back a job id instead, and 45 is the default.

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.

What it will not do

It will not report a single run as a measurement. There is no flag for it and there is not going to be.

It will not merge lab and field numbers into one figure. They disagree by an order of magnitude on real sites, and averaging them would destroy the only honest thing here.

It will not call a change a regression when the change is inside the spread. The spread is printed so you can see for yourself, and a movement inside it is not evidence of anything.

It does not touch Google Search Console. Search Console needs OAuth and per-property authorisation, because it serves private data about a property you own, where PageSpeed and the Chrome UX Report serve public data about public pages that anyone may measure. That is a different kind of tool with a different kind of credential, and bolting it on here would drag a consent flow into something that currently needs one string.

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.

Privacy Policy

This one is not local-only, and pretending otherwise would be the wrong thing to write. Measuring a page means asking Google to measure it, so the address you check goes to Google. That is the tool working rather than a leak, but it is worth stating first rather than buried.

What leaves this machine, and where it goes

The URL you are measuring, sent to the PageSpeed Insights API, and to the Chrome UX Report API when you ask for field data. Your API key travels with those requests if you have one. Nothing else. Google then loads that page from their own infrastructure and measures it, so the page owner sees a visit from Google rather than from you.

Google's handling of those requests is Google's, governed by their privacy policy and their API terms, and it is outside anything Considus can promise for them.

What does not leave

There is no Considus server in this. No account, no telemetry, no crash reporting, no usage counting, and no licence check. We never see what you measured, when, or that you ran it at all. There is nothing on our side to see it from.

What it writes, and where

settings.json holds your API key and any URLs you saved, in your platform's config directory, and PAGESPEED_CONFIG_DIR moves it wherever you like.

Baselines, so a later run can tell you whether a change is real or noise, sit beside it.

HTML reports go to the reports folder next to those settings, or to a folder you named in the conversation. Nothing writes to a folder that does not already exist, and nothing is ever overwritten.

A running measurement writes its progress and then its result to a small file in the jobs folder beside the settings, because a poll and the measurement it is asking about are not always in the same server process. Those files hold measurements of public pages and nothing else, and they are deleted an hour after the job finishes.

All of it is on your own disk and all of it is yours to delete. Deleting settings.json removes the key from the machine.

About the key being in a file

On macOS and Linux it is written owner-readable only. On Windows that request is ignored, because Windows has no POSIX file modes, and the protection there comes from %APPDATA% sitting inside your user profile instead. That is real, and it is inherited rather than set by this tool, which is a weaker promise and worth knowing.

It is a file rather than a keychain entry because reaching the keychain portably needs an installed package, which needs a virtual environment and a pip step, which is the whole "clone it and run it" advantage gone. Weigh that against what the credential is. A PageSpeed key restricted as the setup instructs is read-only access to public measurements of public 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 you can reset by deleting the key. Anyone who disagrees with that trade sets PAGESPEED_API_KEY from their own secret manager and no key is stored at all.

Retention, and who else gets it

We hold nothing, so there is nothing for us to keep, and nothing for us to delete on your behalf. Nothing is shared with anyone, because nothing reaches us to share. Google is the only third party involved, and only for the measurement requests above.

If you want to ask about any of this

Write to privacy@considus.com, or open an issue at github.com/Considus/pagespeed-insights-mcp/issues. The policy covering considus.com itself is at considus.com/privacy, and it is a separate document because it covers a separate thing.

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.

Something broken or a number you don't believe, open an issue. Anything exploitable goes through GitHub's private reporting or to security@considus.com instead, as described in SECURITY.md, rather than a public issue. For anything that fits neither, including press and licensing, it's support@considus.com, and the other ways to reach us are at considus.com/support.

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.

Available Tools

6 tools
analyse_pageAnalyse a pageA

Use to measure a page in the lab and find what to fix (scores with their spread, failing audits ranked by what fixing them is worth, real-user data beside them, and a page to send on); for real-user data alone use get_field_data, which is fast, and for "did that change help" use compare_to_baseline. include picks the parts, "scores", "issues", "field_data" and "html", default all four. ONE MEASUREMENT SERVES EVERY PART, so asking for more costs no extra API calls, while asking in separate calls measures again. SLOW, several minutes. It reports the MEDIAN of several DISTINCT analyses with the min-max spread, so the number comes with its uncertainty. A single Lighthouse run is noise, Total Blocking Time swings threefold between runs on an unchanged page, so runs=1 is fast but its score is not a reliable figure. Google re-analyses a URL only about once a minute and replays the cached result in between, so this keeps asking until it has genuinely different analyses rather than the same one several times. That means runs=5 takes roughly 150 seconds and asking harder will NOT speed it up. Reports fewer analyses honestly if time runs out. Anything that will not fit inside one tool call returns a JOB ID instead and keeps working: collect it with get_job_result, polling about every 15 seconds. Only runs=1 on a single URL with include ["scores"] answers immediately, and that is the noisy single run described above. ISSUES are what is FAILING on the page, from Google's own audit findings and remediation text. Only a fault that failed in EVERY distinct analysis is reported, because audits are as noisy as scores and a one-off failure is the instrument moving rather than a fact about the page. Estimated savings carry their median and spread for the same reason. Ranked by what actually moves the score: the performance score is five metrics and every other performance audit weighs zero, so a big estimated saving on an unweighted diagnostic is not the first thing to fix. Savings do NOT add up, they overlap, so the order is meaningful and the sum is not. FIELD_DATA is real-user data if Google has any. HTML is a complete self-contained page carrying all of it, made to be saved as a file the user can open or forward, because several of the largest findings are usually hosting or third-party decisions that the person running the check cannot fix alone. Set html false if they only want the answer in conversation. TO SAVE IT AS A FILE, pass directory, a folder on the user's own machine that the report is written into. The server writes into whichever existing folder directory names and does not check where that path came from. The folder is checked BEFORE the measurement starts, which takes minutes, so a folder that does not exist or cannot be written to is refused at once. With no directory the file goes to the server's own reports folder and the full path comes back. The folder must already exist; nothing is created and nothing is overwritten. Saved files keep their embedded fonts and are about 150KB; the copy returned in conversation drops them to stay small, so a saved file is the fuller of the two. Returns a readable report and the same figures as JSON.

NameRequiredDescriptionDefault
htmlNoInclude the HTML page in the reply when include has "html". Default true, ignored when saving to a file.
runsNoDISTINCT analyses to collect and median over. Default 5, which takes ~150s, for scores alone, otherwise 3. At least 2 whenever issues or html are included.
urlsNoAbsolute http(s) URLs. Defaults to saved URLs.
includeNoThe parts to return. Default all four. "scores": lab scores and metrics with their spread. "issues": what is failing, ranked. "field_data": real-user data from the Chrome UX Report. "html": the complete report as a self-contained page.
filenameNoFile name only, no slashes and no "..". Defaults to the site and today's date.
strategyNoDefault mobile, which Google ranks on. issues and html are measured on mobile only, so desktop and both need an include without them.
directoryNoAn EXISTING folder to save the report into, which the USER named. Omit to use the server's own reports folder. Needs "html" in include.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations, the description discloses the slow multi-minute runtime, median-of-distinct-runs behavior, Google's roughly once-per-minute URL re-analysis and cached-result replay, the estimated ~150 seconds for runs=5, the job-ID fallback with polling guidance, the failure-only rule for issues, non-additive savings, and file-saving behavior including pre-measurement folder validation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is front-loaded with purpose and alternatives, but the single long paragraph packs in many secondary details—TBT variance, cached-result mechanics, file size and font differences, path provenance—some of which could be trimmed or separated for scannability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex seven-parameter tool with no output schema, the description covers return format (readable report plus JSON), job handling, timing expectations, failure semantics, and file-saving behavior thoroughly enough that an agent can call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already defines each parameter. The description still adds meaningful context: include parts are measured together, runs interacts with caching and time, directory is checked before measurement, and html controls whether the full page is embedded or saved.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource ('measure a page in the lab and find what to fix') and clearly distinguishes the tool from its siblings by naming get_field_data for real-user-only data and compare_to_baseline for change validation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states when to use this tool versus alternatives, explains that include selects report parts, and warns that separate calls re-measure while one measurement serves every part. The runs=1 fast-path and its unreliability are also spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

check_setupCheck configurationA
Read-only

Use first when anything looks wrong, to tell a configuration problem apart from a slow or failing page before committing to a full check. Reports what is configured and what actually works right now (key present, PageSpeed reachable, Chrome UX Report permitted) without disclosing the key.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds real context beyond the readOnlyHint/openWorldHint annotations: it discloses the three specific probes performed and states the key is not disclosed, which is a security-relevant trait. It does not mention latency or how results are structured, so not a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the triggering condition ('Use first when anything looks wrong') followed by what it reports. Two dense sentences with little waste, though the parenthetical enumeration slightly repeats the preceding clause.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-param, read-only diagnostic with no output schema, the description supplies everything an agent needs: when to call it, what it verifies, and that it does not leak the key.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so the baseline is 4; the description correctly implies no inputs are needed and does not misrepresent any parameter behavior.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific diagnostic purpose with a clear verb (check) and enumerates what it reports: key present, PageSpeed reachable, Chrome UX Report permitted. It is clearly separable from siblings like analyse_page or explain_lcp because it is the pre-flight config check.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use first when anything looks wrong' and frames the goal as telling a configuration problem apart from a slow or failing page 'before committing to a full check' — a named alternative and a condition that selects it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

compare_to_baselineCompare to baselineA
Destructive

Use to answer "did that change actually help"; for a one-off measurement with nothing to compare against use analyse_page. Measures now and compares against a saved baseline for the same URL and strategy. THE FIRST CALL ON A URL RECORDS THE BASELINE and compares nothing, which is the correct answer before anything has changed, and a call after the change compares against it. A verdict is only given where the two min-max ranges do NOT overlap: on an unchanged page the performance score has been measured running 27 to 37 and Total Blocking Time 824ms to 3.05s, so comparing medians alone reports improvements that are just the instrument moving. Where a change is real it reports both the difference in medians and the smaller figure the ranges actually guarantee, and the guaranteed one is the figure the measurement supports. Also reports which findings stopped and started failing, and flags a Lighthouse version change, which moves scores without the page moving. Does NOT compare field data, which is a 28-day window and cannot show a change made this week. SLOW, several minutes: it returns a JOB ID and you collect the verdict with get_job_result.

NameRequiredDescriptionDefault
runsNoDistinct analyses each side. Default 3.
urlsNoAbsolute http(s) URLs. Defaults to saved URLs.
strategyNoPart of the baseline identity. Default mobile.
save_baselineNoReplace the baseline with this measurement. Default false, so repeated calls keep comparing against the same starting point.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations: discloses the min-max overlap rule for verdicts, the instrument-noise figures (27–37 score, 824ms–3.05s TBT), that it reports both median difference and guaranteed difference, that it flags Lighthouse version changes, and that it is slow (minutes) and async, returning a JOB ID collected via get_job_result. The baseline-writing behavior is consistent with destructiveHint=true and idempotentHint=false.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the use case and the exclusion, then layered detail. It is long, and the statistical-noise paragraph is dense, but nearly every sentence carries decision-relevant information. Slight over-length keeps it from a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a slow async tool with no output schema, the description supplies the async contract (JOB ID → get_job_result), the verdict semantics, the failure/version-change caveats, and what is out of scope (field data). Nothing an agent needs to call it or interpret the result is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds real meaning beyond it: strategy is part of the baseline identity, and repeated calls keep comparing against the same starting point unless the baseline is replaced. It doesn't elaborate on runs, but the schema already defines it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: measures now and compares against a saved baseline for the same URL and strategy, framed around the question it answers ('did that change actually help'). It explicitly names the sibling it is not (analyse_page for one-off measurements), so an agent can discriminate without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use (after a change), when-not (one-off measurement → analyse_page; field data cannot show a change made this week), and the critical first-call semantics (first call records the baseline and compares nothing). This is as close to a decision rule as a description gets.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

explain_lcpExplain LCPA
Read-only

Use when Largest Contentful Paint is the problem, to find which part of the load owns it; for the LCP figure alone use get_field_data, and for lab scores and what to fix use analyse_page. Breaks a slow LCP into the four phases it is made of: server response, then the delay before the browser starts fetching the largest image, then the download, then the delay before it is painted. FAST, one Chrome UX Report call, no Lighthouse runs and no noise to average away. TWO THINGS NOT TO GET WRONG, both reported in the output: the phases are each a separate 75th percentile so they do NOT sum to the LCP (measured gaps range from -421ms to +2616ms across twelve real origins, in both directions) and the shares are of the phase total, never of the LCP; and they are measured ONLY over visits whose largest element was an image, which on some sites is a small minority, so the output gives the image share alongside them. Needs an API key and real-user data, which many small sites do not have.

NameRequiredDescriptionDefault
urlsNoAbsolute http(s) URLs. Defaults to saved URLs.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint and openWorldHint annotations, it discloses operational traits: one Chrome UX Report call, no Lighthouse runs, no noise to average away, and it requires an API key and real-user data. It also surfaces two crucial caveats that affect interpretation of results: phases are separate 75th percentiles and do not sum to LCP, and they are measured only over image-LCP visits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The definition is front-loaded with when to use it and which siblings to prefer, followed by method and caveats. It is dense but the caveats about phase summing and image-only measurement earn their place; only the extended measured-gap example is slightly verbose relative to the core instruction.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex diagnostic tool with no output schema, the description explains what is returned: the four phase breakdown, phase shares, and the image share. It also covers important prerequisites (API key, real-user data) and interpretation limits, leaving no major gap for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has only one parameter ('urls') and 100% schema description coverage, so the schema already explains that it accepts absolute http(s) URLs and defaults to saved URLs. The description adds no further parameter-specific meaning such as batching behavior or URL-list limits, so the baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: it breaks a slow LCP into four named phases (server response, image fetch delay, download, paint delay). It explicitly distinguishes itself from get_field_data (LCP figure alone) and analyse_page (lab scores and fixes), so an agent can identify when this tool applies.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit trigger: 'Use when Largest Contentful Paint is the problem, to find which part of the load owns it.' It names two alternatives and the precise conditions that route to them: get_field_data for the LCP figure alone, and analyse_page for lab scores and what to fix.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_field_dataReal-user field dataA
Read-only

Use for what real Chrome users actually experienced, with no lab measurement, so it answers in seconds rather than minutes; for lab scores and what to fix use analyse_page, and for which phase owns a slow LCP use explain_lcp. From the Chrome UX Report, the only evidence here about real visitors and the only thing Google ranks on. Set history for the weekly p75 time series, which shows whether a site has been getting slower for real people over months. Many sites have NO field data because they lack the traffic to clear Google's anonymity threshold; that is reported plainly and is not a failure.

NameRequiredDescriptionDefault
urlsNoAbsolute http(s) URLs. Defaults to saved URLs.
historyNoAlso fetch the weekly p75 time series.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, but the description adds real context: it is fast (seconds vs minutes), and crucially that many sites legitimately return NO field data due to Google's traffic/anonymity threshold and that this is not an error — a genuinely useful behavior an agent would otherwise misread as failure. It does not describe the shape of the successful response beyond the p75 series, which keeps it below 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with 'Use for...' and then the alternatives, which is the right order. The opening sentence is long and clause-heavy, packing speed, absence of lab data, and two sibling redirects into one breath, but every sentence carries distinct information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter read-only tool with no output schema, the description covers purpose, alternatives, the history option's meaning, and the important empty-result case. Only the general shape of a successful per-URL response is left implicit, which is a minor gap given the low complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so both parameters are already documented, giving a baseline of 3. The description nonetheless adds interpretation beyond the schema: `history` yields a weekly p75 time series that reveals whether real users have experienced degradation over months, which is more than the schema's 'Also fetch the weekly p75 time series.'

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource (real-user Chrome UX Report field data) and explicitly contrasts it with lab measurement. Names two siblings, analyse_page for lab/fixes and explain_lcp for LCP phase attribution, so an agent can disambiguate without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use (real visitor experience), when-not (lab scores, fixing, LCP diagnostics) and the sibling that covers each alternative. Also states the condition for enabling `history` (wanting to see multi-month slowdown trends).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_job_resultCollect a running measurementA
Read-only

Use to collect a measurement that analyse_page or compare_to_baseline handed back as a job id, calling it about every 15 seconds until it reports done. Anything longer than about a minute cannot be answered inside one tool call (clients enforce a hard wall-clock limit and progress notifications do not extend it), so those tools return a job id and keep working. When done it returns exactly what the tool would have returned. ALWAYS answers immediately. Omit job_id for the most recent job. Starting the same measurement again while one is running does not make it arrive any sooner, because the wait is Google re-analysing the URL about once a minute.

NameRequiredDescriptionDefault
job_idNoThe id returned when the measurement started. Omit for the most recent one.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the readOnlyHint/openWorldHint annotations: it discloses that calls always return immediately (non-blocking poll), that clients enforce a hard wall-clock limit that progress notifications do not extend, and why long measurements must be deferred to a job. It also explains the underlying latency source (Google re-analysing the URL roughly once a minute), which prevents fruitless retry loops.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Use case and polling cadence are front-loaded, and every sentence carries operational information. There is mild redundancy — the wall-clock explanation and the 'does not make it arrive any sooner' sentence overlap in conveying 'wait for it, don't restart' — plus a capitalized emphasis that could be tightened.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description compensates by defining the return value ('exactly what the tool would have returned'). For a one-optional-parameter, read-only polling tool, the description covers purpose, timing, defaults, failure modes, and return semantics with nothing an agent needs left out.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single optional job_id is already documented in the schema, so the baseline would be 3. The description adds real meaning by identifying the provenance of the value ('handed back as a job id' by analyse_page or compare_to_baseline) and restating the omit-for-latest default in behavioral context.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — collect the result of a measurement job — and names the two sibling tools (analyse_page, compare_to_baseline) whose job ids it consumes. It also clarifies the return contract ('returns exactly what the tool would have returned'), so an agent knows this is a completion/collection step, not a new analysis.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit polling cadence ('about every 15 seconds until it reports done'), an explicit default ('omit job_id for the most recent job'), and an explicit anti-pattern ('starting the same measurement again while one is running does not make it arrive any sooner'). The when-to-use chain from analyse_page/compare_to_baseline is spelled out rather than inferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 12 tool updatesv2.0.0
    • Addedanalyse_page
    • Removedcheck_pagespeed
    • Addedcheck_setup
    • Removedcheck_status
    • Removedcompare
    • Addedcompare_to_baseline
    • Removeddiagnose
    • Removeddiagnose_page
    • Removedfield_data
    • Addedget_field_data
    • Addedget_job_result
    • Removedreport
  2. 1 tool update
    • Addedcheck_status
  3. 7 tool updatesv1.4.1
    • First observedcheck_pagespeed
    • First observedcompare
    • First observeddiagnose
    • First observeddiagnose_page
    • First observedexplain_lcp
    • First observedfield_data
    • First observedreport

TDQS

A4.6/5.0

Scored across 6 tools

Disambiguation4/5

The tools are mostly distinct: lab measurement, CrUX field data, LCP phase diagnosis, baseline comparison, setup diagnostics, and async result retrieval each have a clear role. Some overlap remains because analyse_page can return field_data and explain_lcp also uses CrUX/LCP data, but the descriptions explicitly route between them.

Naming Consistency5/5

All tool names follow a consistent snake_case verb_object pattern: get_job_result, check_setup, get_field_data, analyse_page, explain_lcp, compare_to_baseline. There is no mixed casing or conflicting verb style.

Tool Count5/5

Six tools is well scoped for a PageSpeed Insights server, covering setup, lab analysis, field data, LCP explanation, baseline comparison, and async job retrieval. No tool appears to be filler.

Completeness4/5

The surface covers the core PSI workflows: configuration check, lab measurement, real-user field data, LCP diagnosis, regression comparison, and job collection. Minor lifecycle gaps exist, such as no job cancellation/listing or baseline reset/delete, but agents can mostly work around them.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    Enables AI models to analyze webpage performance using the Google PageSpeed Insights API, providing real-time performance scores and improvement suggestions.
    1
    131 npm
    11
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server that audits web pages using Lighthouse and Puppeteer to find performance bottlenecks and generate CPU profiles for optimization.
    2 npm
    MIT