Skip to main content
Glama
Nanparam

IQAir MCP Server

by Nanparam

IQAir MCP Server

An MCP server that exposes real-time air-quality data from IQAir — built by reverse-engineering the IQAir website (no official API key required).

It was created for the Jakarta air-quality page (https://www.iqair.com/id/air-quality/indonesia/jakarta/jakarta) but works for any IQAir location.

What it returns

For a location you get:

  • US AQI and the main pollutant

  • Per-pollutant readings: PM2.5, PM10, O₃, SO₂ (AQI + concentration)

  • Weather: temperature, humidity, pressure, wind

  • Reporting station count

  • 7-day and 72-hour forecasts

  • Coordinates / timezone

Related MCP server: WeatherAPI MCP Server

Tools

Tool

Description

get_jakarta_air_quality(locale="id")

Full Jakarta report (current + forecasts).

get_jakarta_current(locale="id")

Jakarta current conditions only (lighter).

get_jakarta_forecast(locale="id", kind="daily")

Jakarta forecast (daily or hourly).

get_air_quality_by_path(path, locale="en")

Any location, e.g. path="indonesia/west-java/bandung".

path is the segment after /<locale>/air-quality/ in an IQAir URL — country/state/city.

How it works (reverse engineering)

IQAir is a React Router v7 (Remix) app on Vercel. Every air-quality page exposes its loader payload at the same URL with a .data suffix:

GET /id/air-quality/indonesia/jakarta/jakarta.data?_routes=routes%2F%24(locale).air-quality.%24

The response is turbo-stream encoded (an index-referenced pool with deferred promise chunks), decoded by turbo_stream.py.

Anti-bot: the Vercel Security Checkpoint

The site is guarded by Vercel's checkpoint. During reverse engineering we found:

  • Plain requests / httpxHTTP 429 (challenge page).

  • curl_cffi with Chrome TLS impersonation → still 429 (JS/WASM challenge is required, not just a matching TLS fingerprint).

  • Even a valid _vcrcs cookie replayed from a non-browser client → 429 (TLS/JA3 fingerprint is validated).

  • Headless Chromium → checkpoint never clears (headless is detected).

  • Headful Chromium → clears in ~3 s.

So the client (iqair_client.py) drives a headful Chromium with a persistent profile: it solves the checkpoint once, caches the _vcrcs cookie in the profile, and issues the .data fetch from inside the page context so the browser's real TLS stack + solved cookie are used. The window is parked off-screen so it doesn't disturb you.

Setup

cd iqair-mcp
pip install -r requirements.txt
python -m playwright install chromium

A desktop session / display is required (the browser must run headful).

Run

python server.py                    # stdio (for MCP clients)
python server.py --transport sse    # SSE on http://localhost:8000

Register with an MCP client

{
  "mcpServers": {
    "iqair": {
      "command": "python",
      "args": ["C:\\path-to-mcp\\iqair-mcp\\server.py"]
    }
  }
}

Files

  • server.py — FastMCP server exposing the tools.

  • iqair_client.py — browser-backed client (challenge solving + fetch + normalize).

  • turbo_stream.py — decoder for React Router's turbo-stream .data format.

  • requirements.txt — dependencies.

Staying alive across RDP disconnects

The checkpoint forces a headful browser, and an off-screen headful Chromium can lose its rendering surface (and die) when an RDP session disconnects or locks. The server survives this on two levels:

  1. Auto-recovery — if a call hits a "target/browser closed" error, the client tears down the dead browser and relaunches once, transparently.

  2. Watchdog heartbeat — a background thread pings the browser every keepalive_seconds (default 60 s); if it's dead/unresponsive it rebuilds and re-solves the challenge proactively, so a disconnect never leaves a dead browser waiting for the next tool call. Warm calls after a revive return in ~1–2 s with no re-solve.

Additionally, the screensaver/auto-lock was disabled for the current user (HKCU\Control Panel\DesktopScreenSaveActive=0, ScreenSaveTimeOut=0) so a connected session doesn't lock itself out from under the browser. This is per-user and reversible.

Fully headless-of-console setups (keeping a live desktop with nobody connected) require admin rights — either a machine policy MaxDisconnectionTime=0 under HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services, or a tscon-to-console scheduled task. Those aren't applied here (no elevation on this domain account); the watchdog + auto-recovery cover the disconnect case without them.

Notes & limitations

  • First call per session pays a one-time ~3 s browser launch + challenge solve; subsequent calls reuse the warm session.

  • A connected desktop session is still recommended; the watchdog handles brief disconnects/locks by rebuilding, but the machine must have a session to render a headful browser at all.

  • Data is whatever IQAir currently publishes (values update in real time).

  • This relies on IQAir's internal (undocumented) data endpoint; site changes may require updating the route id or decoder.

  • For personal/educational use — respect IQAir's terms of service.

Related MCP Connectors

Related MCP Servers