protogrid

Search / io.github.cyanheads/openaq-mcp-server

openaq-mcp-server

not classified

Find air-quality stations and read pollutant observations from government monitors via OpenAQ v3.

v0.4.1 · active · repository · descriptor JSON · versions

Not classified yet.The remote endpoints have not been probed successfully, or the listing has nothing to connect to.
Quality89good
Trust64of 100
Uptime, 30 days81%
Latency p50170 ms

Connect

8 clients · secrets stay placeholders
{
  "mcpServers": {
    "openaq-mcp-server": {
      "type": "http",
      "url": "https://openaq.caseyjhand.com/mcp"
    }
  }
}
claude mcp add --transport http openaq-mcp-server https://openaq.caseyjhand.com/mcp
[mcp_servers.openaq-mcp-server]
url = "https://openaq.caseyjhand.com/mcp"
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "openaq-mcp-server": {
      "type": "remote",
      "url": "https://openaq.caseyjhand.com/mcp",
      "enabled": true
    }
  }
}
{
  "mcpServers": {
    "openaq-mcp-server": {
      "type": "http",
      "url": "https://openaq.caseyjhand.com/mcp"
    }
  },
  "deeplink": "cursor://anysphere.cursor-deeplink/mcp/install?name=openaq-mcp-server&config=eyJ0eXBlIjoiaHR0cCIsInVybCI6Imh0dHBzOi8vb3BlbmFxLmNhc2V5amhhbmQuY29tL21jcCJ9"
}
{
  "servers": {
    "openaq-mcp-server": {
      "type": "http",
      "url": "https://openaq.caseyjhand.com/mcp"
    }
  }
}
{
  "mcpServers": {
    "openaq-mcp-server": {
      "httpUrl": "https://openaq.caseyjhand.com/mcp"
    }
  }
}
extensions:
  "openaq-mcp-server":
    enabled: true
    name: "openaq-mcp-server"
    type: streamable_http
    uri: "https://openaq.caseyjhand.com/mcp"
    timeout: 300

Quality 89 / 100good

protocol88 · weight 25
  • pass
    protocol.modernsupports 2026-07-28 (server/discover)
  • pass
    protocol.statelessanswers without a session
  • pass
    protocol.transportstreamable HTTP
  • warn
    protocol.list_ttltools/list declares ttlMs 0, so clients re-fetch it on every use
authorizationn/a · weight 15
  • n/a
    auth.prm, auth.as_metadata, auth.cimdno remote uses OAuth
  • n/a
    auth.secret_in_urlno templated URL
tool hygiene86 · weight 30
  • pass
    tools.descriptionsevery tool has a description
  • pass
    tools.description_lengthdescriptions are concise
  • pass
    tools.schemasevery tool has an object input schema
  • pass
    tools.annotations7 of 7 tools declare readOnlyHint or destructiveHint
  • fail
    tools.directory_hintsno tool declares all four of readOnlyHint, destructiveHint, idempotentHint and openWorldHint; missing: openaq_list_countries (destructiveHint, openWorldHint); openaq_dataframe_describe (destructiveHint, idempotentHint, openWorldHint); openaq_find_locations (destructiveHint); …
  • pass
    tools.token_costabout 3,592 tokens to load every tool
  • pass
    tools.api_dumptools are not a one-to-one API dump
stability100 · weight 15
  • pass
    stability.changes5 tool changes in 30 days
  • pass
    stability.rug_pullno tool changed its meaning under the same name
dependenciesn/a · weight 15

7 tools, about 3,592 tokens to load them all · tool set 6859c14ba26f · tools last changed 2026-09-24 · checked 2026-10-20 18:06 UTC

