Skip to main content
Glama

Server Details

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

If you are the author of this connector, you can claim ownership with GitHub, an HTTP challenge, or a DNS record. Claimed connector authors can inspect health checks, view analytics, and manage their listing.
Status
Healthy
Last Tested
Transport
Streamable HTTP
URL
Repository
cyanheads/openaq-mcp-server
GitHub Stars
2
Server Listing
@cyanheads/openaq-mcp-server

Available Tools

7 tools
openaq_dataframe_describeopenaq-mcp-server: dataframe describeA
Read-only
Inspect

List 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_) with its row count and column names. Requires DataCanvas to be enabled.

ParametersJSON Schema
NameRequiredDescriptionDefault
canvas_idYesDataCanvas id returned by openaq_get_measurements when a series spilled.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
noticeNoGuidance when the canvas holds no tables yet.
tablesNoTables currently staged on the canvas.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already indicate readOnlyHint=true, so the read-only nature is covered. The description adds detail about the return content (each measurement table with row count and column names) and the DataCanvas requirement, providing context beyond the annotation without contradicting it.

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

Conciseness5/5

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

Two sentences with zero waste. The primary action is front-loaded ('List the tables and columns'), followed by purpose, return specifics, and a prerequisite. Every sentence contributes necessary information.

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?

Given the presence of an output schema (as indicated by the context signal), the description need not detail return types. It already explains what the tool returns (tables, row counts, column names) and the prerequisite. An agent receives enough to call it correctly without missing context.

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?

Schema coverage is 100% and the canvas_id parameter has a clear, specific description ('DataCanvas id returned by openaq_get_measurements when a series spilled'). The tool description itself does not add parameter details, but the schema fully documents the parameter, so the baseline of 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 clearly states the verb (list), the resource (tables and columns on a DataCanvas), and the intended purpose (writing valid SQL for openaq_dataframe_query). It distinguishes itself from sibling query tools by focusing on schema discovery rather than data retrieval.

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 explains when to use: 'so you can write valid SQL for openaq_dataframe_query without guessing column names.' It also names the prerequisite ('Requires DataCanvas to be enabled') and implicitly positions it as a setup step before querying. No ambiguity about its role in the workflow.

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

openaq_dataframe_queryopenaq-mcp-server: dataframe queryA
Read-only
Inspect

Run 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_). 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.

ParametersJSON Schema
NameRequiredDescriptionDefault
sqlYesRead-only SELECT. Reference tables by the names openaq_get_measurements returned (e.g. measurements_1701). Use openaq_dataframe_describe first to see table and column names.
canvas_idYesDataCanvas id returned by openaq_get_measurements when a series spilled.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsNoResult rows (capped at the canvas row limit).
errorNoPresent when the call failed. Absent on success.
rowCountNoFull result count before the row cap.

TDQS

A4.3/5.0
Behavior4/5

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

Annotations declare readOnlyHint=true, and the description reinforces this with 'read-only SQL SELECT' and adds specifics about rejected operations ('writes, DDL, and file/network table functions'). This expands beyond the annotation, clarifying constraints and expectations.

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

Conciseness5/5

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

The description is compact (three sentences), front-loads the main action ('Run a read-only SQL SELECT'), and efficiently covers table references, typical use cases, and restrictions without redundancy.

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?

With an output schema present and full schema parameter coverage, the description supplies all essential context: allowed SQL subset, how to reference tables, and when to use the tool. No critical information is missing 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?

Schema description coverage is 100%, and both parameters (sql, canvas_id) already have detailed descriptions in the schema, including table naming and the recommendation to use describe first. The tool description reiterates this but does not add novel parameter-level meaning beyond the schema.

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 ('Run a read-only SQL SELECT') and resource ('measurement tables staged on a DataCanvas'), and distinguishes itself from siblings by focusing on querying staged measurement data. It clearly differentiates from retrieval tools like openaq_get_measurements and openaq_dataframe_describe.

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

Usage Guidelines4/5

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

It provides explicit usage context: for aggregation (monthly means, exceedance counts) and cross-sensor comparison over large series. It also recommends running openaq_dataframe_describe first to inspect table/column names. It doesn't explicitly state when not to use it, but the purpose makes that inferable.

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

