Skip to main content
Glama
ActiveGuy

StatsMapped: Irish statistics (CSO, county & council data)

statsmapped-mcp

An MCP server exposing StatsMapped's public API as tools for AI agents (Claude Desktop, Cursor, and any other MCP client).

StatsMapped tracks public data for Ireland (by county) and the UK (by local authority) — housing, crime, health, the economy and social welfare — from official publishers (CSO, PSRA, Central Bank of Ireland, DHLGH, NTPF, the Office of Government Procurement, EU Publications Office for Ireland; ONS/HM Land Registry/Nomis/DfE/DfT for the UK), each figure carrying its own caveats. This package lets an agent query that data directly as tool calls instead of crawling and parsing web pages. Every tool below takes a country argument ("ireland" or "united-kingdom", default "ireland") — the two countries track genuinely different datasets and geography levels, so call query_data/list_areas for the country you actually want rather than assume Ireland's defaults apply.

This is a thin client. It calls StatsMapped's already-public, unauthenticated HTTPS API (documented at statsmapped.com/openapi.json); no API key is needed for anything below. Runs on your own machine over stdio by default (the recommended way to use it today) -- see Why stdio for a hosted streamable-http mode this package also supports, and the real tradeoff that comes with it.

Install

Published on PyPI as statsmapped-mcp (confirmed live: https://pypi.org/project/statsmapped-mcp/):

pip install statsmapped-mcp

Related MCP server: ELS MCP server

Configure

Add to your MCP client's config (for Claude Desktop, claude_desktop_config.json; Cursor uses an equivalent mcp.json):

{
  "mcpServers": {
    "statsmapped": {
      "command": "statsmapped-mcp"
    }
  }
}

Tools

4 tools (consolidated from an original 9 -- query_data and compare are each one tool with a few modes, chosen by which arguments you give, rather than several near-identical single-purpose ones). Every tool takes a country argument ("ireland" or "united-kingdom", default "ireland") — Ireland and the UK track different datasets and geography levels, so call query_data/list_areas for the country you want rather than assume Ireland's defaults apply.

  • query_data(area_id=None, dataset=None, history_months=0, country="ireland") — three modes:

    • neither area_id nor dataset: every stat StatsMapped tracks for one country, with its key, label, and which geography levels it's published at. Start here.

    • area_id given, dataset omitted: every dataset available for one area (e.g. "county:kerry" for Ireland, "uk:lad:e09000033" for the UK), with its latest figure and year-on-year change. Caveats are projected to label + severity only, not full text — the point is deciding which datasets matter before paying for the full detail on any one of them.

    • both area_id and dataset given: full detail on one dataset in one area — a written summary, full caveat text, and (optionally, via history_months) recent history.

  • list_areas(level="county", country="ireland") — every geography at one boundary level. level defaults to Ireland's 26 counties; the UK's own primary level is "lad" (local authority districts), not "county" — other levels exist per country too (Ireland's local_authority/garda_division among them).

  • compare(stat_key=None, level=None, pair_key=None, stat_key_a=None, stat_key_b=None, country="ireland") — four modes, exactly one set of arguments at a time (mixing them raises an error):

    • stat_key alone: every area at one level, ranked by its latest figure for that stat, highest first.

    • pair_key alone: full detail for one registered comparison pair — each axis's label/unit/publisher, the correlation stats (r, rho, a leave-one-out sensitivity range), and caveats. pair_key comes from the no-argument mode below.

    • both stat_key_a and stat_key_b: does StatsMapped have a registered, hand-vetted comparison between these two stats? Registry-backed only — never computes a fresh correlation for an arbitrary pair. comparable is a string, not a bool: "yes" (a registered, hand-vetted pair; pair_key names it), "no" (the two stats share no geography level at all), or "unknown" (not registered, but not ruled out: treat as "ask a human", not "probably yes"). "unknown" is the normal result for most pairs. Compare the value explicitly (result["comparable"] == "yes"); a truthiness test is true for all three. reasons (a list) always explains the answer.

    • none of the above: every registered cross-dataset comparison pair for one country (e.g. "median sale price vs new dwelling completions per 1,000 residents"). A small, hand-curated set, not an arbitrary-pair engine.

  • explain_metric(stat_key, country="ireland") — definition, methodology and standing caveats for one stat, never a current figure. Use this when the question is about what a metric means or how it's measured, not about one area's value.

Why stdio, not a hosted server, by default

StatsMapped runs on a single free-tier instance. A remote MCP endpoint hosted there would let an agent's own multi-area query pattern (calling the same tool once per area, in a loop) reproduce exactly the load pattern that has already caused timeouts on that instance under a large geography fan-out. Running over stdio means every call goes through your own network connection to the same public HTTPS API this package's tools call directly, with no shared bottleneck -- each user's own machine makes the HTTP calls, so N users' traffic is naturally spread across N source IPs, not funnelled through one.

server.py also supports a real hosted streamable-http mode (MCP_TRANSPORT=streamable-http) for a deployment that accepts that tradeoff -- StatsMapped's public API is itself rate-limited per source IP (600 requests/hour), but a hosted MCP endpoint proxies every remote user's calls through ONE shared egress IP, so all remote users of a hosted endpoint would share that one bucket rather than each getting their own. A StatsMapped-hosted endpoint is live at https://mcp.statsmapped.com/mcp (Streamable HTTP) -- confirmed responding correctly, no separate install needed for a client that speaks Streamable HTTP directly. The bare host (https://mcp.statsmapped.com, no path) redirects to the same endpoint as a courtesy.

Development

cd mcp-server
pip install -e .
python tests/test_client.py

The test suite runs against the real live API (https://statsmapped.com by default, or STATSMAPPED_MCP_BASE_URL if set) — read-only GETs only, nothing here writes any data or needs a key.

Licence

MIT for this package. The underlying data keeps each publisher's own licence — see statsmapped.com/ireland/sources for Ireland's own publisher/licence detail before reusing any figure outside of querying it through an agent (a UK equivalent page doesn't exist yet — check each UK tool response's own caveats/sources fields in the meantime).

Available Tools

5 tools
compareAInspect

Use when the question is how areas rank or compare on a stat, or whether two stats can be compared fairly. For one area's own figures use query_data.

Four modes, depending on which arguments are given -- consolidates what were four separate tools (rank_areas, list_comparisons, get_comparison, check_comparability) behind one, since they are all really "how does this stat compare" at different scopes. Exactly one mode's arguments should be given; mixing arguments from different modes (e.g. both stat_key and pair_key, or only one of stat_key_a/stat_key_b) raises an error rather than silently guessing which mode was meant.

  1. stat_key alone (no pair_key, no stat_key_a/stat_key_b): ranks every area at one geography level by its latest figure for that stat, for one country -- e.g. "which counties have the highest median sale price" (country="ireland"). stat_key comes from query_data's dataset-listing mode, for the SAME country. level omitted uses this ranking's own default level; pass one of that dataset's own compatible_levels for a different one -- a level this ranking doesn't have registered returns an empty list rather than an error. Where the underlying stat has no honest per-area denominator (crime, homelessness, live_register and similar -- StatsMapped's own RANKING_NO_DENOMINATOR_STATS), each row's rate_per_1000 is the real figure to rank/compare by, not latest_value, which is a raw count dominated by area population size. Always carry forward every entry in caveats when using a row in an answer.

  2. pair_key alone: full detail for one registered comparison pair -- each axis's label, unit and publisher, the correlation stats (r, rho, and a leave-one-out sensitivity range naming the single most influential area), and caveats. pair_key comes from mode 4's own response, for the SAME country.

  3. Both stat_key_a and stat_key_b given: does StatsMapped have a registered, hand-vetted comparison between these two stats? Registry- backed only -- never computes a fresh correlation for an arbitrary pair. Both stat_keys come from query_data's dataset-listing mode, for the SAME country. verdict is one of "SUPPORT" (a real, hand-vetted registered pair with no open caveats -- may be treated as a confirmed relationship), "QUALIFY" (hand-vetted, but the evidence carries real caveats -- e.g. no robustness check for outliers, or an unverified geography-level join; read uncertainty and reasons before presenting it as confirmed), "REJECT" (a real structural impossibility or a human-vetted "no" -- the two stats share no geography level at all, or a reviewer rejected this exact pairing), or "INSUFFICIENT" (not registered, not ruled out either -- StatsMapped genuinely hasn't vetted this pair; never treat this as "probably comparable"). uncertainty names 4 separate dimensions (data_quality, comparability, statistical_strength, causal_strength) -- causal_strength is always "not_established", since no comparison here implies causation regardless of verdict. comparable (DEPRECATED, kept only for callers that haven't migrated) collapses verdict to the old 3-way yes/no/unknown -- "yes" for both SUPPORT and QUALIFY (both mean "hand-vetted", the old comparable meaning this field has always carried; the caveats a QUALIFY pair carries live in uncertainty/reasons, not in demoting comparable), "no" for REJECT, "unknown" for INSUFFICIENT. Prefer verdict directly when you need to distinguish a fully-confirmed SUPPORT from a caveated QUALIFY. Read reasons before presenting any answer other than "SUPPORT" as unqualified.

  4. None of the above given: lists every registered cross-dataset comparison pair for one country -- e.g. "median sale price vs new dwelling completions per 1,000 residents". A small, hand-curated set, not an arbitrary-pair engine: pass one of the returned pair_key values to mode 2 for the real correlation and axis detail.

    level is only meaningful together with stat_key (mode 1); giving it without stat_key raises rather than silently dropping it and falling through to mode 4's unrelated pair listing.

NameRequiredDescriptionDefault
levelNo
countryNoireland
pair_keyNo
stat_keyNo
stat_key_aNo
stat_key_bNo

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.8/5.0
Behavior5/5

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

No annotations, so the description carries the full load and does: unregistered `level` returns an empty list rather than an error, mixed arguments raise, mode 3 never computes a fresh correlation and is registry-backed only, `causal_strength` is always 'not_established', `comparable` is deprecated with a precise mapping, and `caveats` must be carried forward. These are exactly the behavioral traits annotations would otherwise supply.

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

Conciseness4/5

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

Long but front-loaded with the when-to-use routing before the mode enumeration, and organized as a numbered list an agent can scan. Some passages, notably the deprecated `comparable` mapping, are verbose, though they still carry migration-relevant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Output schema exists, so return values needn't be documented, yet the description still explains the verdict/uncertainty semantics an agent must interpret correctly. For a six-parameter, four-mode tool with no annotations, nothing needed to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate and it does: each of the six args is tied to a mode (stat_key, pair_key, stat_key_a/stat_key_b, level only with stat_key, country implied as 'SAME country' with an example). The only gap is that `country`'s accepted format/default is only shown by example, not defined.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('how areas rank or compare on a stat', 'whether two stats can be compared fairly') and explicitly distinguishes itself from the sibling `query_data` for single-area figures. The four modes are enumerated so an agent knows the full scope of the tool 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.

Usage Guidelines5/5

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

Explicit when-to-use ('question is how areas rank or compare') and when-not ('For one area's own figures use query_data'). It also gives the exact arg-combination rule that selects each mode, warns that mixing arguments raises rather than silently guessing, and notes `level` without `stat_key` raises instead of falling through to mode 4.

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

explain_metricAInspect

Use when the question is what a metric means or how it is measured; for a figure use query_data.

Definition, methodology and standing caveats for ONE stat ('ireland' or 'united-kingdom') -- never a current figure. Call this when the question is about what a metric MEANS or how it's measured ("how is the claimant count defined", "is this a mean or a median"), not about a specific area's value -- query_data/compare already answer that. stat_key comes from query_data(country=...) for the SAME country.

NameRequiredDescriptionDefault
countryNoireland
stat_keyYes

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

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

No annotations are present, so the description carries the behavioral burden. It discloses the output nature (definition/methodology/caveats), the important constraint that it never returns a current figure, supported country values, and the stat_key dependency; it does not spell out read-only/no-side-effect or error behavior, but the core behavior is clear.

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

Conciseness4/5

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

The structure is front-loaded: usage rule, output scope, exclusions, then parameter provenance. However, the 'when to use' rule is repeated in the opening and again in the fourth sentence, so some trimming is possible.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter explanation tool with an output schema, the description supplies the critical routing, scope, and stat_key provenance. No major invocation detail is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It explains that stat_key comes from query_data(country=...) for the same country and lists the supported country values, covering both parameters, though it does not mention the default country or stat_key format.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states that the tool returns definition, methodology, and standing caveats for one metric, and explicitly says it never returns a current figure. It distinguishes itself from query_data and compare by routing numeric questions to those siblings.

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

Usage Guidelines5/5

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

It explicitly says to use this when the question is about what a metric means or how it is measured, not for a figure, and names query_data/compare as the alternatives. It also states where stat_key comes from, giving the agent a clear invocation path.

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

get_sceneAInspect

The data to DRAW one scene, nothing rendered: use it when asked for a chart, still or short clip of a figure you can cite. Today one scene exists, "ireland-builds" (Ireland's homes granted permission, with a commencement notice and completed, by quarter from 2018Q1). The licence DIFFERS BY STAGE (see licence.stages): a stage whose licence is unconfirmed or ShareAlike is withheld (its cells are absences, not zeros) and the licence line names its holder. You get rows, a table twin, alt text, notes, absences (a missing figure is a typed absence, never a zero), the licence block, scene_hash, a receipt and the canonical URL. YOU draw it, and the last frame must match the table. Print the scene_hash and the licence line in the first, middle and last frame, quote only numbers that are in rows, do not loop, and keep the "stages are not a cohort" note. purpose is "analytical" (default) or "promotional": a promotional use of a source whose licence forbids advertising is refused (licence_refused). vintage (ISO date) omitted = the current data; an earlier vintage is not stored and returns unknown_vintage. Example: get_scene(scene_id="ireland-builds").

NameRequiredDescriptionDefault
countryNoireland
purposeNoanalytical
vintageNo
scene_idNoireland-builds

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior5/5

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

With zero annotations the description carries the full burden and does so richly: it discloses that licence differs by stage, that withheld stages produce absences not zeros, that a licence holder is named, that the caller must print scene_hash and the licence line in first/middle/last frames, and that earlier vintages return unknown_vintage. Named error states (licence_refused, unknown_vintage) and anti-loop guidance are exactly the beyond-schema context an agent needs.

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

Conciseness3/5

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

The opening is well front-loaded, but the body is a dense run-on paragraph that mixes parameter semantics, licence rules, and multi-frame rendering instructions. Every element is relevant, yet the lack of structure makes it hard to scan for the arguments an agent must actually pass.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 4-param tool with an output schema, the description is thorough: it explains absences vs zeros, licence staging, error outcomes, and citation requirements. It even describes return contents (rows, table twin, alt text, receipt, canonical URL) that the output schema likely already covers, and only the 'country' parameter is left unexplained.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate and it largely does: scene_id (only 'ireland-builds' exists, example given), purpose ('analytical' default or 'promotional' with refusal behavior), and vintage (ISO date, omitted = current, earlier = unknown_vintage). The 'country' parameter (default 'ireland') is never mentioned, which is the one gap preventing a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource: 'The data to DRAW one scene, nothing rendered', and clarifies scope with 'use it when asked for a chart, still or short clip of a figure you can cite.' It implicitly separates itself from data-query siblings (query_data, compare) by being a rendering-input tool, but never names an alternative explicitly.

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

Usage Guidelines4/5

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

Gives clear trigger conditions ('when asked for a chart, still or short clip of a figure you can cite') and states that nothing is rendered by the tool itself. It also covers purpose-based refusal (promotional + advertising-forbidden licence → licence_refused). No explicit sibling routing or when-not-to-use exclusion, keeping it below a 5.

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

list_areasAInspect

Use when you need the ids and names of the areas (counties, local authorities, districts) to pass to query_data.

List every geography at one boundary level, for one country ('ireland' or 'united-kingdom'). level defaults to "county" (Ireland's 26 counties); the UK's own primary level is "lad" (local authority districts), not "county". Other levels exist per country (e.g. Ireland's "local_authority", "garda_division") -- see a dataset's own compatible_levels from query_data for which levels a given stat is actually published at. Returns each area's id (used by query_data's area-scoped modes, always paired with the SAME country), name and level. Slim by default; include_parents=True adds each area's parents (the regions it sits in) and Irish-language name.

NameRequiredDescriptionDefault
levelNocounty
countryNoireland
include_parentsNo

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior4/5

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

No annotations exist, so the description carries the full burden and does well: it discloses output fields (`id`, `name`, `level`), that results are slim by default, what `include_parents=True` adds, and the pairing constraint that an `id` must always be used with the SAME `country` in `query_data`. It stops short of mentioning ordering, pagination, or whether the list is exhaustive per level, but the behavioral picture is largely complete for a read-only lookup.

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

Conciseness4/5

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

Front-loaded with the use case, then defaults, then caveats, then return shape — a sensible order with no filler sentences. It runs slightly long and repeats the `id`-is-for-`query_data` point twice, which is the only wasted effort.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a three-optional-parameter lookup with no annotations, the description covers defaults, country/level coupling, the extended-output option, and the cross-tool key contract. An output schema exists, so the brief return-value sketch is sufficient and nothing needed to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate and it does: it gives `level`'s default ('county', 26 Irish counties), the country-specific twist that the UK's primary level is 'lad' rather than 'county', the allowed `country` values ('ireland' or 'united-kingdom'), and the exact effect of `include_parents`. That is meaning the schema cannot supply.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('list every geography at one boundary level, for one country') and immediately names its consumer, `query_data`. An agent can distinguish it from `query_data` without opening either schema.

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

Usage Guidelines5/5

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

Opens with an explicit when-to-use ('Use when you need the ids and names of the areas ... to pass to `query_data`') and clarifies the division of labour by pointing to `compatible_levels` on `query_data` for per-dataset level availability. The when-not is implicit but strong: this tool lists geographies, `query_data` fetches statistics.

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

query_dataAInspect

Use when you need to find which datasets exist, which exist for one area, or the figures and history for one area and dataset (or a council planning view). For what a metric means use explain_metric; for how areas rank use compare.

Three modes, depending on which of area_id/dataset are given (plus a planning mode, council, described last) -- consolidates what were three separate tools (list_datasets, list_area_datasets, get_dataset_for_area) behind one, since they are all really "how do I get data" at different levels of specificity:

  1. Neither area_id nor dataset: lists every dataset (stat) StatsMapped tracks for one country ('ireland' or 'united-kingdom'), with its key, human label, and which geography levels it can be shown at. Ireland and the UK track genuinely different datasets -- call this first for the right country before assuming a stat_key exists there, to find the right stat_key for compare's ranking mode.

  2. area_id given, dataset omitted: lists every dataset available for that one area (e.g. "county:kerry" for Ireland, "uk:lad:e09000033" for the UK), with its latest figure, year-on-year change, and caveat labels only (not full caveat text -- use mode 3 for the full detail on any one dataset that matters). Pass stat_key (from mode 1) to list only that stat's series for the area: a full listing is tens of KB, one stat is a few hundred bytes. area_id comes from list_areas; country must match whichever country that call used, or this simply 404s ("unknown geography").

  3. Both area_id and dataset given: full detail for one dataset in one area -- the latest figure, a written summary, full caveat text, and (if history_months is set) recent history. dataset is a series_key from mode 2's own response. history_months means actual months of history (0 = everything) -- e.g. 24 returns 2 years of an annual series, not 24 years. country must match area_id's own country. A stat key from mode 1 is also accepted as dataset when the area has exactly one series for it (the reply says resolved_from_stat_key); with several, the error names the series keys to use.

  4. council alone (an Irish council slug, e.g. "cork-city-council"; country "ireland" only; no other argument): that council's planning applications by year received -- count, grant and refusal share of decided applications, median days to a decision -- with source, licence, the last data date, a receipt, plain notes, and typed absences instead of zeros. A council the national planning feed has stopped carrying (for example Cork City, last record 2025-11-04) says status: "stopped_reporting": a low recent figure is a feed gap, not fewer applications. Example: query_data(council="cork-city-council").

    dataset and history_months are only meaningful together with area_id (and, for history_months, dataset too, since it only applies to mode 3); giving either without its real precondition raises rather than silently dropping the argument and dispatching to the wrong mode.

NameRequiredDescriptionDefault
area_idNo
councilNo
countryNoireland
datasetNo
stat_keyNo
history_monthsNo

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.9/5.0
Behavior5/5

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

With no annotations, the description carries the full behavioral burden and does so well: it explains mode dispatch, error behavior for mismatched country/area, 404s for unknown geography, raises for invalid argument combinations, response sizes, caveat handling, `resolved_from_stat_key`, and `stopped_reporting` status. It also clarifies that `history_months` means actual months rather than periods of the series.

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

Conciseness4/5

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

The description is long, but the length is largely justified by four distinct modes and several argument interactions. It is front-loaded with a usage statement and structured into numbered modes, though some passages could be tightened without losing meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given six parameters with zero schema coverage, no annotations, and an output schema, the description provides all the missing invocation context an agent needs. It covers mode selection, parameter dependencies, error cases, output characteristics, and special status handling.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, and it does: every parameter is explained in context, including `area_id`, `country`, `dataset`, `stat_key`, `history_months`, and `council`. It adds semantics the schema lacks, such as country matching, `series_key` sourcing, `stat_key` acceptance, and `history_months=0` meaning all history.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific multi-mode purpose: finding datasets, area-specific datasets, figures/history for an area and dataset, and Irish council planning data. It explicitly consolidates three former tools and distinguishes the sibling tools `explain_metric` and `compare` by their roles.

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

Usage Guidelines5/5

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

It states when to use the tool and names alternatives for adjacent questions: `explain_metric` for metric meaning and `compare` for rankings. The four modes are keyed to which arguments are supplied, with preconditions such as `area_id` coming from `list_areas` and `dataset` coming from mode 2.

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.

  1. 3 tool updatesv0.3.4
    • Addedget_scene
    • Changedlist_areas1 field changed
      • addedInput schema / properties / include_parents
        Added value: +{
        +  "default": false,
        +  "title": "Include Parents",
        +  "type": "boolean"
        +}
    • Changedquery_data2 fields changed
      • addedInput schema / properties / council
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Council"
        +}
      • addedInput schema / properties / stat_key
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Stat Key"
        +}
  2. 9 tool updatesv0.3.0
    • Removedcheck_comparability
    • Addedcompare
    • Removedget_comparison
    • Removedget_dataset_for_area
    • Removedlist_area_datasets
    • Removedlist_comparisons
    • Removedlist_datasets
    • Addedquery_data
    • Removedrank_areas
  3. 9 tool updatesv0.1.0
    • First observedcheck_comparability
    • First observedexplain_metric
    • First observedget_comparison
    • First observedget_dataset_for_area
    • First observedlist_area_datasets
    • First observedlist_areas
    • First observedlist_comparisons
    • First observedlist_datasets
    • First observedrank_areas

TDQS

A4.4/5.0

Scored across 5 tools

Disambiguation4/5

Each tool has a stated, non-overlapping role and the descriptions explicitly cross-route ("for what a metric means use explain_metric; for how areas rank use compare"), so an agent can pick the right entry point. The main friction is that query_data and compare each multiplex four modes selected by which arguments are present, so within a single tool name the boundaries are argument-driven rather than name-driven.

Naming Consistency4/5

Four of five names follow a clear verb_noun snake_case pattern (query_data, list_areas, explain_metric, get_scene). The bare verb `compare` deviates slightly, but the overall convention is readable and predictable.

Tool Count4/5

Five tools is a well-scoped surface for a read-only statistics server, and the consolidation of nine former tools into query_data/compare is deliberate. The trade-off is that individual tools carry heavy multi-mode dispatch, making each call signature much larger than the count suggests.

Completeness5/5

The lifecycle is covered end to end: discover datasets, resolve area ids, fetch figures with history, rank/compare with vetting verdicts, explain methodology, and pull scene/visualisation data with licensing. Remaining gaps (bulk export, cross-country listing in one call) are minor and workable.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables AI agents to perform due diligence on Irish properties by querying public datasets for planning applications, sold prices, flood risk, radon risk, and zoning.
    1
    74 npm
    8
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables LLM agents to query the Explore Local Statistics API for local area data like employment rates and demographics.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to search, inspect, and retrieve Irish Central Statistics Office PxStat datasets through MCP tools. Supports catalog search, metadata discovery, full-table reads, and filtered JSON-stat queries.
    253 npm
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants to search, preview, and query Ireland's public-sector open data catalogue, including CSV, Excel, JSON, GeoJSON, and JSON-stat resources. It supports filtering, grouping, counting, summing, and sorting datasets conversationally without manual downloads or cleanup.
    MIT