Tool changes

  • openaq_list_countrieschangeddescription, input schema, output schema
    before

    Catalog of country-level coverage: id, ISO code, name, the date span of available station data (datetimeFirst/datetimeLast), and which parameters are measured anywhere in that country. The availability check before a regional sweep — answers "which countries have NO2 monitoring?" and tells you whether a country has recent data before you call openaq_find_locations. Coverage is uneven worldwide; this surfaces where measured data exists.

    after · 80% of words in common

    Catalog of country-level coverage: id, OpenAQ country code, name, the date span of available station data (datetimeFirst/datetimeLast), and which parameters are measured anywhere in that country. The availability check before a regional sweep — answers "which countries have NO2 monitoring?" and tells you whether a country has recent data before you call openaq_find_locations. Coverage is uneven worldwide; this surfaces where measured data exists. Results come a page at a time (20 countries by default); totalCount is the full filtered count.

  • openaq_list_parameterschangeddescription, input schema
    before

    Catalog of every measurable pollutant and its canonical unit: id, code, display name, unit, and a one-line description (pm25, pm10, o3, no2, so2, co, bc, and ~38 more). This is the unit-disambiguation reference — the same pollutant exists under several ids with different units (CO is id 4 in µg/m³, id 8 in ppm, id 102 in ppb), so use this to pick the exact parametersId for openaq_find_locations / openaq_get_readings / openaq_get_measurements and to interpret a reading's unit. A small bounded catalog fetched live from OpenAQ.

    after · 99% of words in common

    Catalog of every measurable pollutant and its canonical unit: id, code, display name, unit, and a one-line description (pm25, pm10, o3, no2, so2, co, bc, and more). This is the unit-disambiguation reference — the same pollutant exists under several ids with different units (CO is id 4 in µg/m³, id 8 in ppm, id 102 in ppb), so use this to pick the exact parametersId for openaq_find_locations / openaq_get_readings / openaq_get_measurements and to interpret a reading's unit. A small bounded catalog fetched live from OpenAQ.

  • openaq_get_measurementschangeddescription, input schema, output schema
    before

    Historical measurement series for one pollutant at one station over a date range — for trend analysis and "was last week worse than the monthly average?". Pass a locationId and a parametersId and work in stations — you get the series for that pollutant at that station. Choose aggregation: raw (every reported value), hourly, or daily — daily and hourly add a per-bucket statistical summary (min, median, max, mean, sd). Large ranges produce thousands of rows and stage on a DataCanvas: the response returns a preview plus a canvasId and table name — call openaq_dataframe_describe on the canvasId for the table's columns, then openaq_dataframe_query to run SQL over it. Passing a canvas_id stages the series there whatever its size, so two stations land on one canvas for a side-by-side comparison. Values carry their unit; the server never converts between µg/m³, ppm, and ppb.

    after · 95% of words in common

    Historical measurement series for one pollutant at one station over a date range — for trend analysis and "was last week worse than the monthly average?". Pass a locationId and a parametersId and work in stations — you get the series for that pollutant at that station. Choose aggregation: raw (every reported value), hourly, or daily — daily and hourly add a per-bucket statistical summary (min, median, max, mean, sd). A date-only bound means the station's local calendar day. Large ranges produce thousands of rows and stage on a DataCanvas: the response returns a preview plus a canvasId and table name — call openaq_dataframe_describe on the canvasId for the table's columns, then openaq_dataframe_query to run SQL over it. Passing a canvas_id stages the series there whatever its size, so two stations land on one canvas for a side-by-side comparison. Values carry their unit; the server never converts between µg/m³, ppm, and ppb.

  • openaq_get_readingschangeddescription, input schema, output schema
    before

    Latest measured value for every sensor at a monitoring station — the current-conditions tool. Returns one record per parameter, each with the value, its unit, the UTC and local timestamp, and the sensor id, joined so every value carries its pollutant and unit (the raw latest feed is keyed only by sensor id). Pass a locationId from openaq_find_locations, or pass coordinates to auto-resolve to the nearest station that measures the requested parametersId. Data recency varies by station reporting cadence — read each value's timestamp to know whether "latest" is minutes or hours old. These are measured observations with coverage gaps, not a modeled grid.

    after · 94% of words in common

    Latest measured value for every sensor at a monitoring station — the current-conditions tool. Returns one record per parameter, each with the value, its unit, the UTC and local timestamp, and the sensor id, joined so every value carries its pollutant and unit (the raw latest feed is keyed only by sensor id). The station block names its provider (for attribution) and timezone. Pass a locationId from openaq_find_locations, or pass coordinates to auto-resolve to the nearest station that measures the requested parametersId. Data recency varies by station reporting cadence — read each value's timestamp to know whether "latest" is minutes or hours old. These are measured observations with coverage gaps, not a modeled grid.

  • openaq_find_locationschangeddescription, input schema, output schema
    before

    Find air-quality monitoring stations (measured by physical sensors, not modeled) near a point, within a bounding box, or by country. Returns each station's id, name, coordinates, distance from the query point (when searching by coordinates), country, provider, the parameters its sensors measure, and the timestamp of its most recent data (datetimeLast). Required first step: openaq_get_readings and openaq_get_measurements key on the location id this returns. Coverage is uneven and real — a station only reports the parameters it measures, and the absence of a nearby station means no monitoring there, not clean air. For dense modeled coverage anywhere on Earth, use open-meteo-mcp-server's air-quality tool instead.

    after · 86% of words in common

    Find air-quality monitoring stations (measured by physical sensors, not modeled) near a point, within a bounding box, or by country, optionally narrowed to one parameter, one station class (reference monitors or low-cost sensors, mobile or fixed), or one provider network. Returns each station's id, name, coordinates, distance from the query point (when searching by coordinates), country, provider name and id, the parameters its sensors measure, and the timestamp of its most recent data (datetimeLast). Required first step: openaq_get_readings and openaq_get_measurements key on the location id this returns. Coverage is uneven and real — a station only reports the parameters it measures, and the absence of a nearby station means no monitoring there, not clean air. For dense modeled coverage anywhere on Earth, use open-meteo-mcp-server's air-quality tool instead.