openaq_find_locationsopenaq-mcp-server: find locationsA
Read-onlyIdempotent
Inspect

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
isoNoRestrict to a country by ISO 3166-1 alpha-2 code (e.g. "US", "IN", "DE"). Combine with bbox/coordinates to scope, or use alone for a country-wide list. Discover coverage with openaq_list_countries.
bboxNoBounding box as "minLon,minLat,maxLon,maxLat" (west,south,east,north). Alternative to coordinates+radius for area sweeps. Results have no distance field (no center point).
pageNoWhich page of results to return (1-based). Default 1. The only way past the 100-station cap: with limit 100, page 2 returns stations 101–200. Distance ordering applies within a page, not across pages, so paging is for iso/bbox sweeps — a near-me coordinates search should stay on page 1.
limitNoMax stations to return (1–100). Default 20. Results are ordered by distance when searching by coordinates.
radiusNoSearch radius in metres around coordinates (1–25000; the API hard-caps at 25000). Default 12000 (~12km). Only used with coordinates.
coordinatesNoCenter point as "latitude,longitude" (e.g. "47.6062,-122.3321"). Pair with radius for a near-me search. Resolve a place name to coordinates with openstreetmap-mcp-server or open-meteo geocode first. Provide either coordinates+radius OR bbox, not both.
parametersIdNoOnly return stations that measure this parameter id (e.g. 2 = PM2.5 µg/m³). Get ids from openaq_list_parameters — the same pollutant has several ids for different units. Narrows the station set; each returned station still lists all its sensors.

Output Schema

ParametersJSON Schema
NameRequiredDescription
capNoThe limit that was applied.
errorNoPresent when the call failed. Absent on success.
shownNoNumber of stations returned.
noticeNoGuidance when OpenAQ reports a lower-bound total without the result set hitting the limit.
locationsNoMatching stations. Empty array means no monitoring coverage for the query — NOT clean air. Widen the radius, try openaq_list_countries, or use the modeled open-meteo air-quality tool.
truncatedNoTrue when the station list was capped at the limit.
totalCountNoTotal matching stations before the limit. A floor (not an exact count) when totalCountIsLowerBound is true.
totalCountIsLowerBoundNoTrue when OpenAQ reported a lower bound (">N"): totalCount is a floor and more stations match than the count shown.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, openWorldHint, and idempotentHint, so the safety profile is covered. The description adds valuable context beyond that: coverage is uneven and real, absence of stations means no monitoring, not clean air, and distance is only returned for coordinate searches. This enriches the agent's mental model of result interpretation, though it does not disclose pagination quirks (which live in the schema).

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

Conciseness5/5

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

The description is concise but information-dense, with three clear sentences: purpose and selection criteria, return fields, then usage and caveats. It is front-loaded with the central action, and every sentence earns its place by adding distinct value (purpose, returns, sequence, limitation, alternative). No filler or redundancy.

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?

Given the tool has 7 optional parameters, a rich output schema, and clear annotations, the description covers all the essential contextual pieces: what the tool returns (key fields), how it relates to sibling tools (first step), its data-source limitations, and the recommended alternative for modeled data. Nothing an agent needs to choose and invoke it correctly is missing.

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 input schema has 100% description coverage, with each parameter explained in detail (e.g., coordinates pattern, bbox format, radius bounds, page/limit behavior). The description adds only marginal parameter-level detail, such as implying bbox vs coordinates via 'within a bounding box' and noting distance only when searching by coordinates. Since the schema already carries the full burden, a baseline of 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 ('Find') and a precise resource ('air-quality monitoring stations') with explicit selection criteria: near a point, within a bounding box, or by country. It also distinguishes itself from the modeled-data alternative by explicitly noting it uses physical sensors, and from sibling openaq tools by positioning itself as the required first step for readings/measurements. This leaves no ambiguity about what the tool does.

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?

The description gives explicit when-to-use guidance: it is the first step for openaq_get_readings and openaq_get_measurements, and it names the open-meteo alternative for dense modeled coverage. It also implicitly excludes modeled data by stating it is physical sensors only. This clearly routes the agent to the correct tool for each scenario.

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

