icos-mcp
OfficialReturns the DOI, citation and licence for every dataset from the ICOS data passport, so data answers and the get_citation tool carry the persistent identifier to cite.
Offered as an identity provider for signing in to the server: users can authenticate through Google via the ICOS Keycloak realm.
Delegates sign-in to an ICOS Keycloak realm over OAuth: the server acts as a resource server, publishes /.well-known/oauth-protected-resource so MCP clients can discover, register and redirect users to Keycloak, and validates realm-signed tokens carrying the server's audience. Keycloak groups (below /apps/mcp) also drive per-store access rules, so stores can be opened or closed to group members without changing config.
Offered as an identity provider for signing in to the server: users can authenticate through ORCID via the ICOS Keycloak realm.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@icos-mcpfind stations measuring methane near Berlin and show recent observations"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
icos-mcp
ICOS carbon and greenhouse-gas observations for AI assistants, over the
Model Context Protocol. A thin server over the ICOS zarr proxy
(https://zarr.icos-cp.eu): every number comes from the proxy, and every
data answer carries the citation, licence and DOI. The plan, and why it
is built this way, is docs/plan.md.
Phases 1 and 2: seven read-only tools, five resources, three prompts, and a per-store access policy.
tool | does |
| the stores, with domain, coverage, licence, DOI |
| variable ids by name, quantity or unit ("methane" finds |
| one variable in full, with how its QC flag is read |
| the stations carrying a variable: near a point, in a box, by name or country |
| how big a selection is, before fetching it |
| at most 2,000 rows, the total, a CSV download URL for the whole selection, the citation |
| citation, licence and DOI from the data passport |
Run it
uv run icos-mcp # stdio, for a local client
uv run icos-mcp --transport streamable-http --port 8090 # http://127.0.0.1:8090/mcpICOS_PROXY points it at another proxy (the RC, a test instance).
Over HTTP the server also answers GET / with an HTML page in the style
of zarr.icos-cp.eu -- built from what it offers an anonymous caller: the
endpoint and how to connect, the public datasets, the tools and prompts,
the citation rule (landing.py; ICOS_MCP_PUBLIC_URL sets the endpoint
it names, default https://mcp.icos-cp.eu) -- and GET /health.
Related MCP server: Climatiq MCP Server
Which stores it offers
DEFAULT-DENY: a store is offered only when a rule names it, whatever the
proxy behind the server would serve. Without a config file the rules are
exactly the public stores of zarr.icos-cp.eu (config/public.json writes
them out). ICOS_MCP_CONFIG=/path/to/config.json sets the proxy, the
Host header to send it, the base of download links, and an ordered list
of store rules, each public or token:
uv run icos-mcp --show-policy # what this config offers, to whom
uv run icos-mcp --hash-token TOKEN # the line a tokens_file takesconfig/ct122.example.json is the shape for running beside zpd on CT122,
where zpd answers for EVERY store on the machine (the shuttle, GlobalView,
AGAGE): the public stores by name, those three behind tokens, anything
else hidden. A token store is offered to an HTTP caller sending
Authorization: Bearer <token> whose SHA-256 is in its tokens_file
(plain tokens are never stored); a local stdio caller is the operator and
uses ICOS_MCP_TOKEN, which never speaks for an HTTP caller. A hidden
store answers exactly as one that does not exist, and the static
icos://catalogue resource -- which the protocol does not tell who is
asking -- lists the public stores only.
Signing in
An oauth section in the config makes signing in required over HTTP.
Anyone can sign in. The aim is to know who uses the server, not to keep
anyone out:
"oauth": {"issuer": "https://sso.fs4vm.icos-cp.eu/realms/icos-test",
"audience": "https://mcp.icos-cp.eu"}The server is only a resource server. ICOS's Keycloak handles the
sign-in, through cpauth, Google or ORCID. MCP clients find Keycloak in
/.well-known/oauth-protected-resource, register themselves with it and
send the user to sign in. A token must be signed by the realm and carry
this server's audience, which needs an audience mapper in Keycloak.
The store tokens above still work. The landing page and /health stay
open, and the stdio operator is never asked to sign in (auth.py).
Signing in also allows a group store rule. The store is offered to
members of a Keycloak group, or of a subgroup of one, and to no one else.
The groups come from the verified token's groups claim:
{"match": "agage*", "access": "group", "groups": ["agage"],
"public_url": "https://agage-zarr.icos-cp.eu"}Group names are paths below /apps/mcp in the realm: agage is
/apps/mcp/agage. That is the only part of a user's groups Keycloak
sends this server.
Access is granted and withdrawn in Keycloak, not in the config. A group
rule may also list store tokens for scripts. A group rule without an
oauth section is refused at load, because nobody could reach it.
In Claude Code:
claude mcp add icos -- uv --directory /path/to/zarrm9/mcp run icos-mcpResources: icos://stores/{store}/agent-guide, icos://stores/{store}/croissant,
icos://catalogue, icos://llms.txt, icos://data-passport. Prompts:
station_record, compare_stations, ocean_region.
Test it
uv run pytest # over RECORDED proxy answers (tests/cassette)
ICOS_CASSETTE_MODE=record uv run pytest # re-record from the live proxy
ICOS_LIVE=1 uv run pytest # live, nothing recorded
uv run ruff check src tests
conformance/run.sh # the MCP conformance suite (needs Node)conformance/run.sh starts a local server and runs the MCP project's own
conformance suite against it, pinned to 0.1.16 by
conformance/package-lock.json. conformance/baseline.yml lists the
known failures and why each one fails (mostly the suite's own test
fixtures, which a real server doesn't carry). It is read in both
directions: a new failure fails the run, and so does an entry that now
passes.
eval/questions.json is the DRAFT evaluation set: 30 questions across
the three domains, each naming the variable and station a correct answer
must use and a truth computed from the proxy at run time. It needs one
reviewer per domain before it is relied on; the runner is not written yet.
This server is the ORACLE for the M9 port (mcpd, plan phase 7): its tool
names, descriptions and answer shapes are the contract the port is
replayed against, so change them deliberately.
Licence
Copyright (C) 2026 ICOS ERIC -- Carbon Portal.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version. See LICENSE.
The DATA this server serves are not covered by this licence: each answer carries the licence of its dataset (ICOS data are CC BY 4.0) and the citation to give with it.
Available Tools
8 toolsaggregateARead-onlyIdempotentInspect
Statistics over a WHOLE selection, computed on the server: count, mean,
min, max, sum and standard deviation of one variable, overall or per
station, year, month or day. Use this for any average, total, range or
trend instead of reading rows. Any size: a selection over 500,000 rows
is split by time on the server and merged exactly. std is the
POPULATION standard deviation (ddof 0). sum adds the values as
stored, which is a total only for values that are themselves totals per
row: summing a RATE (umol m-2 s-1 per half-hour, gC m-2 d-1 per day)
or a concentration gives no physical quantity -- for a flux total
multiply the mean by the period.
| Name | Required | Description | Default |
|---|---|---|---|
| by | No | the groups: none (one result), station, a period, or both | none |
| id | Yes | a variable id from search_variables | |
| end | No | ISO date or datetime, EXCLUSIVE | |
| start | No | ISO date or datetime, inclusive (2025-01-01) | |
| cruise | No | cruise expocodes, a list or a string; ocean observation tables only | |
| lat_max | No | northern edge of a bounding box, degrees north | |
| lat_min | No | southern edge of a bounding box, degrees north | |
| lon_max | No | eastern edge of a bounding box, degrees east | |
| lon_min | No | western edge of a bounding box, degrees east | |
| station | No | station ids -- a list, or one id (or several, comma-separated) as a string; CASE-SENSITIVE: HTM150 (trigram + sampling height) for atmosphere, SE-Htm for FLUXNET sites | |
| apply_qc | No | DEFAULT TRUE: drop the observations the store's recommended quality control rejects; false keeps every observation, flagged ones included | |
| question | No | the user's request as they wrote it; kept only if the user agreed in the licence form to share their questions anonymously, otherwise dropped | |
| height_max | No | highest sampling height above ground, metres (station series) | |
| height_min | No | lowest sampling height above ground, metres (station series) |
Output Schema
| Name | Required | Description |
|---|---|---|
| by | Yes | |
| id | Yes | |
| note | No | |
| unit | No | |
| groups | Yes | one entry per group: its keys (station, period), n (finite values), and mean, min, max, sum, std over them (null when n is 0) |
| citation | Yes | |
| n_groups | Yes | |
| variable | Yes | |
| rows_used | Yes | |
| download_url | Yes | |
| download_urls | No | |
| download_complete | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds real behavior beyond the read-only/idempotent annotations: exact server-side splitting and merging for selections over 500,000 rows, and that `std` is population (ddof 0). It adds little else the agent needs in order to call it safely; it extends useful behavioral detail but stops short of discussing failures or empty results.
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?
Front-loads what is computed and the routing advice, then adds two justified caveats about `std` and `sum` (the caveat saves the agent from producing physical nonsense). The `sum` caveat runs long with its parenthetical unit examples but each 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?
Output schema exists, so return values need no explanation, and the description covers the aggregation semantics and large-selection behavior for a 14-parameter tool. It is slightly incomplete for parameters in the schema (apply_qc default true, station/height filters) that materially change what gets aggregated and are documented only in the schema.
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%, so every parameter already carries its own description and the baseline applies. The description adds only the notion of a single variable and the grouping axis ('overall or per station, year, month or day'), which largely restates the `by` enum, and says nothing about the spatial/temporal/QC filters.
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?
States a concrete verb and resource: server-computed statistics (count, mean, min, max, sum, std) over a whole selection of one variable, grouped by station/period. It explicitly distinguishes itself from row-fetching siblings with 'instead of reading rows', so an agent can route correctly without opening the schema.
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?
Gives explicit when-to-use guidance ('Use this for any average, total, range or trend instead of reading rows') that names the alternative behavior (reading rows via get_data). It also warns when not to trust `sum` (rates/concentrations) and tells the agent what to do instead (multiply the mean by the period).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
describe_variableBRead-onlyIdempotentInspect
One variable in full: names, unit, which dataset, its coverage, how its quality flag is read, licence and DOI. For a multi-dimensional variable read through zarr (access 'zarr'), its axes with their labels, the slice /query serves (prescribed) and an xarray line for any other slice.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | a variable id from search_variables |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| doi | No | |
| ecv | No | the GCOS Essential Climate Variable this observation contributes to; ICOS provides the observation, not the ECV product |
| axes | No | zarr variables: each extra axis and its labels |
| name | Yes | |
| unit | No | |
| store | Yes | |
| access | No | query: estimate/get_data/aggregate serve it; zarr: a multi-dimensional variable (e.g. the ONEFlux NEE cube) read with xarray -- see zarr_recipe, and `prescribed` for its /query slice |
| domain | No | |
| license | No | |
| ecv_role | No | observation, or derived and how (gap-filled, partitioned, aggregated) |
| variants | No | the multi-dimensional variables this one is the PRESCRIBED slice of; their other slices are read through zarr (describe_variable on one gives the recipe) |
| long_name | No | |
| prescribed | No | zarr variables: the id of the preferred slice /query serves |
| query_kind | Yes | station_time (series at stations), obs_table (ship/cruise observations) or grid (gridded product) |
| zarr_recipe | No | |
| ecv_relation | No | 'related' when the variable is outside the ECV's own variable set (NEE to the greenhouse-gas ECV) |
| quality_flag | No | |
| dataset_title | Yes | |
| units_also_as | No | the same quantity in other communities' units: multiply by factor (e.g. umol CO2 m-2 s-1 to gC m-2 d-1 or t CO2 km-2 yr-1) |
| vocabulary_uri | No | |
| quality_control | No | how the store's QC flag is read; apply_qc=true applies the recommended rule |
| cf_standard_name | No | the CF standard name, given only for an exact match |
| time_coverage_end | No | |
| vocabulary_issues | No | a gap or an error in CF or in the ECV vocabulary for this variable -- read it before comparing with a CF-named field elsewhere |
| time_coverage_start | No | |
| vocabulary_mappings | No | SKOS mappings to NVS terms: the CF standard name (P07) as exactMatch or closeMatch with the conversion from this variable's units (factor, offset, to_units; a factor of -1 x ... is a sign flip), and the ECV (EXV) it contributes to |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld and non-destructive, so the safety profile is covered. The description's additional content is almost entirely about what data comes back, which overlaps with the existing output schema. It adds no operational context such as cost, size limits, or failure modes for unknown ids.
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?
It is compact and front-loads the return contract, which is good. However, the second sentence is dense and partly opaque ('the slice /query serves (prescribed)', 'an xarray line for any other slice') and spends its weight on zarr-specific return mechanics rather than on how to call the tool.
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 one-parameter read tool with full schema coverage, an output schema and complete annotations, most required information already exists in structured fields. The description nonetheless leans on return-value detail that the output schema already supplies, while leaving agent-facing decisions such as when to prefer get_data unaddressed.
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 single parameter is documented in the schema as 'a variable id from search_variables'. The description adds no format or validity detail beyond that. Baseline 3 applies when the schema carries the parameter semantics.
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 opens with a clear scoping statement ('One variable in full') and enumerates what is resolved: names, unit, dataset, coverage, quality flag, licence, DOI. That is a specific verb-plus-resource picture an agent can match against search_variables (which only finds ids). It never names a sibling explicitly, so differentiation is inferred rather than stated.
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?
Usage context is implied only: the schema says the id comes from search_variables, and the second sentence hints this tool is the follow-up for zarr-backed multi-dimensional variables. There is no explicit when-to-use versus get_data or aggregate, and no statement of when this tool is unnecessary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
estimateARead-onlyIdempotentInspect
How big a selection is -- rows, stations, time span -- without fetching it. Call this before get_data.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | a variable id from search_variables | |
| end | No | ISO date or datetime, EXCLUSIVE | |
| start | No | ISO date or datetime, inclusive (2025-01-01) | |
| cruise | No | cruise expocodes, a list or a string; ocean observation tables only | |
| lat_max | No | northern edge of a bounding box, degrees north | |
| lat_min | No | southern edge of a bounding box, degrees north | |
| lon_max | No | eastern edge of a bounding box, degrees east | |
| lon_min | No | western edge of a bounding box, degrees east | |
| station | No | station ids -- a list, or one id (or several, comma-separated) as a string; CASE-SENSITIVE: HTM150 (trigram + sampling height) for atmosphere, SE-Htm for FLUXNET sites | |
| apply_qc | No | DEFAULT TRUE: drop the observations the store's recommended quality control rejects; false keeps every observation, flagged ones included | |
| height_max | No | highest sampling height above ground, metres (station series) | |
| height_min | No | lowest sampling height above ground, metres (station series) |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| note | No | |
| rows | Yes | rows the selection holds; None when only an upper bound is known |
| exact | Yes | |
| time_max | No | |
| time_min | No | |
| est_bytes | No | |
| n_stations | No | |
| rows_at_most | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint, so the safety profile is covered. The description adds one genuinely useful behavioral fact -- the call does not fetch the data, so it is a cheap pre-flight check. It says nothing about cost, latency, or how the estimate is computed/sampled, which would matter for a planning tool.
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 short sentences, zero filler, with the substantive explanation front-loaded and the usage directive second. Nothing could be removed without losing 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?
With an output schema present and 100% parameter coverage, the description needn't explain return values or arguments, and it correctly focuses on purpose plus sequencing. For a 12-parameter tool the description is thin on how the estimate interacts with the filters, but the structured fields carry that load.
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 all 12 parameters are documented in the schema itself and the baseline is 3. The description's mention of "rows, stations, time span" loosely maps to the row/station/time filtering dimensions, but it adds no syntax, defaults, or interaction detail beyond what the schema already provides.
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?
"How big a selection is -- rows, stations, time span -- without fetching it" states the specific operation (size estimation) and enumerates what is measured, which is far more informative than the bare name "estimate." It is clearly distinguishable from get_data, though it doesn't name the other sizing-adjacent siblings (aggregate, find_stations) that an agent might confuse it with.
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?
"Call this before get_data" gives an explicit ordering rule that routes the agent correctly between the two tools. There is no explicit when-not guidance (e.g., when skipping the estimate is fine, or how it relates to aggregate/find_stations), so it stops short of full alternative coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_stationsARead-onlyIdempotentInspect
The stations that carry a variable: id, name, position, sampling height, country, ecosystem -- near a point (sorted by distance), in a box, by name or by country. The ids are what estimate and get_data take as station=.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | a station-based variable id from search_variables | |
| text | No | part of a station's id or name, e.g. 'Hyltemossa', 'HTM', 'Hyy' | |
| limit | No | the most stations to return | |
| country | No | two-letter code, e.g. SE | |
| lat_max | No | northern edge of a bounding box, degrees north | |
| lat_min | No | southern edge of a bounding box, degrees north | |
| lon_max | No | eastern edge of a bounding box, degrees east | |
| lon_min | No | western edge of a bounding box, degrees east | |
| near_lat | No | search around this point, with near_lon and radius_km | |
| near_lon | No | longitude of the point near_lat names | |
| radius_km | No | search radius around near_lat/near_lon, km |
Output Schema
| Name | Required | Description |
|---|---|---|
| stations | Yes | |
| variable | Yes | |
| n_matches | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld and non-destructive, so safety is covered; the description still adds real value by disclosing the sort order for proximity search (by distance) and the identity of the returned fields. It stops short of behavioral edge cases such as what happens with zero matches, how conflicting filters interact, or how the default limit truncates results.
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 dense sentences with no filler, and the most actionable fact (ids feed estimate and get_data) is placed last but is short enough to read. Minor ambiguity: the field list reads like return values while 'a variable' is an input id, which takes a moment to disentangle.
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 an 11-parameter search tool with a full output schema and rich annotations, the description covers the essentials: what is searched, the mode options, sort order, and the id handoff. The remaining gap is filter-combination semantics, which matters given four mutually overlapping selection mechanisms but is not addressed anywhere.
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 all 11 parameters are individually documented (id source, text matching examples, two-letter country, bounding-box edges, near_lat/near_lon/radius_km trio, limit bounds), so the schema does the heavy lifting. The description adds only the conceptual grouping of those parameters into four search modes plus the reminder that the resulting ids are the station= input for other tools; baseline 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?
Names the resource (stations carrying a given variable), enumerates the metadata returned (id, name, position, sampling height, country, ecosystem), lists the four selection modes (near a point, in a box, by name, by country), and closes with the downstream contract that the ids are what estimate and get_data accept as station=. An agent can tell exactly what this yields and how to chain it, which no sibling 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 four filter modes imply when each is appropriate (proximity vs bounding box vs text vs country), and pointing at estimate/get_data establishes the follow-on step, so usage is inferable. But there is no explicit routing guidance against search_variables, describe_variable, or list_datasets, and no statement about whether the modes can be combined or which wins when several are supplied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_citationARead-onlyIdempotentInspect
How to cite a variable's data: the citation text, licence and DOI from its data passport, and -- for a selection (stations, a time window, a box) -- every data object it came from, in full. Data answers carry a short citation and only the COUNT of their sources; this lists them.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | a variable id from search_variables | |
| end | No | ISO date or datetime, EXCLUSIVE | |
| start | No | ISO date or datetime, inclusive (2025-01-01) | |
| lat_max | No | northern edge of a bounding box, degrees north | |
| lat_min | No | southern edge of a bounding box, degrees north | |
| lon_max | No | eastern edge of a bounding box, degrees east | |
| lon_min | No | western edge of a bounding box, degrees east | |
| station | No | station ids -- a list, or one id (or several, comma-separated) as a string; CASE-SENSITIVE: HTM150 (trigram + sampling height) for atmosphere, SE-Htm for FLUXNET sites |
Output Schema
| Name | Required | Description |
|---|---|---|
| doi | No | |
| license | No | |
| sources | No | the citation of each data object the selection came from -- what to cite when there is no collection citation (near-real-time data) |
| citation | No | |
| n_sources | No | how many data objects the selection came from; in a data answer their citations are listed only when there are three or fewer -- get_citation (with the same stations) lists them all |
| license_url | No | |
| selection_id | No | stable id of this exact selection over this exact store version |
| store_version | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, non-destructive, open-world behavior, so the safety profile is covered. The description adds that the source list is returned 'in full' while other tools return only a count, which is useful context, but it says nothing about what happens when a selection matches no sources or a variable lacks a passport entry.
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?
Front-loaded with the return content, then a clarifying contrast in the second sentence. The em-dash parenthetical is a bit convoluted but every clause carries meaning and there is no filler.
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, the description does not need to explain return values, and it correctly focuses on scope and the distinction from count-only sources. For an 8-parameter, read-only lookup this is close to complete, with only edge-case behavior left unstated.
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%, so the baseline would be 3; the description goes slightly beyond by framing the optional parameters as a 'selection (stations, a time window, a box)' and explaining that they determine which data objects are listed in full. That gives semantic intent to the optional filters rather than just restating field types.
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 concretely what the tool returns: citation text, licence and DOI from a variable's data passport, plus the full list of underlying data objects. It implicitly distinguishes itself from data-answer tools that carry only a short citation and a source count, giving an agent a clear sense of the resource without opening the schema.
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?
Usage is implied by the contrast 'Data answers carry a short citation and only the COUNT of their sources; this lists them', which suggests calling this when full provenance is needed. However, no sibling tool is named (e.g. get_data, describe_variable) and there is no explicit when-to-use/when-not-to-use statement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_dataARead-onlyIdempotentInspect
Observations for one variable, filtered by box, time, station, cruise, height and quality control. Returns at most max_rows rows as a table, the total, a download URL for the whole selection, and the citation the user must be given with any use of the data.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | a variable id from search_variables | |
| end | No | ISO date or datetime, EXCLUSIVE | |
| start | No | ISO date or datetime, inclusive (2025-01-01) | |
| cruise | No | cruise expocodes, a list or a string; ocean observation tables only | |
| lat_max | No | northern edge of a bounding box, degrees north | |
| lat_min | No | southern edge of a bounding box, degrees north | |
| lon_max | No | eastern edge of a bounding box, degrees east | |
| lon_min | No | western edge of a bounding box, degrees east | |
| station | No | station ids -- a list, or one id (or several, comma-separated) as a string; CASE-SENSITIVE: HTM150 (trigram + sampling height) for atmosphere, SE-Htm for FLUXNET sites | |
| apply_qc | No | DEFAULT TRUE: drop the observations the store's recommended quality control rejects; false keeps every observation, flagged ones included | |
| max_rows | No | rows to return; the whole selection is at download_url | |
| question | No | the user's request as they wrote it; kept only if the user agreed in the licence form to share their questions anonymously, otherwise dropped | |
| height_max | No | highest sampling height above ground, metres (station series) | |
| height_min | No | lowest sampling height above ground, metres (station series) |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| note | No | |
| rows | Yes | |
| columns | Yes | |
| citation | Yes | |
| truncated | Yes | |
| rows_total | Yes | rows in the whole selection, when known |
| download_url | Yes | the selection as CSV when it fits one download; when it does not, the FIRST of download_urls |
| download_urls | No | when the selection is larger than one download (the proxy caps an answer at 500,000 rows): one CSV link per time window, together the WHOLE selection -- give the user all of them |
| rows_returned | Yes | |
| download_complete | No | false when no set of links could be planned to cover the whole selection; the note says what is missing |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld, and non-destructive, so safety is covered. The description adds genuinely new behavior: results are capped at max_rows rows as a table, the response also carries the total count and a download URL covering the whole selection, and the returned citation must be surfaced to the user with any use of the data — a licensing obligation not present in any structured field. That is meaningful context beyond the annotations, though it does not discuss truncation semantics or rate limits.
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?
Three compact sentences with the filter scope front-loaded, followed by the return contract and the citation requirement. No filler, no restatement of the tool name, and each sentence carries distinct 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?
For a 14-parameter query tool with full schema coverage and an output schema, the description covers scope, the row-cap-plus-download-URL pattern that resolves the truncation concern, and the citation duty. What is missing is minor: it does not note that certain filters (e.g. cruise, height) apply only to particular dataset types, though the schema covers this per-parameter.
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 every one of the 14 parameters is already documented, including exclusive end dates, case-sensitive station ids, and the apply_qc default. The description only restates the filter categories (box, time, station, cruise, height, quality control) without adding syntax, interaction, or precedence detail beyond the schema. Baseline 3 applies when the schema does the heavy lifting.
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?
"Observations for one variable, filtered by box, time, station, cruise, height and quality control" gives a specific verb (retrieve observations), a specific resource (a single variable's data), and enumerates the filter axes. This clearly separates it from sibling tools like aggregate, estimate, and describe_variable, which operate on variables rather than returning raw observations. It stops short of naming those siblings explicitly, so it lands at 4 rather than 5.
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?
There is no explicit when-to-use / when-not-to-use statement and no pointer to alternatives such as aggregate for summarised results or search_variables for obtaining the required id. Usage is only implied by the phrase "Observations for one variable" and by the citation obligation at the end. An agent can infer the purpose but must reason about sibling selection on its own.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_datasetsARead-onlyIdempotentInspect
List the ICOS datasets: one per data store, with domain (atmosphere, ocean, ecosystem), time and space coverage, licence and DOI.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| datasets | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, openWorldHint and destructiveHint=false, so safety is covered. The description adds useful context about the shape of the result set (one entry per data store, with coverage, licence and DOI), but says nothing about result size, pagination or freshness.
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?
A single sentence, front-loaded with the action and resource, with the returned attributes packed into a parenthetical. No filler and nothing repeated from the schema or annotations.
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, the description does not need to enumerate return fields in detail, and it already summarizes them. For a zero-parameter read-only listing, this is nearly complete; only the absence of any statement about the catalogue's scope or ordering leaves a small gap.
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 tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline applies. The description correctly implies no filtering is possible — callers get the whole catalogue.
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?
States a specific verb and resource ("List the ICOS datasets") and adds the granularity (one per data store) plus the fields returned. An agent can tell this is the catalogue-level discovery tool rather than a data retrieval tool, though it never names a sibling explicitly.
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?
Usage is only implied: a catalogue listing is naturally the first step before get_data or find_stations, but the description never says so, nor does it state any precondition or exclusion. No explicit when-to-use versus alternatives guidance is present.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_variablesARead-onlyIdempotentInspect
Find variable ids in the ICOS catalogue by name, quantity or unit. The ids are what estimate and get_data take. Primary measurements come before their companions (standard deviation, value counts, flags).
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | what to look for: a gas, a quantity or a name, e.g. 'co2', 'methane', 'net ecosystem exchange', 'fCO2' | |
| limit | No | the most variables to return, best first | |
| store | No | limit to one store, e.g. icos-obspack.zarr | |
| domain | No | atmosphere, ocean or ecosystem |
Output Schema
| Name | Required | Description |
|---|---|---|
| n_matches | Yes | |
| variables | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, openWorld), so the bar is lower. The one genuine addition is the result-ordering trait: "Primary measurements come before their companions (standard deviation, value counts, flags)," which the agent cannot infer from the schema. Nothing is said about ranking, matching behaviour on partial terms, or empty results, and the return shape is covered by the output 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?
Three short sentences, purpose front-loaded, followed by the downstream use and then the ordering caveat. No filler or restatement of the tool name, and every sentence contributes information the agent would otherwise lack.
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?
The tool is a simple, well-annotated search with full schema coverage and an output schema, so return values need no explanation. Purpose, downstream use, and result ordering are all present; the only thin spot is interaction with the optional `store`/`domain` filters, which are already documented in the schema.
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%, including concrete examples for `text` and defaults/bounds for `limit`, so the schema carries the parameter burden. The description only restates the searchable facets (name, quantity, unit) without adding syntax, matching, or filter-combination semantics. Baseline 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 gives a specific verb and resource ("Find variable ids in the ICOS catalogue") plus the searchable dimensions (name, quantity, unit), so the agent knows exactly what the tool returns. It does not, however, contrast itself with the closely related sibling describe_variable, so the boundary between "search for an id" and "describe a variable" is left implicit.
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 states the downstream contract clearly — "The ids are what estimate and get_data take" — which tells the agent this is the lookup step before those tools. It stops short of explicit when-not guidance or naming alternatives such as describe_variable or find_stations.
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.
8 tool updates
v0.3.0- First observed
aggregate - First observed
describe_variable - First observed
estimate - First observed
find_stations - First observed
get_citation - First observed
get_data - First observed
list_datasets - First observed
search_variables
TDQS
Scored across 8 tools
Each tool maps to a distinct step in the data-access pipeline: discovery (search_variables, find_stations, list_datasets), metadata (describe_variable), sizing (estimate), retrieval (get_data), statistics (aggregate) and attribution (get_citation). The closest pairs — search_variables vs describe_variable and estimate vs aggregate — are explicitly separated by their descriptions (find ids vs read one variable in full; size vs statistics).
Seven of eight names follow a predictable verb_noun pattern (get_data, find_stations, search_variables, list_datasets, describe_variable, get_citation). Only 'estimate' and 'aggregate' are bare verbs, but they remain unambiguous in context.
Eight tools is well-scoped for a read-only scientific data catalogue, covering discovery, inspection, retrieval, aggregation and citation without redundancy. No tool feels padded or missing from the workflow.
The surface covers the full read-only lifecycle: catalogue/dataset/station discovery, variable description, size estimation before fetching, filtered retrieval with download URL, server-side aggregation and citation metadata. Trimming isn't a concern since get_data returns a download link for the full selection, so no bulk-export gap remains.
Maintenance
Related MCP Connectors
The official Model Context Protocol server for Ambee. It gives any MCP-compatible AI assistant — Claude, ChatGPT, Cursor, VS Code, Ollama, and more direct access to live air quality, pollen, and weather data. To get started, including information on signing up and obtaining your Ambee key, check out the Ambee documentation on https://docs.ambeedata.com
MCP server for the GDEX (Geoscience Data Exchange) data portal: datasets, files, metrics, subsetting
Real-time planetary signal engine and Model Context Protocol (MCP) server for autonomous AI agents.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceA Model Context Protocol server that enables efficient discovery and retrieval of NASA Earth Data for geospatial analysis.26BSD 3-Clause
- AlicenseAqualityDmaintenanceA Model Context Protocol server that enables AI assistants to perform real-time carbon emissions calculations and provide climate impact insights by interfacing with the Climatiq API.1012MIT
- AlicenseNot gradedqualityBmaintenanceA self-hosted MCP server that gives LLM agents local, reproducible access to Copernicus environmental data including observations, reanalysis, forecasts, and climate indicators.128 PyPI15BSD 3-Clause
- AlicenseNot gradedqualityDmaintenanceA Model Context Protocol server for scholarly tools, enabling AI assistants to interact with scholarly data and services via discoverable tools.MIT