Full history: list_changes · trend: quality JSON.

Checks run on what our credential-free, read-only probes observe; tools are never called and no code audit is performed. How it is computed.

Trust 64 / 100

provenance80
liveness0
freshness100
hygiene100

+repository +namespace-matches-repo -unreachable(9 failures) +updated-24d-ago

Derived from observable signals (official registry feed and our own credential-free probes); no code audit performed. How it is computed.

Dependencies

@cyanheads/[email protected] npmthis version is not known to the dependency resolver

@cyanheads/[email protected] npmthis version is not known to the dependency resolver

Dependency graphs from deps.dev; advisories from OSV.dev, including the GitHub Advisory Database and the PyPI Advisory Database; all CC BY 4.0. The graph is what a clean install of this version resolves today; nothing was installed or run. How it is checked.

Endpoints

remoteauthreachableuptime 30dp50protocollast ok
https://openaq.caseyjhand.com/mcp streamable-httpnoneno (9 failures)81%170 ms2026-07-282026-09-29
packageregistryversionruntimesecrets
@cyanheads/openaq-mcp-servernpm0.4.1nodeOPENAQ_API_KEY
@cyanheads/openaq-mcp-servernpm0.4.1nodeOPENAQ_API_KEY

Tools (7)

openaq_dataframe_describeread-onlyList the tables and columns staged on a DataCanvas so you can write valid SQL for openaq_dataframe_query without guessing column names. Returns each measurement table (measurements_<sensorId>) with its row count and column names. Requires DataCanvas to be enabled.
openaq_dataframe_queryread-onlyRun a read-only SQL SELECT against the measurement tables openaq_get_measurements staged on a DataCanvas. Reference tables by the name the measurements call returned (measurements_<sensorId>). For aggregation (monthly means, exceedance counts) and cross-sensor comparison over series too large to inline. Only SELECT is allowed — writes, DDL, and file/network table functions are rejected. Responses carry at most 200 rows; aggregate in SQL, or page with ORDER BY plus LIMIT/OFFSET, rather than selecting a whole table.
openaq_find_locationsread-onlyFind air-quality monitoring stations (measured by physical sensors, not modeled) near a point, within a bounding box, or by country, optionally narrowed to one parameter, one station class (reference monitors or low-cost sensors, mobile or fixed), or one provider network. Returns each station's id, name, coordinates, distance from the query point (when searching by coordinates), country, provider name and id, the parameters its sensors measure, and the timestamp of its most recent data (datetimeLast). Required first step: openaq_get_readings and openaq_get_measurements key on the location id this returns. Coverage is uneven and real — a station only reports the parameters it measures, and the absence of a nearby station means no monitoring there, not clean air. For dense modeled coverage anywhere on Earth, use open-meteo-mcp-server's air-quality tool instead.
openaq_get_measurementsread-onlyHistorical measurement series for one pollutant at one station over a date range — for trend analysis and "was last week worse than the monthly average?". Pass a locationId and a parametersId and work in stations — you get the series for that pollutant at that station. Choose aggregation: raw (every reported value), hourly, or daily — daily and hourly add a per-bucket statistical summary (min, median, max, mean, sd). A date-only bound means the station's local calendar day. Large ranges produce thousands of rows and stage on a DataCanvas: the response returns a preview plus a canvasId and table name — call openaq_dataframe_describe on the canvasId for the table's columns, then openaq_dataframe_query to run SQL over it. Passing a canvas_id stages the series there whatever its size, so two stations land on one canvas for a side-by-side comparison. Values carry their unit; the server never converts between µg/m³, ppm, and ppb.
openaq_get_readingsread-onlyLatest measured value for every sensor at a monitoring station — the current-conditions tool. Returns one record per parameter, each with the value, its unit, the UTC and local timestamp, and the sensor id, joined so every value carries its pollutant and unit (the raw latest feed is keyed only by sensor id). The station block names its provider (for attribution) and timezone. Pass a locationId from openaq_find_locations, or pass coordinates to auto-resolve to the nearest station that measures the requested parametersId. Data recency varies by station reporting cadence — read each value's timestamp to know whether "latest" is minutes or hours old. These are measured observations with coverage gaps, not a modeled grid.
openaq_list_countriesread-onlyCatalog of country-level coverage: id, OpenAQ country code, name, the date span of available station data (datetimeFirst/datetimeLast), and which parameters are measured anywhere in that country. The availability check before a regional sweep — answers "which countries have NO2 monitoring?" and tells you whether a country has recent data before you call openaq_find_locations. Coverage is uneven worldwide; this surfaces where measured data exists. Results come a page at a time (20 countries by default); totalCount is the full filtered count.
openaq_list_parametersread-onlyCatalog of every measurable pollutant and its canonical unit: id, code, display name, unit, and a one-line description (pm25, pm10, o3, no2, so2, co, bc, and more). This is the unit-disambiguation reference — the same pollutant exists under several ids with different units (CO is id 4 in µg/m³, id 8 in ppm, id 102 in ppb), so use this to pick the exact parametersId for openaq_find_locations / openaq_get_readings / openaq_get_measurements and to interpret a reading's unit. A small bounded catalog fetched live from OpenAQ.