openaq_get_measurementsopenaq-mcp-server: get measurementsA
Read-onlyIdempotent
Inspect

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 spill to a DataCanvas: the response returns a preview plus a canvasId and table name you query with openaq_dataframe_query. Values carry their unit; the server never converts between µg/m³, ppm, and ppb.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows per page from the API (1–1000). Default 1000. The tool pages internally up to the spill threshold.
canvas_idNoDataCanvas id from a prior call to reuse the same canvas (e.g. to compare two stations' series side by side). Omit to start fresh; the response returns a new canvas_id when the series spills.
datetimeToNoEnd of the range, inclusive. Date "YYYY-MM-DD" covers that whole day (closes at 23:59:59Z) or full UTC "YYYY-MM-DDTHH:MM:SSZ". Must land after datetimeFrom — the two forms mix freely, so "2026-06-25" to "2026-06-25" is a valid one-day range. Omit for "up to now".
locationIdYesStation id from openaq_find_locations.
aggregationNoTime bucketing. "raw" = every reported value (often hourly at source). "hourly"/"daily" = server-side rollups with a statistical summary per bucket. Use "daily" for multi-month trends to keep the series small; "raw" for fine-grained recent analysis.raw
datetimeFromNoStart of the range, inclusive. Date "YYYY-MM-DD" (opens at 00:00:00Z that day) or full UTC "YYYY-MM-DDTHH:MM:SSZ". Omit to get the most recent values.
parametersIdYesParameter id to pull the series for (e.g. 2 = PM2.5 µg/m³). Get ids from openaq_list_parameters. Must be a parameter the station measures — find_locations lists each station's parameters.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
noticeNoWhat limited this response, when something did — the row cap, a failed page, or DataCanvas being unavailable — plus how to reach the rest.
seriesNoThe (possibly previewed) series, newest or oldest first per the API. When truncated, this is a preview — query canvasId for the rows staged there.
canvasIdNoDataCanvas id holding the pulled series. Query with openaq_dataframe_query. The pull stops at 5000 rows, so this is the whole series only when totalCount is at or below that — read the notice, which says so when the cap or a failed page cut the pull short.
locationNoStation the series came from
rowCountNoRows in this response (preview length when spilled)
sensorIdNoResolved sensor id the series was pulled from
parameterNoWhat was measured, resolved from the station's sensor
tableNameNoCanvas table name for the staged series (e.g. "measurements_1701"). Reference it in SQL.
truncatedNoTrue when the series exceeded the inline limit, so series is a preview and the pulled rows were staged on canvasId. Absent/false when everything fit inline. It says nothing about whether the pull itself was complete — compare rowCount and totalCount, and read the notice.
totalCountNoTotal rows in the full series.
aggregationNoBucketing applied

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already carry readOnlyHint/openWorldHint/idempotentHint, lowering the burden. The description adds genuinely valuable non-obvious behavior beyond that: the DataCanvas spill mechanism (preview + canvasId + table name), internal paging up to a spill threshold, and the explicit unit non-conversion ('the server never converts between µg/m³, ppm, and ppb'). These give the agent actionable expectations not visible in annotations. It doesn't specify the exact spill threshold, a minor gap.

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?

Single dense paragraph, front-loaded with purpose before mechanics. Every sentence earns its place: use case, spill behavior, unit handling, and aggregation tradeoffs. Slightly run-on with semicolon/colon chains, but efficient overall and avoids redundancy with the schema.

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 7-parameter tool with an output schema, the description is strong: it covers the spill path (critical for large ranges), unit semantics, aggregation choice tradeoffs, and references openaq_find_locations/openaq_dataframe_query for id sourcing and follow-up. Since an output schema exists, return values need no explanation. Minor omissions are the exact spill threshold and explicit differentiation from get_readings.

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?

Schema description coverage is 100%, so the schema already documents every parameter thoroughly (datetime formats, defaults, limits, source of ids). The description adds some value — the per-bucket statistical summary composition (min, median, max, mean, sd) for hourly/daily aggregation, and the conceptual pairing of locationId+parametersId — but these are marginal additions over an already-rich schema.

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 — 'Historical measurement series for one pollutant at one station over a date range' — and immediately gives the intended use cases ('trend analysis', 'was last week worse than the monthly average?'). The single-pollutant, single-station scope distinguishes it from broader siblings like get_readings, and the DataCanvas spill explicitly ties it to openaq_dataframe_query.

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

Usage Guidelines4/5

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

The description gives clear selection context: 'work in stations', trend-focused use, and concrete aggregation guidance ('Use "daily" for multi-month trends... "raw" for fine-grained recent analysis'). It also names openaq_dataframe_query as the follow-up when results spill. However, it never explicitly says when NOT to use this tool versus the sibling openaq_get_readings, so the routing guidance stops short of full exclusions.

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

openaq_get_readingsopenaq-mcp-server: get readingsA
Read-onlyIdempotent
Inspect

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
locationIdNoStation id from openaq_find_locations. Provide this OR coordinates. When set, returns the latest value for every sensor at this station.
coordinatesNoFallback "latitude,longitude" when you do not have a locationId — resolves to the nearest station (within 25km) that measures parametersId, then reads its latest values. Requires parametersId.
parametersIdNoRequired with coordinates: which parameter id the nearest station must measure (get ids from openaq_list_parameters). With locationId, optionally filters the returned values to this parameter id; omit to get all sensors.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
noticeNoGuidance when the station resolved but returned no recent values.
locationNoThe station these readings came from
readingsNoLatest value per sensor. An old datetime means the station reports infrequently or is stale — not that the value is current.

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare readOnlyHint, openWorldHint, and idempotentHint, so the safety profile is covered. The description adds substantial behavioral context: the output structure (one record per parameter with value, unit, UTC/local timestamps, sensor id), the join that the raw feed lacks, the recency caveat tied to station cadence, and the warning that these are measured observations with gaps—not a modeled grid. This goes well beyond what annotations provide and sets accurate expectations.

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 description is dense but each sentence earns its place: it states the core purpose, details the returned record structure, gives both invocation routes, and flags the recency and data-coverage caveats. It is a bit longer than minimal but everything included is relevant. The structure front-loads the purpose and then expands logically. A slightly tighter wording could trim a few words, but no sentence is redundant.

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 tool with three optional parameters, two distinct call patterns, and an output schema already present, the description covers all the essential aspects an agent needs: how the result is shaped, how to select the target station, the dependency between parameters, and the reliability caveat about timestamps. The output schema covers the exact return fields, so the description does not need to repeat them. Nothing critical 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?

The input schema already gives detailed descriptions for all three parameters (100% coverage). The description adds value by explaining the relationship and constraints: locationId OR coordinates, coordinates as a 'fallback' that auto-resolves within 25km, and parametersId being 'Required with coordinates' while optionally filtering when locationId is used. This conditional logic is not obvious from the schema alone and meaningfully helps an agent invoke the tool correctly.

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 starts with a precise verb and resource: 'Latest measured value for every sensor at a monitoring station'. It also brands itself as 'the current-conditions tool', which cleanly separates it from siblings like openaq_get_measurements (historical data). The scope (per-sensor, latest value) and the join detail (carries pollutant and unit) leave no ambiguity about what the tool returns.

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

Usage Guidelines4/5

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

The description clearly explains the two entry modes (locationId or coordinates) and the condition that coordinates requires parametersId. It even names the source for locationId (openaq_find_locations). It stops short of explicitly stating 'use this for current conditions, use openaq_get_measurements for historical trends', but the 'current-conditions tool' label and the mention of recency implicitly frame the appropriate use case.

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

openaq_list_countriesopenaq-mcp-server: list countriesA
Read-onlyIdempotent
Inspect

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoCase-insensitive filter over the bounded country catalog (~153) by code and name. A two-letter query is treated as an exact ISO 3166-1 alpha-2 code (e.g. "US" → United States); longer queries match as substrings (e.g. "united", "germany"). Omit to list all.
parametersIdNoOnly return countries that measure this parameter id somewhere (e.g. 2 = PM2.5 µg/m³) — the one-call answer to "which countries have NO2 monitoring?". Get ids from openaq_list_parameters; the same pollutant has several ids for different units. Composes with query.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
noticeNoGuidance when the query matched nothing.
countriesNoMatching countries with coverage metadata.
totalCountNoTotal countries matched after filtering.

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and idempotentHint=true, so the safety profile is covered. The description adds context that coverage is uneven worldwide, that the catalog is bounded (~153 countries), and that the date span reveals recency of data. This goes beyond what annotations state and helps the agent understand the tool's limitations without contradicting any annotation.

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

Conciseness5/5

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

Two compact sentences front-load the core value (country-level coverage) and then the practical use (availability check before find_locations). No filler or redundancy; every sentence earns its place.

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?

Given the tool is read-only, idempotent, has a fully documented 2-parameter schema, and includes an output schema, the description fully covers what the agent needs to decide when and how to call it. It explains the data's meaning, the usage path, and the coverage caveat. Nothing critical is missing.

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?

Schema coverage is 100% and both parameters are described in detail: query explains case-insensitive exact-vs-substring behavior and the two-letter ISO code rule; parametersId explains filtering by id, gives an example, notes multiple ids per pollutant, and mentions composition with query. The description itself does not add parameter-level semantics beyond what the schema provides, so the baseline of 3 applies.

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 uses a specific verb ('list') with a clear resource ('countries') and states exactly what the catalog contains: id, ISO code, name, date span, and measured parameters. It also frames the purpose as an availability check and a pre-step to openaq_find_locations, making it distinct from siblings like openaq_list_parameters or openaq_find_locations.

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

Usage Guidelines4/5

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

The description explicitly tells when to use it: before a regional sweep and before calling openaq_find_locations, and gives concrete questions it answers (e.g., 'which countries have NO2 monitoring?', 'whether a country has recent data'). It does not explicitly state when not to use it, but the context is clear and no direct sibling competes for this narrow role, so exclusion is not critical.

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

openaq_list_parametersopenaq-mcp-server: list parametersA
Read-onlyIdempotent
Inspect

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoCase-insensitive filter over the bounded parameter catalog (~44) by code, display name, and description (e.g. "pm" for particulates, "ozone", "co"). Omit to list everything.
pollutantsOnlyNoWhen true, exclude meteorological/auxiliary parameters (temperature, humidity, wind, pressure, particle-count channels) and return only air pollutants. Default false (full catalog).

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
noticeNoGuidance when the query matched nothing.
parametersNoMatching parameters. Multiple rows can share a name with different ids/units — pick the id whose unit you want.
totalCountNoTotal parameters matched after filtering.

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare readOnlyHint and idempotentHint, covering safety. The description adds behavioral context: 'A small bounded catalog fetched live from OpenAQ' reveals that the tool performs a live fetch and returns a limited set, which is useful for planning. It also notes the default behavior for pollutantsOnly via the parameter, but that is schema-covered. No contradictions with annotations; the added info is valuable but not extensive.

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

Conciseness5/5

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

The description is three sentences with zero fluff. It leads with the tool's core function (catalog of pollutants and units), then explains purpose and usage, and ends with a note on the bounded live fetch. Every sentence earns its place, and the critical unit-disambiguation point is front-loaded.

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 simple-list tool with a rich output schema, the description is complete. It covers the tool's output content (fields), the filtering parameters, the use cases, and the bounded nature. It does not need to describe return format because the output schema defines it. There are no obvious gaps an agent would need to know to call it correctly.

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?

Schema description coverage is 100%, so the baseline is 3. The description does not add new parameter meanings beyond what the schema already provides: the query parameter's filtering behavior and the pollutantsOnly flag are both fully documented in the schema with examples and defaults. The description reuses the same information without extension, so no extra semantic value is added.

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 clearly states the tool lists a catalog of pollutants with canonical units, specifying the exact fields (id, code, display name, unit, description). It distinguishes itself from siblings like openaq_list_countries by focusing on pollutant parameters and unit disambiguation, and it explicitly mentions the sibling tools (openaq_find_locations, openaq_get_readings, openaq_get_measurements) that it supports. Purpose is unambiguous.

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

Usage Guidelines4/5

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

The description gives explicit when-to-use guidance: 'use this to pick the exact parametersId for openaq_find_locations / openaq_get_readings / openaq_get_measurements and to interpret a reading's unit.' It also indicates that omitting the query lists everything, which clarifies a common usage. It does not explicitly state when not to use it or name alternatives to avoid, but the context is strong enough to route an agent correctly.

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. Dates show when Glama detected each change.

  1. 7 tool updates
    • Changedopenaq_dataframe_describe6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "tables"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: DataCanvas is not enabled (CANVAS_PROVIDER_TYPE is not duckdb). `canvas_not_found`: The canvas_id is unknown or its canvas has expired. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "canvas_unavailable",
        +            "canvas_not_found"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "tables"
        -]
    • Changedopenaq_dataframe_query6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "rows",
        +      "rowCount"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: DataCanvas is not enabled (CANVAS_PROVIDER_TYPE is not duckdb). `canvas_not_found`: The canvas_id is unknown or its canvas has expired. `missing_table`: The SQL references a table that is not staged on this canvas (dropped, expired, or misspelled). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "canvas_unavailable",
        +            "canvas_not_found",
        +            "missing_table"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "rows",
        -  "rowCount"
        -]
    • Changedopenaq_find_locations6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "locations",
        +      "totalCount"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `no_locations_found`: No monitoring stations match the given area or filters. `no_search_scope`: None of coordinates, bbox, or iso was provided. `upstream_error`: OpenAQ returned 5xx or an unreadable body on every retry. `rate_limited`: OpenAQ returned 429 — the request budget for this key is exhausted. `upstream_timeout`: OpenAQ did not respond within the request timeout on every retry. `invalid_api_key`: OpenAQ returned 401 — the configured OPENAQ_API_KEY is missing, invalid, or revoked. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "no_locations_found",
        +            "no_search_scope",
        +            "upstream_error",
        +            "rate_limited",
        +            "upstream_timeout",
        +            "invalid_api_key"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "locations",
        -  "totalCount"
        -]
    • Changedopenaq_get_measurements6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "location",
        +      "parameter",
        +      "sensorId",
        +      "aggregation",
        +      "series",
        +      "rowCount",
        +      "totalCount"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `location_not_found`: The locationId does not exist. `parameter_not_at_location`: No sensor at the station measures parametersId (often the wrong unit variant was chosen). `no_data_for_range`: The sensor has no measurements in the requested date range. `invalid_date_range`: The range is empty — once both bounds are expanded to full UTC timestamps, datetimeTo does not land after datetimeFrom. `canvas_not_found`: The supplied canvas_id is unknown or has expired, so the series cannot be staged onto it. `upstream_error`: OpenAQ returned 5xx or an unreadable body on every retry. `rate_limited`: OpenAQ returned 429 — the request budget for this key is exhausted. `upstream_timeout`: OpenAQ did not respond within the request timeout on every retry. `invalid_api_key`: OpenAQ returned 401 — the configured OPENAQ_API_KEY is missing, invalid, or revoked. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "location_not_found",
        +            "parameter_not_at_location",
        +            "no_data_for_range",
        +            "invalid_date_range",
        +            "canvas_not_found",
        +            "upstream_error",
        +            "rate_limited",
        +            "upstream_timeout",
        +            "invalid_api_key"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "location",
        -  "parameter",
        -  "sensorId",
        -  "aggregation",
        -  "series",
        -  "rowCount",
        -  "totalCount"
        -]
    • Changedopenaq_get_readings6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "location",
        +      "readings"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `location_not_found`: The locationId does not exist (API returns {\"detail\":\"Location not found\"}). `parameter_not_at_location`: No sensor at the resolved station measures parametersId (often the wrong unit variant was chosen). `no_station_near_coordinates`: The 25km auto-resolution sweep found no station measuring the requested parametersId. `no_recent_values`: The station has the requested sensors but its latest feed carried no values for them. `invalid_location_scope`: Both locationId and coordinates were provided, or neither was. `missing_coordinates_parameter`: coordinates was provided without parametersId. `upstream_error`: OpenAQ returned 5xx or an unreadable body on every retry. `rate_limited`: OpenAQ returned 429 — the request budget for this key is exhausted. `upstream_timeout`: OpenAQ did not respond within the request timeout on every retry. `invalid_api_key`: OpenAQ returned 401 — the configured OPENAQ_API_KEY is missing, invalid, or revoked. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "location_not_found",
        +            "parameter_not_at_location",
        +            "no_station_near_coordinates",
        +            "no_recent_values",
        +            "invalid_location_scope",
        +            "missing_coordinates_parameter",
        +            "upstream_error",
        +            "rate_limited",
        +            "upstream_timeout",
        +            "invalid_api_key"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "location",
        -  "readings"
        -]
    • Changedopenaq_list_countries6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "countries",
        +      "totalCount"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `upstream_error`: OpenAQ /countries returned 5xx or an unreadable body on every retry. `rate_limited`: OpenAQ returned 429 — the request budget for this key is exhausted. `upstream_timeout`: OpenAQ /countries did not respond within the request timeout on every retry. `invalid_api_key`: OpenAQ returned 401 — the configured OPENAQ_API_KEY is missing, invalid, or revoked. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "upstream_error",
        +            "rate_limited",
        +            "upstream_timeout",
        +            "invalid_api_key"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "countries",
        -  "totalCount"
        -]
    • Changedopenaq_list_parameters6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "parameters",
        +      "totalCount"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `upstream_error`: OpenAQ /parameters returned 5xx or an unreadable body on every retry. `rate_limited`: OpenAQ returned 429 — the request budget for this key is exhausted. `upstream_timeout`: OpenAQ /parameters did not respond within the request timeout on every retry. `invalid_api_key`: OpenAQ returned 401 — the configured OPENAQ_API_KEY is missing, invalid, or revoked. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "upstream_error",
        +            "rate_limited",
        +            "upstream_timeout",
        +            "invalid_api_key"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "parameters",
        -  "totalCount"
        -]
  2. 1 tool update
    • Changedopenaq_get_measurements11 fields changed
      • changedInput schema / properties / datetimeFrom / description
        Previous value: -"Start of the range, inclusive. Date \"YYYY-MM-DD\" or full UTC \"YYYY-MM-DDTHH:MM:SSZ\". Omit to get the most recent values."New value: +"Start of the range, inclusive. Date \"YYYY-MM-DD\" (opens at 00:00:00Z that day) or full UTC \"YYYY-MM-DDTHH:MM:SSZ\". Omit to get the most recent values."
      • changedInput schema / properties / datetimeTo / description
        Previous value: -"End of the range, inclusive. Must be on or after datetimeFrom. Omit for \"up to now\"."New value: +"End of the range, inclusive. Date \"YYYY-MM-DD\" covers that whole day (closes at 23:59:59Z) or full UTC \"YYYY-MM-DDTHH:MM:SSZ\". Must land after datetimeFrom — the two forms mix freely, so \"2026-06-25\" to \"2026-06-25\" is a valid one-day range. Omit for \"up to now\"."
      • changedOutput schema / properties / canvasId / description
        Previous value: -"DataCanvas id holding the full series. Query with openaq_dataframe_query."New value: +"DataCanvas id holding the pulled series. Query with openaq_dataframe_query. The pull stops at 5000 rows, so this is the whole series only when totalCount is at or below that — read the notice, which says so when the cap or a failed page cut the pull short."
      • changedOutput schema / properties / notice / description
        Previous value: -"Degraded-mode hint when the series was truncated but DataCanvas is unavailable."New value: +"What limited this response, when something did — the row cap, a failed page, or DataCanvas being unavailable — plus how to reach the rest."
      • changedOutput schema / properties / series / description
        Previous value: -"The (possibly previewed) series, newest or oldest first per the API. When truncated, this is a preview — query canvasId for the full set."New value: +"The (possibly previewed) series, newest or oldest first per the API. When truncated, this is a preview — query canvasId for the rows staged there."
      • changedOutput schema / properties / series / items / properties / summary / description
        Previous value: -"Per-bucket statistics — present for hourly/daily, null for raw"New value: +"Per-bucket statistics — present for hourly/daily, null for raw. Every field is null in a gap bucket"
      • addedOutput schema / properties / series / items / properties / value / anyOf
        Added value: +[
        +  {
        +    "type": "number"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedOutput schema / properties / series / items / properties / value / description
        Previous value: -"Value for the bucket (the measurement for raw; the bucket aggregate for hourly/daily)"New value: +"Value for the bucket (the measurement for raw; the bucket aggregate for hourly/daily). Null for a gap bucket the sensor reported nothing into — the bucket is kept so the series stays evenly spaced on the time axis"
      • removedOutput schema / properties / series / items / properties / value / type
        Removed value: -"number"
      • changedOutput schema / properties / tableName / description
        Previous value: -"Canvas table name for the full series (e.g. \"measurements_1701\"). Reference it in SQL."New value: +"Canvas table name for the staged series (e.g. \"measurements_1701\"). Reference it in SQL."
      • changedOutput schema / properties / truncated / description
        Previous value: -"True when the series exceeded the inline limit and the full set was staged on canvasId. Absent/false when everything fit inline."New value: +"True when the series exceeded the inline limit, so series is a preview and the pulled rows were staged on canvasId. Absent/false when everything fit inline. It says nothing about whether the pull itself was complete — compare rowCount and totalCount, and read the notice."
  3. 2 tool updates
    • Changedopenaq_find_locations1 field changed
      • addedInput schema / properties / page
        Added value: +{
        +  "default": 1,
        +  "description": "Which page of results to return (1-based). Default 1. The only way past the 100-station cap: with limit 100, page 2 returns stations 101–200. Distance ordering applies within a page, not across pages, so paging is for iso/bbox sweeps — a near-me coordinates search should stay on page 1.",
        +  "maximum": 9007199254740991,
        +  "minimum": 1,
        +  "type": "integer"
        +}
    • Changedopenaq_list_countries1 field changed
      • addedInput schema / properties / parametersId
        Added value: +{
        +  "description": "Only return countries that measure this parameter id somewhere (e.g. 2 = PM2.5 µg/m³) — the one-call answer to \"which countries have NO2 monitoring?\". Get ids from openaq_list_parameters; the same pollutant has several ids for different units. Composes with query.",
        +  "maximum": 9007199254740991,
        +  "minimum": -9007199254740991,
        +  "type": "integer"
        +}
  4. 3 tool updates
    • Changedopenaq_find_locations3 fields changed
      • addedOutput schema / properties / notice
        Added value: +{
        +  "description": "Guidance when OpenAQ reports a lower-bound total without the result set hitting the limit.",
        +  "type": "string"
        +}
      • changedOutput schema / properties / totalCount / description
        Previous value: -"Total matching stations before the limit."New value: +"Total matching stations before the limit. A floor (not an exact count) when totalCountIsLowerBound is true."
      • addedOutput schema / properties / totalCountIsLowerBound
        Added value: +{
        +  "description": "True when OpenAQ reported a lower bound (\">N\"): totalCount is a floor and more stations match than the count shown.",
        +  "type": "boolean"
        +}
    • Changedopenaq_list_countries1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Local case-insensitive filter on country code and name (e.g. \"united\", \"IN\", \"germany\"). The list is bounded (~153 countries); omit to list all. Filters the fetched list on our side, not an upstream search."New value: +"Case-insensitive filter over the bounded country catalog (~153) by code and name. A two-letter query is treated as an exact ISO 3166-1 alpha-2 code (e.g. \"US\" → United States); longer queries match as substrings (e.g. \"united\", \"germany\"). Omit to list all."
    • Changedopenaq_list_parameters1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Local case-insensitive filter on code, display name, and description (e.g. \"pm\" for particulates, \"ozone\", \"co\"). The full catalog is small (~44 entries); omit to list everything. This filters the fetched list on our side — it is not an upstream search."New value: +"Case-insensitive filter over the bounded parameter catalog (~44) by code, display name, and description (e.g. \"pm\" for particulates, \"ozone\", \"co\"). Omit to list everything."
  5. 7 tool updates
    • First observedopenaq_dataframe_describe
    • First observedopenaq_dataframe_query
    • First observedopenaq_find_locations
    • First observedopenaq_get_measurements
    • First observedopenaq_get_readings
    • First observedopenaq_list_countries
    • First observedopenaq_list_parameters

Frequently Asked Questions

Discussions

No comments yet. Be the first to start the discussion!

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables querying global air-quality data from the OpenAQ API, including nearest-station readings, station discovery, latest pollutant values, and historical time series for sensors.
    12
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables querying Polish air-quality data from GIOŚ stations by providing tools to list stations, get sensor readings, and retrieve the composite air-quality index.
    4
    MIT
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

TDQS

A4.4/5.0
Disambiguation5/5

Each tool serves a distinct purpose: catalog discovery (list_countries, list_parameters), station lookup (find_locations), current data (get_readings), historical data (get_measurements), and DataCanvas query support (dataframe_describe, dataframe_query). No two tools overlap in function.

Naming Consistency4/5

The prefix 'openaq_' is consistent, and most tools follow a verb_noun pattern (find_locations, get_measurements, list_countries). However, 'dataframe_describe' and 'dataframe_query' invert this to noun_verb, a minor deviation that could confuse agents.

Tool Count5/5

Seven tools covers the full workflow (discover, locate, read current/historical, and handle large data) without redundancy. This is a well-scoped surface for an air quality data server.

Completeness5/5

The set provides end-to-end capability: metadata discovery, location search, current conditions, historical time series, and a mechanism for large SQL queries. No critical gaps like missing update/delete operations exist because the server is read-only by design.