au-opendata-mcp
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., "@au-opendata-mcpwhich state has the most public toilets? include source and licence"
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.
au-opendata-mcp
An MCP server that gives an AI client direct access to Australian government open data. Search the catalogue, inspect a file's schema, then run real SQL over it — with the source URL, publisher and licence attached to every answer.
Covers data.gov.au (the federal catalogue) and
data.sa.gov.au (South Australia). Both run CKAN; list_portals
reports each portal's current dataset count.

A real run, recorded with scripts/demo.py: Claude Sonnet 5.5 with this server as its only
tool source. Pauses longer than 1.5 s are trimmed.
Quickstart
git clone https://github.com/hanhsienlei/au-opendata-mcp
cd au-opendata-mcp
uv sync
uv run au-opendata-mcp --transport http --port 8000 # REST + MCP over HTTP
curl "localhost:8000/api/datasets?q=bus+stops&limit=3"Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"au-opendata": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/au-opendata-mcp", "run", "au-opendata-mcp"]
}
}
}This runs the server over stdio, this server's default transport when --transport is not
passed (see main.py). Restart Claude Desktop after editing the config.
Related MCP server: data-brussels
Tools
Tool | What it does |
| The portals available, with dataset counts |
| Keyword search, one portal or both, with each portal's total match count |
| A dataset's metadata and files, each flagged queryable or not |
| The publishers on a portal, largest first, with how many datasets each has |
| A file's columns, types, sample rows and row count |
| A single DuckDB |
| ABN checksum, per the ATO algorithm |
| Maps a date to the Australian FY (1 July - 30 June) |
The intended flow is search_datasets → get_dataset → preview_resource → query_resource.
You cannot write SQL for columns you have not seen, so preview_resource comes first.
Every tool has a matching REST endpoint under /api (list_organizations -> GET /api/organizations, query_resource -> POST /api/resources/{resource_id}/query, and so
on), and both interfaces share one service layer — see src/au_opendata_mcp/api.py and
src/au_opendata_mcp/mcp_server.py.
Design notes
One SQL dialect, always. CKAN's DataStore is PostgreSQL and the fallback engine is DuckDB. Routing queries to whichever happens to be available would mean the same tool silently changing dialect between datasets — different function names, different casting, different date handling — with no way for the caller to know in advance. So every SQL query runs in DuckDB. The DataStore is used only for download-free schema and sample retrieval.
The query sandbox. A resource is materialised to Parquet once, then loaded into a fresh
in-memory DuckDB. Only after the table exists is enable_external_access revoked and the
configuration locked — that order matters, because revoking access first also disables the
Parquet read that loads the table. Caller SQL is parsed with sqlglot and must be a single
SELECT over the table t; row caps are enforced by wrapping the query in an outer SELECT ... LIMIT, never by rewriting it. Queries also run under a timeout and are executed off the
event loop (asyncio.to_thread), so one slow or runaway query cannot stall every other
concurrent request.
Outbound fetches are constrained. Only URLs the portal itself published for that resource
are fetched, http(s) only. Every resolved address is checked for global routability (not just
private/loopback/link-local, but multicast and CGNAT ranges too). On the download path that
check runs again at every redirect hop. get_dataset's HEAD size probe runs the same check
before the one request it makes, and deliberately does not follow redirects — there is no
second hop to check, and an unfollowed redirect just leaves the size unresolved rather than
reaching an address nothing validated. A 50 MB ceiling is enforced while streaming the
response body — never trusted from the content-length header, which is just a hint.
Attribution is structural. Every result model carrying portal data inherits a base
(Sourced) with dataset_url, organisation and licence. There is no un-sourced result
type to return.
Security notes
This is a self-hosted tool, not a multi-tenant service, and its threat model is scoped accordingly: the operator trusts the two configured portals but not the SQL or URLs an AI client sends it. A few things worth knowing before you rely on it:
DNS TOCTOU in the download guard. The SSRF check (
assert_safe_url) resolves a resource's hostname itself and rejects it if any resolved address is not globally routable. But httpx then resolves the same hostname again, independently, when it actually opens the connection — and a hostile authoritative DNS server can answer differently between those two lookups (classic DNS rebinding). Re-validating on every redirect hop narrows this window — a long redirect chain gives an attacker more chances, not fewer, since each hop is checked again — but it does not close it. Closing it properly needs a custom transport that connects to the already-validated IP while still presenting the original hostname for TLS SNI, which was judged disproportionate for a tool that, in practice, only ever downloads from URLs a known government CKAN instance published. See the docstring onMaterialiser._downloadinsrc/au_opendata_mcp/services/materialise.pyfor the full reasoning.Caller SQL is parsed, not pattern-matched, so comments, string literals and stacked statements cannot smuggle a second statement past the check — but the parser (
sqlglot) is still trusted code. A parser bug would be a real hole.The sandbox lockdown order is load-bearing.
services/query.py::execute_in_sandboxcreates the table, then revokesenable_external_access, then locks the DuckDB configuration. Reordering any of these three statements either breaks the Parquet read or reopens the filesystem to caller SQL; this is covered by a comment and a dedicated test (tests/test_query_execute.py), not just convention.The Docker image runs as root. See the comment in the
Dockerfile— this is a self-hosted tool, not a hardened production deployment, and no attempt has been made to build a non-root, distroless, or otherwise locked-down image.
If you spot something else, please open an issue rather than assume it was missed on purpose — some of the above trade-offs are deliberate, but not all corners have necessarily been checked.
Licensing
Datasets carry their own licences, and they vary widely. Creative Commons variants are common
on both portals, but on data.gov.au roughly two-thirds of datasets specify no licence at all
(measured via the portal's own package_search licence facet, September 2026 — worth
re-checking rather than trusting, since it will drift). data.sa.gov.au is Creative
Commons-dominated, but it is a small fraction of the combined catalogue, so it does not set
the norm for the whole server.
Every result includes its licence field — treat an unspecified licence as "unknown," not
"free to use," and attributing the source when you reuse the data is your responsibility.
Demo scenarios
Three prompts to paste into Claude Desktop once the config above is in place — catalog
search, an aggregate query over a real resource, and ABN validation. They name what the user
wants rather than which tool to call, so what you see is the tool selection a real user would
get, across the intended chain search_datasets → get_dataset → preview_resource →
query_resource.
These are scenarios to try, not a transcript. Unlike the evaluation below, they have not been run and recorded here, so no output or success rate is reproduced.
1. Catalog search.
Search data.gov.au for datasets about public toilets. Give me the five best matches, and for each one the publisher, the licence, and which file formats it comes in. Flag any where the licence is not specified.
The interesting part is the last sentence: roughly two-thirds of data.gov.au datasets carry
no licence, and every result model here has a licence field precisely so that is visible
rather than assumed.
2. An aggregate query over a real resource.
On data.gov.au, find a CSV of Australian Government contract notices. Show me the columns first, then run one SQL query that gives me the ten agencies with the highest total contract value. Tell me the source URL and licence of the exact file you queried, and how many rows it has.
This is the full chain: search_datasets to find it, get_dataset to pick a file that comes
back queryable: "yes", preview_resource to see the real column names — you cannot write
SQL for columns you have not seen — then a single SELECT through query_resource. The file
is downloaded once, converted to Parquet and cached, and the query runs in a locked-down
in-memory DuckDB over the table t.
3. ABN validation.
Is the ABN 51 824 753 556 valid? And which Australian financial year does 2026-03-31 fall in?
validate_abn runs the ATO's weighted-modulus checksum (spacing and formatting are
tolerated), and au_financial_year maps the date to the 1 July – 30 June year — neither
needs a portal, so both answer even if data.gov.au is down.
Evaluation
evals/questions.md has 20 questions, each with the tool sequence a correct response should
produce. evals/run_eval.py asks them of a real model through Claude Code's headless mode,
with this server as its only tool source: no built-in tools, no other MCP servers, no user
settings, and a one-line system prompt. The model sees only what any MCP client sees, the tool
names, descriptions and schemas, so the score is a test of those descriptions. Questions 6–16
run as one conversation, the way a user would ask them.
A question passes when the model calls the expected tools in order and gives a correct, sourced answer. A dataset or preview it already fetched earlier in the same conversation counts. The runner checks the tool sequence; the answers were graded by hand.
Run, 2026-10-01 | Claude Sonnet 5.5 | Claude Haiku 4.5 |
First run | 15/20 | 16/20 |
After the | 16/20 | 17/20 |
Current code, after the | 17/20 (85%) | 17/20 (85%) |
The target was 95%, and it is not met. Per-question results and notes are in
evals/questions.md, and each run's tool calls and answers are in evals/runs/. In short:
The first run found a real bug.
list_organizationsreturned 25 of data.gov.au's 1,142 publishers, because CKAN capsorganization_listat 25 rows when it returns full records. Asked who publishes the most datasets, Sonnet said the list looked truncated, and Haiku confidently named the wrong publisher. Publishers now come from a search facet, largest first, and both models answer Geoscience Australia (42,411 datasets) in one call.It also found a missing feature, since added.
search_datasetsreturned at most 50 results and dropped CKAN's total match count, so "compare the number of bus stop datasets on each portal" could only be answered with the search limit. It now reports each portal's total, and both models answer 469 on data.gov.au against 5 on data.sa.gov.au.The remaining misses are on the expected path, not the answer. Asked to delete rows, both models refused on the strength of the tool description instead of calling
query_resourceand letting it reject the SQL. Asked to query the PDF, both did the opposite: they calledquery_resourceinstead of refusing on thequeryable: noflag they already had. On the ABS question, both searched directly instead of looking up the publisher first. All of those answers were correct; they are counted as misses anyway, because the bar was set before the run.The PDF question was first graded as a pass, which was a mistake. The runner let a dataset lookup from earlier in the conversation satisfy it, and the first write-up excused the query while failing the mirror-image delete question. The runner now catches this case, and every run above was re-graded, losing one point each.
A single run is noisy. Haiku passed the ABS question in one run and missed it in the next, with the same server and question, so treat a one-question difference as noise.
Re-run it with uv run python evals/run_eval.py --model sonnet (or --model haiku).
A separate, weaker check ran on 2026-09-29: each question's expected tool sequence was executed directly against the live portals, and all 20 succeeded and returned correct, sourced answers. That shows the tools work on real data. It shows nothing about whether a model would choose them, which is what the table above measures.
Development
uv sync
uv run pytest # unit tests, no network
uv run pytest -m live # contract tests against the real portals
uv run ruff check . && uv run ruff format --check . && uv run mypyService-layer coverage (src/au_opendata_mcp/services) is gated at 80% in CI
(.github/workflows/ci.yml) and currently sits at 96%. The live contract tests run weekly
(.github/workflows/upstream-drift.yml) rather than per-PR, so upstream downtime cannot block
a pull request.
Not in this version
User accounts, API key issuance, and quotas; a web frontend or dashboard; a full data mirror
or daily crawl; the ABS SDMX statistics API; the NSW, VIC and QLD portals; and actual cloud
deployment (a Dockerfile exists and has been built and run locally, but nothing has been
deployed to Cloud Run or any other host). See docs/prd.md and
docs/design.md for the reasoning behind each.
Available Tools
8 toolsau_financial_yearAu Financial YearA
Map a date to the Australian financial year, which runs 1 July to 30 June.
Use this whenever a question involves an Australian financial year, rather than assuming a calendar year.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | A date as YYYY-MM-DD. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden, and it does disclose the governing rule (fiscal year runs 1 July–30 June), which is the essential behavioral fact for interpreting results. It is a pure deterministic mapping with no side effects, auth needs, or rate limits to disclose, though it does not address boundary-date behavior explicitly.
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, zero waste, with the core semantic definition front-loaded before the usage instruction. Nothing is padded or 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 one-parameter deterministic conversion with a full input schema and an output schema, the description supplies the one piece of semantics the schemas cannot encode — the definition of the Australian financial year. 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single 'date' parameter already documents its YYYY-MM-DD format in the schema. The description adds nothing parameter-specific, so the 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?
States a specific verb and resource ('Map a date to the Australian financial year') and immediately defines the domain concept by its boundaries (1 July to 30 June). An agent knows exactly what transformation this performs without opening the schema, and no sibling competes for this function.
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 says 'Use this whenever a question involves an Australian financial year, rather than assuming a calendar year' — a clear trigger condition plus a caution against the default calendar-year assumption. It stops short of naming alternatives, but no sibling tool is a real substitute for this conversion.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_datasetGet DatasetA
Get one dataset's full metadata and its list of resources (files).
Each resource reports queryable: "yes" means query_resource will work on it,
"no" gives the reason (wrong format, or too large), "unknown" means the size could
not be determined in advance. Use the resource_id of a queryable resource with
preview_resource and query_resource.
| Name | Required | Description | Default |
|---|---|---|---|
| portal | No | The portal the dataset came from. | data.gov.au |
| dataset_id | Yes | The dataset name/id from search_datasets. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It does disclose a meaningful behavioral trait of the output: the `queryable` field's three states and what each means for downstream calls. However it says nothing about permissions, rate limits, or cost, and an output schema already exists.
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 the purpose in one sentence, then uses a tight second block to explain the `queryable` states and the follow-up calls. Every sentence earns its place with 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?
An output schema exists so return values needn't be re-explained, and the description covers the one output field an agent must act on (`queryable`) plus the routing to preview/query. Minor gap: no mention of the portal parameter's default or multi-portal behavior.
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 both parameters are already documented, making 3 the baseline. The description adds only that dataset_id comes from search_datasets; it says nothing about the portal parameter or its default.
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: 'Get one dataset's full metadata and its list of resources (files).' This clearly distinguishes it from siblings like search_datasets (list/search), preview_resource and query_resource (operate on resources).
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 routes the agent downstream: use the resource_id of a queryable resource with preview_resource and query_resource. It also implies the entry point by noting dataset_id comes from search_datasets. No explicit 'when not to use' statement, but the chaining guidance is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_organizationsList OrganizationsA
List a portal's publishers with how many datasets each has, largest first.
Only publishers with at least one public dataset are counted. The first result is the portal's largest publisher; use q to find a specific one beyond the limit.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Optional case-insensitive substring filter, e.g. "bureau". | |
| limit | No | Maximum publishers to return, capped at 100. | |
| portal | No | The portal to list publishers for. | data.gov.au |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description carries the full burden. It does disclose meaningful behavior: result ordering (largest first), an inclusion filter (only publishers with >=1 public dataset), and that the first result is the portal's largest publisher. It does not state that this is a read-only operation, nor anything about permissions or what happens past the limit cap.
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, front-loaded with the core action and ordering, then the inclusion rule, then the parameter hint. Nothing is repeated from the schema and no sentence is 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?
For a zero-required-parameter read tool with an output schema and full schema coverage, the description supplies ordering, filtering rules, and the q/limit interaction, which is enough to call it correctly. The only gap is the absence of any explicit read-only/safety statement, which matters slightly given there are no annotations.
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 is 3, but the description adds real meaning beyond the schema: it explains why q exists (results beyond the default limit are not returned, so q is the way to locate a specific publisher) and ties q to the limit behaviour. That interaction is not visible from the parameter descriptions alone.
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 a portal's publishers") plus the enrichment it performs (dataset count per publisher) and the ordering (largest first). This distinguishes it from list_portals and search_datasets by resource and shape, though it never names those siblings, and the name/detail mismatch ("organizations" vs "publishers") is resolved only by reading the description.
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 gives one concrete usage hint ("use q to find a specific one beyond the limit") and a scope constraint (only publishers with at least one public dataset are counted). However, it never says when to pick this over search_datasets or list_portals, so routing must be inferred from the resource noun.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_portalsList PortalsA
List the Australian open data portals this server can query, with dataset counts.
Call this when the user asks what data sources are available.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are supplied, so the description carries the burden. It does disclose the behavioral shape of the response (a fixed enumeration including dataset counts) and 'list' implies a safe read, but it says nothing about cost of the call, whether counts are cached/live, or any permission requirements.
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, zero filler. What it returns is front-loaded and the usage trigger follows; no sentence 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?
An output schema exists, so return-value documentation is not the description's job, and the description still conveys the essential content (portals plus dataset counts). For a no-arg read-only enumeration, nothing an agent needs to 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 tool takes zero parameters, so there is nothing for the description to disambiguate; the empty schema is self-explanatory and the description correctly adds no parameter claims. Baseline 4 applies for a parameterless tool.
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 Australian open data portals this server can query') plus the payload detail ('with dataset counts'). It implicitly separates itself from list_organizations by scoping to portals/data sources, but never names a sibling to make the distinction explicit.
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 a clear trigger condition: 'Call this when the user asks what data sources are available.' That is actionable context, but there are no exclusions or named alternatives (e.g. use list_organizations for publishers, search_datasets for keyword lookup).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
preview_resourcePreview ResourceA
Show a resource's columns, their types, a sample of rows, and the total row count.
ALWAYS call this before query_resource: you cannot write correct SQL without first seeing the column names. Costs nothing for resources held in the portal's DataStore; for others it downloads and caches the file, which then makes the follow-up query free.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Sample rows to return, capped at 20. | |
| portal | No | The portal the resource belongs to. | data.gov.au |
| resource_id | Yes | From get_dataset's resources list. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose real behavioral traits: a cost/side-effect profile (free for portal DataStore resources, otherwise it downloads and caches the file and makes the follow-up query free). It still omits permissions/auth requirements, whether the caching is durable across sessions, and failure modes for missing resource_id.
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, each earning its place: one defines output, one defines the call ordering requirement, one explains cost. The prerequisite is front-loaded rather than buried at the end.
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?
An output schema exists, so return values need not be explained, and the description still summarizes them usefully. It covers the prerequisite, the cost model, and the caching side effect, leaving only auth/permission context and error behavior unaddressed — modest gaps for an otherwise well-specified read-with-caching tool.
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 resource_id, limit, and portal are already documented in the schema; the baseline is 3. The description only alludes to the sample size implicitly ('a sample of rows') and adds no formatting or portal-selection guidance 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 first sentence names a specific verb plus the exact payload the agent gets back: columns, column types, sample rows, and total row count. That is enough to separate it from get_dataset (metadata) and query_resource (SQL execution) 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states an explicit prerequisite ('ALWAYS call this before query_resource') and gives the reason ('you cannot write correct SQL without first seeing the column names'), which is precisely the routing decision an agent needs. The alternative tool is named and the condition that triggers this one is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_resourceQuery ResourceA
Run a read-only SQL query over one tabular resource and get the rows back.
The SQL dialect is DuckDB. The resource is always exposed as the table t,
whatever the file was called. Only a single SELECT statement is permitted:
no DDL, no DML, no reading other tables or files.
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | A single DuckDB SELECT over the table `t`. | |
| limit | No | Maximum rows to return, capped at 100. | |
| portal | No | The portal the resource belongs to. | data.gov.au |
| resource_id | Yes | From get_dataset's resources list. Must have queryable == "yes". |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and largely succeeds: it discloses read-only semantics, the DuckDB dialect, the fixed table alias `t`, and hard restrictions (no DDL, no DML, no other tables or files). It omits timeout/error behavior on violations, which keeps it short of a 5.
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 paragraphs, front-loaded with the action and then the constraints. Every sentence carries information; nothing is repeated from the name or title.
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?
An output schema exists, so return format need not be explained, and required params are documented in the schema. The description covers dialect, restriction scope, and table naming; only edge-case behavior (errors, timeouts) 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%, so the baseline is 3. The description reinforces the dialect and table naming for the `sql` param but adds no syntax examples, escaping rules, or guidance beyond what the schema already states.
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?
Starts with a specific verb+resource ('Run a read-only SQL query over one tabular resource') and states the return ('get the rows back'). This distinguishes it from siblings like preview_resource and get_dataset, which do not accept SQL.
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 constrains the operation (single SELECT, DuckDB, table `t`) but never says when to choose this over preview_resource or how to obtain a queryable resource_id (the schema covers the latter). Usage is implied by the SQL-execution framing rather than explicitly routed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_datasetsSearch DatasetsC
Search Australian government datasets by keyword.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | Keywords, e.g. "bus stops", "rainfall", "hospital waiting times". | |
| limit | No | Maximum datasets to return, capped at 50. | |
| format | No | Optional resource format filter, e.g. "CSV". | |
| portal | No | "data.gov.au" (federal, default), "data.sa.gov.au" (South Australia), or "all" to search both. | data.gov.au |
| organisation | No | Optional publisher name to filter by, as returned by list_organizations. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden, yet it discloses nothing beyond the one-line purpose. It does not say whether results are paginated, how ranking works, whether the portal parameter changes coverage, or anything about rate limits or read-only safety.
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 front-loaded sentence with no filler or redundancy. It is efficient, though its brevity borders on under-specification rather than disciplined conciseness.
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?
An output schema exists, so return values need not be explained, and the rich schema covers all five parameters. However, for a multi-portal search tool with zero annotations, the description omits scoping behavior (e.g. that portal defaults to federal only and 'all' broadens coverage) that would help an agent 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%: q, limit (cap 50), format, portal, and organisation are all documented in the schema, including the portal enumeration semantics in prose. The description adds no parameter meaning beyond that, so the 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?
States a specific verb (search) and resource (Australian government datasets) with the query mechanism (keyword). It is clear on its own, but it does not differentiate itself from siblings like get_dataset or query_resource, which an agent must infer.
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?
No when-to-use guidance, no prerequisites, and no mention of alternatives. The agent must infer that this is the discovery entry point versus get_dataset (single record retrieval) or query_resource (data extraction) purely from the names.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
validate_abnValidate AbnA
Validate an Australian Business Number against the ATO's official checksum.
Use this instead of judging an ABN yourself — the check is a deterministic modulus-89 calculation and guessing gets it wrong.
| Name | Required | Description | Default |
|---|---|---|---|
| abn | Yes | 11 digits, with or without spaces or hyphens. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description still discloses the behavioral profile that matters: the check is deterministic and authoritative (modulus-89 against the ATO checksum), which tells the agent the result is trustworthy and not heuristic. It doesn't mention error behavior for malformed input, but as a stateless pure validator that is 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?
Two short sentences with no filler; the what is front-loaded and the why/when follows immediately. Every clause 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?
For a one-parameter validator with an output schema present, the description supplies everything the agent needs: what is validated, against what authority, and why to prefer it over inference. Return format is covered by the output schema, so nothing essential 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 the single parameter's format ('11 digits, with or without spaces or hyphens') is fully documented in the schema. The description adds no parameter-level detail beyond that, so the 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 precise verb plus resource ('Validate an Australian Business Number') and even names the authoritative mechanism ('ATO's official checksum'). No sibling tool in the set performs validation, so the scope is unambiguous at a glance.
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 explicitly tells the agent when to use it ('Use this instead of judging an ABN yourself') and names the alternative it is displacing — self-reasoning. It gives the rationale (deterministic modulus-89), though it offers no explicit exclusions or edge-case conditions.
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.1.0- First observed
au_financial_year - First observed
get_dataset - First observed
list_organizations - First observed
list_portals - First observed
preview_resource - First observed
query_resource - First observed
search_datasets - First observed
validate_abn
TDQS
Scored across 8 tools
Each tool targets a distinct resource or action: portals vs organizations, search vs get for datasets, and preview vs query for resources. The two utility tools (validate_abn, au_financial_year) sit outside the dataset workflow but have clear, non-overlapping purposes.
Nearly all names follow a verb_noun snake_case pattern (list_portals, search_datasets, get_dataset, preview_resource, query_resource). The two exceptions, validate_abn and au_financial_year, are still readable and consistent in casing.
Eight tools is well-scoped for a read-only open-data query server. Each tool earns its place, covering discovery, inspection, and querying without bloat.
The surface covers the read-only lifecycle well: discover portals/orgs, search and inspect datasets, preview and query resources, plus ABN and financial-year helpers. Minor gaps exist around exporting or downloading raw files, but core workflows are complete.
Maintenance
Related MCP Connectors
Search, filter, count and sum Australian government open data, with every version kept.
Reliable Australian business-day, public-holiday and deadline tools for AI agents.
List datasets, schemas, run APL queries, and use prompts for exploration, anomalies, and monitoring.
Official UK, US and Australia public datasets as filtered CSV, REST API and MCP for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to search, explore, and query Calgary's open data portal using SoQL, with tools for dataset discovery, metadata inspection, and data querying.MIT
- AlicenseNot gradedqualityBmaintenanceEnables querying Brussels Open Data datasets through natural language or direct tool calls, including search, metadata retrieval, and SQL-like queries.25 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to browse, search, and query Australian Bureau of Statistics datasets through the ABS Data API, exposing dataflow discovery, dimension/code structure inspection, and SDMX dataKey-based retrieval of time-indexed observations with decoded labels. It can run as a hosted gateway endpoint or a local stdio server.13 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables 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