Schemas: list_tools.

Similar MCP servers

More from io.github.cyanheads

And 140 more from this publisher.

Official server.json
{
  "name": "io.github.cyanheads/openaq-mcp-server",
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
  "remotes": [
    {
      "url": "https://openaq.caseyjhand.com/mcp",
      "type": "streamable-http"
    }
  ],
  "version": "0.4.1",
  "packages": [
    {
      "version": "0.4.1",
      "transport": {
        "type": "stdio"
      },
      "identifier": "@cyanheads/openaq-mcp-server",
      "runtimeHint": "node",
      "registryType": "npm",
      "registryBaseUrl": "https://registry.npmjs.org",
      "packageArguments": [
        {
          "type": "positional",
          "value": "run"
        },
        {
          "type": "positional",
          "value": "start:stdio"
        }
      ],
      "environmentVariables": [
        {
          "name": "OPENAQ_API_KEY",
          "format": "string",
          "isSecret": true,
          "isRequired": true,
          "description": "OpenAQ v3 API key, sent as the X-API-Key header. Free from openaq.org."
        },
        {
          "name": "OPENAQ_API_BASE_URL",
          "format": "string",
          "default": "https://api.openaq.org/v3",
          "description": "OpenAQ v3 API base URL override (testing / proxy)."
        },
        {
          "name": "CANVAS_PROVIDER_TYPE",
          "format": "string",
          "default": "none",
          "description": "Set to 'duckdb' to enable DataCanvas SQL over large measurement series."
        },
        {
          "name": "OPENAQ_ENABLE_CANVAS_DROP",
          "format": "boolean",
          "default": "false",
          "description": "Enable deletion of complete staged canvases; requires CANVAS_PROVIDER_TYPE=duckdb."
        },
        {
          "name": "MCP_LOG_LEVEL",
          "format": "string",
          "default": "info",
          "description": "Sets the minimum log level for output (e.g., 'debug', 'info', 'warn')."
        }
      ]
    },
    {
      "version": "0.4.1",
      "transport": {
        "url": "http://localhost:3010/mcp",
        "type": "streamable-http"
      },
      "identifier": "@cyanheads/openaq-mcp-server",
      "runtimeHint": "node",
      "registryType": "npm",
      "registryBaseUrl": "https://registry.npmjs.org",
      "packageArguments": [
        {
          "type": "positional",
          "value": "run"
        },
        {
          "type": "positional",
          "value": "start:http"
        }
      ],
      "environmentVariables": [
        {
          "name": "OPENAQ_API_KEY",
          "format": "string",
          "isSecret": true,
          "isRequired": true,
          "description": "OpenAQ v3 API key, sent as the X-API-Key header. Free from openaq.org."
        },
        {
          "name": "OPENAQ_API_BASE_URL",
          "format": "string",
          "default": "https://api.openaq.org/v3",
          "description": "OpenAQ v3 API base URL override (testing / proxy)."
        },
        {
          "name": "CANVAS_PROVIDER_TYPE",
          "format": "string",
          "default": "none",
          "description": "Set to 'duckdb' to enable DataCanvas SQL over large measurement series."
        },
        {
          "name": "OPENAQ_ENABLE_CANVAS_DROP",
          "format": "boolean",
          "default": "false",
          "description": "Enable deletion of complete staged canvases; requires CANVAS_PROVIDER_TYPE=duckdb."
        },
        {
          "name": "MCP_HTTP_HOST",
          "format": "string",
          "default": "127.0.0.1",
          "description": "The hostname for the HTTP server."
        },
        {
          "name": "MCP_HTTP_PORT",
          "format": "string",
          "default": "3010",
          "description": "The port to run the HTTP server on."
        },
        {
          "name": "MCP_HTTP_ENDPOINT_PATH",
          "format": "string",
          "default": "/mcp",
          "description": "The endpoint path for the MCP server."
        },
        {
          "name": "MCP_AUTH_MODE",
          "format": "string",
          "default": "none",
          "description": "Authentication mode to use: 'none', 'jwt', or 'oauth'."
        },
        {
          "name": "MCP_LOG_LEVEL",
          "format": "string",
          "default": "info",
          "description": "Sets the minimum log level for output (e.g., 'debug', 'info', 'warn')."
        }
      ]
    }
  ],
  "repository": {
    "url": "https://github.com/cyanheads/openaq-mcp-server",
    "source": "github"
  },
  "description": "Find air-quality stations and read pollutant observations from government monitors via OpenAQ v3."
}