openaq-mcp-server
Server Details
Find air-quality stations and read pollutant observations from government monitors via OpenAQ v3.
- 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 toolsopenaq_dataframe_describeopenaq-mcp-server: dataframe describeARead-onlyInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| canvas_id | Yes | DataCanvas id returned by openaq_get_measurements when a series spilled. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| notice | No | Guidance when the canvas holds no tables yet. |
| tables | No | Tables currently staged on the canvas. |
TDQS
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.
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.
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.
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.
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.
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 queryARead-onlyInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | Read-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_id | Yes | DataCanvas id returned by openaq_get_measurements when a series spilled. |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | No | Result rows (capped at the canvas row limit). |
| error | No | Present when the call failed. Absent on success. |
| rowCount | No | Full result count before the row cap. |
TDQS
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.
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.
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.
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.
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.
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 locationsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| iso | No | Restrict 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. | |
| bbox | No | Bounding 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). | |
| page | No | 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. | |
| limit | No | Max stations to return (1–100). Default 20. Results are ordered by distance when searching by coordinates. | |
| radius | No | Search radius in metres around coordinates (1–25000; the API hard-caps at 25000). Default 12000 (~12km). Only used with coordinates. | |
| coordinates | No | Center 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. | |
| parametersId | No | Only 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
| Name | Required | Description |
|---|---|---|
| cap | No | The limit that was applied. |
| error | No | Present when the call failed. Absent on success. |
| shown | No | Number of stations returned. |
| notice | No | Guidance when OpenAQ reports a lower-bound total without the result set hitting the limit. |
| locations | No | Matching 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. |
| truncated | No | True when the station list was capped at the limit. |
| totalCount | No | Total matching stations before the limit. A floor (not an exact count) when totalCountIsLowerBound is true. |
| totalCountIsLowerBound | No | True when OpenAQ reported a lower bound (">N"): totalCount is a floor and more stations match than the count shown. |
TDQS
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.
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.
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.
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.
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.
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 measurementsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max rows per page from the API (1–1000). Default 1000. The tool pages internally up to the spill threshold. | |
| canvas_id | No | DataCanvas 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. | |
| datetimeTo | No | 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". | |
| locationId | Yes | Station id from openaq_find_locations. | |
| aggregation | No | Time 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 |
| datetimeFrom | No | 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. | |
| parametersId | Yes | Parameter 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
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| notice | No | What limited this response, when something did — the row cap, a failed page, or DataCanvas being unavailable — plus how to reach the rest. |
| series | No | The (possibly previewed) series, newest or oldest first per the API. When truncated, this is a preview — query canvasId for the rows staged there. |
| canvasId | No | 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. |
| location | No | Station the series came from |
| rowCount | No | Rows in this response (preview length when spilled) |
| sensorId | No | Resolved sensor id the series was pulled from |
| parameter | No | What was measured, resolved from the station's sensor |
| tableName | No | Canvas table name for the staged series (e.g. "measurements_1701"). Reference it in SQL. |
| truncated | No | 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. |
| totalCount | No | Total rows in the full series. |
| aggregation | No | Bucketing applied |
TDQS
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.
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.
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.
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.
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.
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 readingsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| locationId | No | Station id from openaq_find_locations. Provide this OR coordinates. When set, returns the latest value for every sensor at this station. | |
| coordinates | No | Fallback "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. | |
| parametersId | No | Required 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
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| notice | No | Guidance when the station resolved but returned no recent values. |
| location | No | The station these readings came from |
| readings | No | Latest value per sensor. An old datetime means the station reports infrequently or is stale — not that the value is current. |
TDQS
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.
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.
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.
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.
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.
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 countriesARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | 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. | |
| parametersId | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| notice | No | Guidance when the query matched nothing. |
| countries | No | Matching countries with coverage metadata. |
| totalCount | No | Total countries matched after filtering. |
TDQS
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.
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.
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.
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.
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.
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 parametersARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | 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. | |
| pollutantsOnly | No | When true, exclude meteorological/auxiliary parameters (temperature, humidity, wind, pressure, particle-count channels) and return only air pollutants. Default false (full catalog). |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| notice | No | Guidance when the query matched nothing. |
| parameters | No | Matching parameters. Multiple rows can share a name with different ids/units — pick the id whose unit you want. |
| totalCount | No | Total parameters matched after filtering. |
TDQS
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.
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.
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.
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.
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.
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.
7 tool updates
- Changed
openaq_dataframe_describe6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "tables" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "tables" -]
- Changed
openaq_dataframe_query6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "rows", + "rowCount" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "rows", - "rowCount" -]
- Changed
openaq_find_locations6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "locations", + "totalCount" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "locations", - "totalCount" -]
- Changed
openaq_get_measurements6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "location", + "parameter", + "sensorId", + "aggregation", + "series", + "rowCount", + "totalCount" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "location", - "parameter", - "sensorId", - "aggregation", - "series", - "rowCount", - "totalCount" -]
- Changed
openaq_get_readings6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "location", + "readings" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "location", - "readings" -]
- Changed
openaq_list_countries6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "countries", + "totalCount" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "countries", - "totalCount" -]
- Changed
openaq_list_parameters6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "parameters", + "totalCount" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "parameters", - "totalCount" -]
1 tool update
- Changed
openaq_get_measurements11 fields changed- changed
Input schema / properties / datetimeFrom / descriptionPrevious 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." - changed
Input schema / properties / datetimeTo / descriptionPrevious 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\"." - changed
Output schema / properties / canvasId / descriptionPrevious 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." - changed
Output schema / properties / notice / descriptionPrevious 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." - changed
Output schema / properties / series / descriptionPrevious 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." - changed
Output schema / properties / series / items / properties / summary / descriptionPrevious 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" - added
Output schema / properties / series / items / properties / value / anyOfAdded value: +[ + { + "type": "number" + }, + { + "type": "null" + } +] - changed
Output schema / properties / series / items / properties / value / descriptionPrevious 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" - removed
Output schema / properties / series / items / properties / value / typeRemoved value: -"number" - changed
Output schema / properties / tableName / descriptionPrevious 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." - changed
Output schema / properties / truncated / descriptionPrevious 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."
2 tool updates
- Changed
openaq_find_locations1 field changed- added
Input schema / properties / pageAdded 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" +}
- Changed
openaq_list_countries1 field changed- added
Input schema / properties / parametersIdAdded 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" +}
3 tool updates
- Changed
openaq_find_locations3 fields changed- added
Output schema / properties / noticeAdded value: +{ + "description": "Guidance when OpenAQ reports a lower-bound total without the result set hitting the limit.", + "type": "string" +} - changed
Output schema / properties / totalCount / descriptionPrevious 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." - added
Output schema / properties / totalCountIsLowerBoundAdded value: +{ + "description": "True when OpenAQ reported a lower bound (\">N\"): totalCount is a floor and more stations match than the count shown.", + "type": "boolean" +}
- Changed
openaq_list_countries1 field changed- changed
Input schema / properties / query / descriptionPrevious 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."
- Changed
openaq_list_parameters1 field changed- changed
Input schema / properties / query / descriptionPrevious 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."
7 tool updates
- First observed
openaq_dataframe_describe - First observed
openaq_dataframe_query - First observed
openaq_find_locations - First observed
openaq_get_measurements - First observed
openaq_get_readings - First observed
openaq_list_countries - First observed
openaq_list_parameters
Frequently Asked Questions
Claiming proves that you control a remote MCP connector. It does not move, proxy, or interrupt the server.
Open the connector listing, choose Claim ownership, and sign in to Glama.
Complete one verification method:
GitHub identity — fastest for official registry listings. For a namespace such as
io.github.alice/server, link the matching GitHub user, then choose Claim with GitHub. An organization namespace such asio.github.acme/serveralso needs that organization to have installed the Glama AI GitHub App and approved its permissions, because GitHub discloses organization membership only to apps it has installed. Use HTTP or DNS when it has not.HTTP challenge — works when you can deploy a public file. Generate a token, publish the exact JSON Glama shows at
/.well-known/glama.jsonon the same origin as the connector, then choose Check HTTP challenge.DNS challenge — works when you control DNS but cannot change the server. Generate a token, create the exact TXT record Glama shows, wait for it to propagate, then choose Check DNS challenge.
After verification, Glama sends a confirmation email and gives you access to listing details, thumbnails, health checks, and analytics. Keep the HTTP file or DNS record in place: Glama periodically checks it and ownership remains verified while the token is discoverable.
The HTTP ownership file has this structure:
{
"$schema": "https://glama.ai/mcp/schemas/connector.json",
"claim": "glama_claim_..."
}Claim tokens are opaque, stable, and bound to the signed-in Glama account. They contain no email address or other personal information. If Glama can no longer discover a verified HTTP or DNS token, it starts a seven-day grace period before removing claim-based access. Restore the same token during that period to keep ownership verified. Never publish an email address, Glama session token, GitHub token, or connector credential as ownership proof.
If verification fails, confirm that you copied the current token exactly. The HTTP file must be public, return valid JSON with a successful HTTP response, and stay on the connector's origin. DNS changes may need more time to propagate. A claim cannot transfer to a different origin or hostname: if the connector target changes, Glama starts the grace period and the new target must be claimed separately after the previous claim is released.
For a connector linked to the official MCP Registry, registry updates continue to replace its name, description, and URL by default. After claiming, open Manage connector and enable Use Glama listing details as the source of truth if edits made on Glama should be preserved. Categories and thumbnails are always managed on Glama; registry linkage and technical connection settings continue to sync.
Control your server's listing on Glama, including description and metadata
Access analytics and receive server usage reports
Get monitoring and health status updates for your server
Feature your server to boost visibility and reach more users
To improve your MCP server's ranking:
Claim ownership of the server listing
Complete the server profile with an accurate description and thumbnail
Provide a test profile so Glama can connect to and evaluate the server
Keep tool definitions clear and complete to earn a high Tool Definition Quality Score (TDQS)
Route real usage through the Glama Gateway; more recorded successful server uses also improve the ranking
For users:
Full audit trail – every tool call is logged with inputs and outputs for compliance and debugging
Granular tool control – enable or disable individual tools per connector to limit what your AI agents can do
Centralized credential management – store and rotate API keys and OAuth tokens in one place
Change alerts – get notified when a connector changes its schema, adds or removes tools, or updates tool definitions, so nothing breaks silently
For server owners:
Proven adoption – public usage metrics on your listing show real-world traction and build trust with prospective users
Tool-level analytics – see which tools are being used most, helping you prioritize development and documentation
Direct user feedback – users can report issues and suggest improvements through the listing, giving you a channel you would not have otherwise
The connector status is unhealthy when Glama is unable to successfully connect to the server. This can happen for several reasons:
The server is experiencing an outage
The URL of the server is wrong
Credentials required to access the server are missing or invalid
If you are the owner of this MCP connector and would like to make modifications to the listing, including providing test credentials for accessing the server, please contact support@glama.ai.
Discussions
No comments yet. Be the first to start the discussion!
Related MCP Connectors
OpenAQ MCP — wraps OpenAQ v2 API (free, no auth required)
Search NOAA climate stations and datasets, fetch historical weather observations.
Search NOAA CDO stations and datasets, fetch historical weather observations.
Access UK air quality data, monitoring sites, and hourly pollutant measurements across regions
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables querying global air-quality data from the OpenAQ API, including nearest-station readings, station discovery, latest pollutant values, and historical time series for sensors.12MIT
- AlicenseNot gradedqualityCmaintenanceAccess air quality data from Open-Meteo API, free and without authentication.16MIT
- AlicenseAqualityBmaintenanceEnables querying Polish air-quality data from GIOŚ stations by providing tools to list stations, get sensor readings, and retrieve the composite air-quality index.4MIT
- AlicenseNot gradedqualityCmaintenanceProvides real-time Air Quality Index data from the World Air Quality Index service, enabling queries by location, station, or keyword search.6MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.
TDQS
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.
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.
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.
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.