datosgobdo-mcp
This server lets you search, inspect, download, and analyze Dominican open data from datos.gob.do directly through your AI assistant, without writing code.
Discovery & catalog navigation: Search datasets by keyword, organization, tag, or group; retrieve full dataset/resource metadata; list recent datasets; browse institutions, thematic groups, and tags; autocomplete names; get portal-wide statistics.
Resource access & preview: Preview file contents (CSV, TSV, XLSX, XLS, ODS, JSON) with head/tail/random sampling and a 5 MB cap; get resource metadata and search resources by name; check whether resource URLs are actually downloadable.
Analytics & querying: Inspect column schema and sample values; auto-profile resources with row counts, nulls, distinct counts, min/max/mean, and top values; filter rows with typed WHERE/SELECT/ORDER BY/LIMIT conditions; aggregate with GROUP BY and functions like count, sum, avg, median, min, max, stddev, variance; compute quantiles; find duplicates; detect outliers with IQR; run read-only SQL queries (DuckDB) against cached resources; save results to local CSV.
Cache management: View cache statistics and clear the local Parquet cache to free disk space.
Guided workflows (prompts): Use slash commands /empezar_aqui, /serie_temporal, /auditar_nomina, /verificar_fuente, /explorar_institucion, and /cruzar_fuentes to guide common analysis tasks.
Optional GCP pipeline: Load resources to BigQuery for large-scale cross-dataset JOINs and inspect BigQuery tables.
Key characteristics: Read-only toward the portal; runs locally for privacy; transparent about data quality and provenance; caches downloads as Parquet for fast repeat queries; supports files up to 100 MB for analytics and 5 MB for previews.
Click on "Install 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., "@datosgobdo-mcpHow much does the Judicial Branch spend on salaries?"
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.
datosgobdo-mcp
Ask an AI assistant a question about Dominican public data, and get an answer traced back to the government file it came from.
This is a Model Context Protocol server for datos.gob.do, the Dominican Republic's official open-data portal. It plugs into Claude Desktop, Claude Code, Cursor, ChatGPT Desktop or any MCP-compatible client, and lets the model search the catalog, download the actual files, parse them, and run real analysis — without you writing code, opening a URL, or downloading a spreadsheet.
Official source. The canonical repository is
alcastaro/datos.gob.do-MCP-server. The only official distributions are the PyPI packagedominican-open-data-mcpand the MCP Registry entryio.github.alcastaro/datos.gob.do-MCP-server. Copies published elsewhere are not maintained by the author and may be outdated or modified — verify against this repository before installing.
This README is written to be read in order. Part 1 needs no technical knowledge. Part 2 teaches what MCP actually is, using this server as the worked example. Parts 3 to 6 are the reference and the engineering detail. If you want the same material as a walkthrough, read the Tutorial (Español).
Contents
Part 1 — Start here (no technical knowledge needed)
Part 2 — Understanding MCP (educational)
Part 3 — What this server exposes
Part 4 — Why this server exists
Part 5 — Technical reference
Part 6 — Development
Part 1 — Start here
Related MCP server: eRegulations MCP Server
1. What this is, in plain words
The Dominican government publishes thousands of files: public payrolls, budget execution, hospital activity, migration flows, procurement contracts, electricity losses, forest fires. It is all public. Almost nobody reads it, because reading it means knowing which of 266 institutions published what, finding the file, downloading a spreadsheet with the header on row 3, and knowing what to do next.
This server hands that whole job to your AI assistant. You ask in your own words. The assistant finds the dataset, downloads the file from the institution's own server, figures out the columns, runs the sum or the average, and tells you the answer along with where it came from and what it had to leave out.
Three things worth knowing up front, because they shape everything else:
It only reads. Nothing here can modify the portal or publish anything. There is no login and no password.
It runs on your computer, alongside your assistant. Your questions do not pass through a server owned by this project.
It tells you when the data is bad. Roughly half the catalog cannot be downloaded programmatically at all, and the tools say so instead of inventing a number. That is the point of the whole design, not a caveat buried at the bottom.
2. Quick start
You need uv, a small tool that runs Python programs without you installing anything else. On macOS or Linux, paste this into a terminal:
curl -LsSf https://astral.sh/uv/install.sh | shOn Windows, follow the uv installation page.
Then tell your assistant about the server.
Claude Desktop. Open Settings → Developer → Edit Config, or edit the file directly:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
Paste this, replacing YOUR_USERNAME:
{
"mcpServers": {
"datosgobdo": {
"command": "/Users/YOUR_USERNAME/.local/bin/uvx",
"args": ["dominican-open-data-mcp"]
}
}
}Use the full path to uvx — Claude Desktop does not read your shell's PATH. Then quit Claude Desktop completely (Cmd+Q on macOS, not just closing the window) and reopen it. Under Settings → Developer you should see datosgobdo running.
Nothing else is required. If you later want to change a setting — the network guard, the cache directory — it goes in an "env" block inside this file, not in your shell: see §13.
Claude Code. One line:
claude mcp add datosgobdo -- uvx dominican-open-data-mcpAnything else. Same idea: register uvx as the command with dominican-open-data-mcp as its argument. The MCP clients directory lists which clients support which features. Full options — dev versions, local clones, hosted mode — are in §13.
3. The six guided prompts — start with /empezar_aqui
Twenty-four tools is not an invitation. Someone who has never seen this catalog has no way to know that payrolls, budget execution and public investment are the three things it covers best.
So the server ships six prompts: ready-made questions, written to encode the habits that took a full catalog audit to learn. In Claude Code and Claude Desktop they appear as slash commands. Type:
/empezar_aquiand the assistant will introduce you to the portal, tell you what it covers well, propose three concrete questions you could ask next, and warn you up front about what cannot be downloaded.
The other five take one argument each:
Prompt | You give it | What it does |
| — | Portrait of the portal and three questions to start with. |
| a topic | Builds a year-by-year series, declaring the real period covered and refusing to treat the year column as a measure. |
| an institution | Sum, average and salary distribution of a public payroll, declaring how many rows were excluded and why. |
| a resource URL | Checks scope, provenance and shape of a file before you rely on it. |
| an institution | Inventory of everything that institution publishes, with the real download status of each file. |
| a topic | Crosses two resources, declaring units, periods and the limits of the join. |
If your client does not show prompts as slash commands, see its entry in the MCP clients directory — prompt support is optional for clients, and the MCP Inspector can always show and run them.
4. What you can ask
Plain questions, in Spanish or English. Some that work today:
How many datasets are on the datos.gob.do portal, and which institutions publish the most?
Find the five most relevant budget datasets and tell me which institution publishes each one.
How much does the Judicial Branch spend on salaries?
How many active employees does the Ministry of Agriculture have in April 2026, broken down by employment status?
That last one is worth pausing on, because it is the kind of question the whole analytics layer exists for. The Agriculture payroll is a CSV with 826,000 rows and 94 MB — far too big to paste into a conversation. The server downloads it once, converts it to a columnar cache, and answers with a grouped aggregation: 6 status types, roughly 8,915 employees. The first call takes about 14 seconds; every later question about the same file answers in under half a second.
Compare FONDOMARENA's approved versus executed budget over the last three years.
What columns does the Ministry of Interior's vehicle-theft dataset have?
List the ten most recently updated datasets.
Who this tends to be useful for: data journalists who would otherwise write a scraper; researchers who need programmatic access; transparency groups tracking budget execution and procurement; developers prototyping on public data; public officials checking what their own institution already publishes; and anyone curious about how the state actually operates.
5. Read this before you quote a number
This catalog has real defects, and they were measured — a census of the whole thing on 2026-08-08, one resource per dataset, 1,056 resources over real MCP sessions. Four findings change how you should read any figure you get from here:
About half the catalog cannot be downloaded by a program. 561 of 1,056 resources can be read (53.1 %), up from 540 in the census: 0.14.0's format work recovered 21 of them, re-measured against the live portal on 2026-08-13. The largest single cause of the rest is not this server and no version of it can fix it: 360 resources across 98 institutions sit behind a site configuration that refuses programmatic downloads of the files those same institutions publish as open data. From the same address, 21 other government hosts behind the same CDN answer normally — so it is per-site configuration, not our network. A further 15 links are dead and 6 files are unreadable at any encoding.
One in three multi-format datasets contradicts itself. Of 528 datasets whose formats could be compared, 176 disagree on row count or column count. One example: the Treasury's recaudaciones-sirite-2021-2025 has 971,818 rows as CSV and 197,338 as ODS. A citizen downloading the ODS and a journalist downloading the CSV would quote different numbers from the same official dataset. Practical rule: check more than one format before you publish a total.
Numbers are often stored as text. 93 of the 540 resources readable in that census hold numeric columns as text, usually because a handful of cells say N/A or #REF!. The tools read such a column as numbers where each value permits it and report what that cost — see §14. Read values_excluded before quoting the total.
No dataset declares how often it is updated. The periodicidad field is empty in all 1,056. A dataset labelled "2018-2026" may have been fed last month or frozen two years ago; you have to infer freshness from the last period that actually has data.
None of this is a reason not to use the catalog. It is a reason to cite it precisely — which is what /verificar_fuente and the self-describing response fields are for.
Part 2 — Understanding MCP
6. What is MCP? Tools, resources and prompts
Model Context Protocol is an open standard — created by Anthropic, now adopted across the industry — for connecting language models to outside data and capabilities. Instead of every application inventing its own plugin format, a model-facing app (the client, e.g. Claude Desktop) talks to any number of servers over one protocol.
A server can offer three kinds of thing. The distinction matters, because it determines who decides when something is used:
Primitive | Controlled by | What it is | In this server |
the model | Functions the model may call, with typed arguments. The model picks when and with what. | 24 functions: search, download, aggregate, query… | |
the application | Data the app can attach as context, addressed by URI. No side effects, no cost to reason about. | 3 documents + 1 URI template | |
the user | Templates the user invokes deliberately, usually as slash commands. | 6 guided workflows |
flowchart LR
subgraph HOST["Your app — the host"]
U["You"] --> M["The model"]
M <--> C["MCP client"]
end
subgraph SRV["datosgobdo-mcp — the server"]
T["24 tools<br/><i>the model calls these</i>"]
R["3 resources + 1 template<br/><i>the app attaches these</i>"]
P["6 prompts<br/><i>you invoke these</i>"]
end
C -- "JSON-RPC 2.0 over stdio" --> T
C --> R
C --> P
T --> CK["datos.gob.do<br/>CKAN API"]
T --> FS["273 institutional<br/>web servers"]
U -. "/empezar_aqui" .-> P
style T fill:#E6F4EA,stroke:#34A853
style R fill:#FEF7E0,stroke:#FBBC04
style P fill:#E8F0FE,stroke:#4285F4The dotted line is the whole point: a prompt is the one thing here you start. The model never invokes a prompt, and the application never calls a tool.
The protocol also defines client-side primitives — sampling, elicitation, roots — which this server does not use.
Concept guides: tools, resources, prompts. If you want to build one, start with Build a server, and read Part 3 of our Tutorial for what this project learned doing it.
What this server declares on connection, verified over a live session on 2026-08-12:
{
"serverInfo": { "name": "datosgobdo-mcp", "version": "0.14.0" },
"protocolVersion": "2025-11-25",
"capabilities": {
"tools": { "listChanged": false },
"resources": { "subscribe": false, "listChanged": false },
"prompts": { "listChanged": false }
}
}listChanged: false and subscribe: false are honest declarations, not omissions: the tool list is fixed at startup, and no resource here changes often enough to be worth a subscription.
On the two version numbers. The spec links above point to
2026-07-28, the current specification, because that is what you should read. The server negotiates2025-11-25because it pinsmcp>=1.9.0,<2— SDK 2.0 renamedFastMCPtoMCPServerand dropped the old import path with no shim. What 2026-07-28 adds and this server therefore does not implement:server/discover, the per-request_metafields, and per-request log levels. Migration is tracked, not accidental.
7. What is datos.gob.do?
The Dominican government's official open-data portal, operated by OGTIC. It runs CKAN 2.11.3 — the same platform behind data.gov (USA), data.gov.uk and much of Latin America.
What the portal declares (queried live on 2026-08-12):
Datasets | 1,061 |
Registered organizations | 266 |
Thematic groups | 11 |
Tags | 874 |
CKAN extensions loaded |
|
What the 2026-08-08 audit measured, which is a different thing and the gap is instructive:
Resources (files) in the catalog | 3,826 |
Organizations that actually own a dataset | 261 of the 266 registered |
Resources tested (one per dataset) | 1,056 |
Machine-readable | 561 (53.1 %) — 540 in the 2026-08-08 census, plus 21 recovered by 0.14.0 |
Rows downloaded and cached | 13,371,601 in the census, plus 82,490 recovered — and 846,388 more in sibling files outside it |
Resources hosted on | 66 — the rest live on 273 other domains |
That last row is the structural fact behind most of this project. The portal is a catalogue of links, not a repository. Each institution keeps its own files on its own web server, so availability, format hygiene and access rules are decided in 273 places the portal does not control.
Note also what the extension list does not include: CKAN's DataStore is not installed here. That single fact is why this server looks the way it does — see §12.
This project was inspired by datagouv-mcp (Etalab, France), but datos.gob.do runs CKAN rather than udata, so the implementation is its own.
Part 3 — What this server exposes
8. Tools (24, plus 3 optional)
Typed functions, grouped in five families. The data-producing tools (analytics, preview, cache) return typed outputSchema / structuredContent so hosts can validate results; navigational metadata tools return JSON. Every portal-facing tool is annotated readOnlyHint: true; network-facing ones openWorldHint: true.
Every tool answers with one object. Listings name what they carry and count it — {organizations, count, limit_reached}, {tags, count, limit_reached}, {groups, count}, {suggestions, count, kind, query}. limit_reached matters because the caps are lower than the catalog: 200 institutions against 266, and any tag listing without a query is a sample of 874.
Discovery
Tool | What it does |
| Search datasets by keyword, organization, tag, or group. Combinable filters, pagination. |
| Full metadata for a dataset: title, description, licence, author, and every resource with its direct download URL. |
| Datasets sorted by most-recent modification. Useful for monitoring portal updates. |
| Portal-wide counts (datasets, organizations, groups, tags). |
Resource files
Tool | What it does |
| Metadata for a single resource (URL, format, size, date). |
| Search resources by name. |
| Download a file and return N rows. CSV, TSV, XLSX, XLS, ODS, JSON. 5 MB cap. Sample mode: head / tail / random. |
| Ask up to 25 URLs whether their files can actually be downloaded, without downloading them. Returns a class per URL — reachable, browser challenge, site rule, dead link, no answer — because a catalog entry is not evidence the file is still there. |
Analytics
DuckDB over a persistent Parquet cache. The first call per resource downloads and caches (up to 100 MB); later calls are sub-second. The cache is worth roughly 44× on measured medians.
Tool | What it does |
| Column names, inferred types, sample values. The cheap reconnaissance step before any aggregation. |
| Auto profile: row count, per-column nulls and distinct counts, min/max/mean on numerics, top-N on categoricals. |
| Typed WHERE / SELECT / ORDER BY / LIMIT. Ops: |
| Typed GROUP BY + aggregations + HAVING + ORDER BY. Fns: |
| Percentile distribution (p25/p50/p75/p90/p95/p99) of numeric columns. |
| Rows duplicated on given columns (or all). Essential for payroll and census quality checks. |
| Rows outside the IQR fence on a numeric column, sorted by distance from the median. |
| Power-user escape hatch: read-only SQL against table |
| Write a filter or SQL result to a local CSV. Absolute destination, or the default |
| On-disk Parquet cache statistics, plus the server's own identity and effective security mode. |
| Wipe the local Parquet cache. The only non-read-only tool in the server ( |
Catalog
Tool | What it does |
| Publishing institutions with a dataset count each. |
| Detail for one institution (description, dataset count, URL). |
| Thematic categories with counts. |
| Tags, optionally filtered by prefix. |
Autocomplete
Tool | What it does |
| Resolve partial names for datasets, organizations, groups or tags — for when the user only gives part of a name. |
GCP pipeline (optional)
Installed with pip install 'dominican-open-data-mcp[gcp]'; three extra tools register automatically when the Google Cloud libraries are present, taking the count to 27. They make this server the ingestion half of a BigQuery pipeline: discover here, load to BigQuery, then query with Google's own BigQuery MCP for the cross-dataset JOINs a local DuckDB cache cannot do.
Tool | What it does |
| Resource → Parquet cache → GCS upload → BigQuery external table (default, zero-ETL) or load job. |
| List tables in a BigQuery dataset. |
| Schema, row count and source URIs of a table. |
Set DATOSGOBDO_GCS_BUCKET to avoid passing the bucket on every call. Preview status: these three are outside the stability promise and have not been exercised against a live project.
9. Resources (3) and one resource template
Resources are addressed by URI and read by the application, not called by the model. They exist here for the facts that are small, stable, and wasteful to spend a tool call on. All three are read-only and free of side effects.
URI | Type | What it holds |
|
| Portal totals: datasets, institutions, groups, tags. |
|
| Every publishing institution with its dataset count — the answer to "which institution?" before any query. |
|
| The four fields that make a number checkable, and what to do when they are missing. |
That last one is a resource rather than a prompt on purpose: it is not a request to act, it is reference text worth having in context while you work.
One resource template — a URI pattern with a parameter, so one definition addresses every dataset in the catalog:
Template | Fill in | Returns |
| a dataset id or slug | That dataset's metadata as attachable context. |
Example: datosgobdo://dataset/nomina-poder-judicial.
How to use them. In Claude Desktop, resources appear in the attachment menu of a conversation with the server connected. In other clients, check the clients directory — resource support is optional. In every client, the Inspector's Resources panel lists them and shows the raw payload, including expanding the template.
10. Prompts (6)
Prompts are user-controlled: nothing invokes them but you. Each one here encodes a habit learned the hard way during the catalog audit — which is why they are worth using even when you know the tools well.
Prompt | Argument | The habit it encodes |
| — | Orientation before exploration, and the download warning stated up front rather than discovered later. |
|
| Declare the real period the data covers, not the one in the title; never treat the year column as a measure. |
|
| Report excluded rows and their provenance alongside any salary total. |
|
| Check scope, provenance and shape before relying on a file. |
|
| Inventory with each file's real download status, not just its catalog entry. |
|
| State units, periods and join limits before crossing two sources. |
How to invoke them. In Claude Code and Claude Desktop, as slash commands: /empezar_aqui, or /serie_temporal and then the topic when asked. Some clients present them in a menu instead. In the Inspector, the Prompts panel lists each one with its arguments and renders the expanded text before anything is sent to a model — the most reliable way to see exactly what a prompt does.
11. Which primitive to reach for
You want to… | Use | Why |
Answer a specific question about data | a tool, via ordinary conversation | The model chooses and combines them. |
Start from zero, or follow a rigorous method | a prompt | Six workflows with the caveats already built in. |
Give the assistant standing background | a resource | Attach it once; no tool call, no tokens spent deciding. |
Pin one dataset as context | the resource template |
|
Do something the typed tools do not cover |
| Read-only SQL, sandboxed. The escape hatch, not the first move. |
Part 4 — Why this server exists
12. How it compares with other CKAN MCP servers
CKAN powers hundreds of government portals, so a generic CKAN MCP server is an obvious idea and a good one. The most developed is ondata/ckan-mcp-server (MIT, TypeScript, adopted by AgID, Italy's digital agency): dataset search with full Solr syntax, organizations and groups, discovery across ~950 portals, and tabular access through CKAN's DataStore API. It points at any portal via a server_url argument. If your portal has DataStore populated, use it — it is broader than this project and more actively released.
The difference is not quality, it is where the data lives. Verified live on 2026-08-12:
GET /api/3/action/status_show → extensions: ["activity", "datosgobdo_theme"]
GET /api/3/action/datastore_search
→ 400 "Action name not known: datastore_search"
resources with datastore_active: 0 / 254 sampleddatos.gob.do has no DataStore. There is no datastore_search, no SQL endpoint, and not one resource is loaded into it. A generic CKAN MCP server pointed here can search metadata perfectly well and cannot read a single row of data. That is not a flaw in it — the extension is optional in CKAN and this portal never enabled it.
So the two projects divide along a real line:
Portals with DataStore | Portals that are file catalogs | |
Where the data is | Loaded into CKAN, queryable by API | Files on 273 institutional web servers |
How to read it |
| Download, sniff the encoding, parse, cache, query |
Best tool | this one |
Everything that makes this codebase larger than a CKAN API wrapper exists because of that right-hand column: encoding detection scored by the Spanish it recovers, streaming ODS parsing (loading the full DOM multiplied memory by ~580×), a Parquet cache keyed on the parser build, numeric coercion that declares what it excluded, page→file resolution for the 37 URLs that answer with HTML, an SSRF guard for downloads reaching 273 third-party hosts, and an optional archived-copy fallback that always says when it fired.
If you are building for another Latin American portal, check status_show first. If DataStore is absent — as it is in the Dominican Republic — the file-reading pipeline in this repository is the part you will need, and the Tutorial documents it so it can be reused.
Part 5 — Technical reference
13. Installation and client configuration
Option A — uvx from PyPI (recommended)
Package: dominican-open-data-mcp.
uvx dominican-open-data-mcpA short alias binary ships too — both launch the same server:
uvx --from dominican-open-data-mcp datosgobdo-mcpuvx downloads the package, builds an isolated venv and runs it. First run takes seconds; later runs are instant.
Upgrading from ≤ 0.7.0? Those releases pinned
mcp>=1.9.0with no upper bound, and MCP Python SDK 2.0 (2026-07-28) removed themcp.server.fastmcpimport path — a fresh install fails withModuleNotFoundError. Install 0.7.1 or later, or pin it yourself:uvx --with "mcp<2" --from dominican-open-data-mcp datosgobdo-mcp.
Option B — uvx from GitHub (development version)
uvx --from git+https://github.com/alcastaro/datos.gob.do-MCP-server.git datosgobdo-mcpOption C — local clone (for development)
git clone https://github.com/alcastaro/datos.gob.do-MCP-server.git
cd datos.gob.do-MCP-server
uv sync
uv run datosgobdo-mcp # stdio; Ctrl+C to exitmacOS note: do not clone inside
~/Library/CloudStorage/GoogleDrive-*or similar. macOS blocks executing binaries from cloud-synced paths (a TCC restriction). Use~/code/or equivalent.
Client configuration
Claude Desktop and Claude Code are covered in §2. To track the development version in Claude Desktop, replace the args with ["--from", "git+https://github.com/alcastaro/datos.gob.do-MCP-server.git", "datosgobdo-mcp"]; in Claude Code, claude mcp add datosgobdo -- uvx --from git+https://github.com/alcastaro/datos.gob.do-MCP-server.git datosgobdo-mcp.
For Cursor and others, the principle is identical — register uvx as the command. Each client's config file location is in its own docs; the MCP clients directory is the index.
Passing settings to the server: the env block
Every DATOSGOBDO_* variable in this README goes in an "env" object inside the client's config:
{
"mcpServers": {
"datosgobdo": {
"command": "/Users/YOUR_USERNAME/.local/bin/uvx",
"args": ["dominican-open-data-mcp"],
"env": {
"DATOSGOBDO_NETGUARD": "strict",
"DATOSGOBDO_CACHE_DIR": "/Users/YOUR_USERNAME/.cache/datosgobdo-mcp"
}
}
}
}export DATOSGOBDO_NETGUARD=strict in your shell does not reach the server. A stdio MCP server launched by a client inherits only a limited, platform-dependent subset of the environment — MCP debugging guidance is explicit about it. Set the variable in your shell and the server starts in the default mode while you believe it is locked down. This matters most for DATOSGOBDO_NETGUARD, which is a security control (§15).
Two consequences of the same fact, both worth knowing before you file a bug:
Use absolute paths for every path-valued setting. The working directory of a client-launched server is undefined —
/on macOS.DATOSGOBDO_ARCHIVE_DIR=mi-archivoresolves nowhere, and the server now logsis not a directory … Archive fallback stays offrather than going quiet. Same forDATOSGOBDO_CACHE_DIRand for thedestargument ofsave_query_to_csv, which refuses a relative path outright.uv run datosgobdo-mcpin a terminal behaves differently — there the working directory is wherever you ran it, and your shell environment does apply. A bug that only appears under the client is usually this.
For Claude Code, pass them with -e: claude mcp add datosgobdo -e DATOSGOBDO_NETGUARD=strict -- uvx dominican-open-data-mcp.
Hosted mode (experimental)
DATOSGOBDO_TRANSPORT=streamable-http serves MCP over HTTP (stateless, for horizontal scaling) instead of stdio. In this mode save_query_to_csv and clear_cache are disabled — they touch the server's filesystem and shared cache — and cache statistics omit server paths.
Logs are your problem in this mode. Under stdio the client captures the server's stderr and writes it to a file you can tail; over Streamable HTTP it does not. Collect stderr yourself, or wire up OpenTelemetry, and use ordinary HTTP tooling (curl, a browser's Network panel) to inspect requests and SSE streams.
Variable | Default | Meaning |
|
|
|
|
| HTTP bind address. |
|
| DuckDB memory ceiling per connection. |
|
| DuckDB thread cap. |
|
| Wall-clock seconds before a DuckDB run is interrupted. Covers both |
14. What the answers tell you about themselves
Three fields appear in responses when the server had to do something the caller did not ask for. Each exists because a tool used for auditing must not quietly paper over a defect in the data.
numeric_coercion — a column stored as text was read as numbers.
The most common defect in this catalog: 93 of the 540 resources readable in the 2026-08-08 census hold numeric columns as text, because a handful of cells say N/A or #REF! and that is enough to make a whole payroll column non-numeric. aggregate_resource, quantiles_resource and detect_outliers_resource read such a column as numbers where each value permits it, and report what it cost:
"numeric_coercion": [{
"column": "SUELDO BRUTO (RD$)", "coerced": true,
"values_used": 21469, "values_excluded": 37,
"excluded_values": [{"value": "N/A", "count": 21}, {"value": "#REF!", "count": 16}]
}]Read values_excluded before quoting the total. A column less than 90 % parseable is left as text and the reply says why, rather than answering a question about a measure from an arbitrary subset of rows. count and count_distinct are never coerced.
linked_files — the URL served a page, and the page linked data files.
37 catalog resources answer with a web page instead of a file. When one linked file clearly matches the request it is fetched, and cache.resolved_from records {page, followed} — you asked for one URL and received data from another, which the reply says rather than hides. When several candidates are indistinguishable they come back as linked_files with names and scores, for you to choose and call again. Files named clss.csv and xls.csv both exist in this catalog; guessing between them would be inventing.
A file the page opens from JavaScript counts as linked. Some portals put the address in onclick="window.location.assign('…')" and nowhere else — the Tribunal Constitucional publishes all three of its formats that way — so reading only anchors reported "no data file on it" about a page anyone can download from in one click.
cache.format_corrected — the catalog's declared format was wrong, and the reply says which way.
The format in the catalog is a claim about the file, and 83 of 1,595 sibling resources have it wrong in both directions: a spreadsheet registered as CSV, and a CSV registered as ODS. The container is identified from what is inside it — the mimetype member for ODS, a workbook part for XLSX — never from the signature alone, because PK is how both start. A ZIP holding exactly one data file is unpacked and detected_from names the member; a ZIP holding several is left alone, because deciding which one is "the data" would be inventing. source_sha256 always covers what the portal served, so a re-download can be compared against it even when what was parsed came from inside an archive.
A pre-2007 .xls (BIFF/OLE2) cannot be read at all and says so, with what to ask the publisher for. It is the worst-served format in the catalog: 12 of 22 readable.
A note on the CSV save_query_to_csv writes. It is UTF-8 with CRLF line endings and no BOM. That is a correct CSV, and Excel on a Spanish-language Windows will still open it as cp1252 and show Año for Año, because without a BOM that is what Excel assumes. The file is fine; the tool most of this audience will open it with is the problem. Two ways around it: open it through Excel's Data → From Text/CSV, which asks for the encoding, or use LibreOffice, which detects UTF-8. Measured on Windows 11: 4E 6F 6D 62 72 65 2C 41 C3 B1 6F 0D — Nombre,Año\r, valid UTF-8, no EF BB BF.
cache.provenance — the answer came from an archived copy rather than the portal.
Government links rot: the 2026-08-08 census found 15 resource URLs already dead and 98 institutions whose sites refuse programmatic access, so a figure you cite today may be uncheckable next year. Point DATOSGOBDO_ARCHIVE_DIR at a directory holding a manifest.json and its Parquet files, and when a portal cannot be reached the server answers from the archived copy. It is off by default, the portal is always tried first, and the reply always says so — cache.provenance carries the capture date, the sha256, the licence and why the origin was not used. A tool that quietly returned yesterday's copy as today's would stop being useful for an audit.
An archive only holds what could be downloaded, so it does not contain the resources a portal refuses. That is the natural assumption and it is wrong.
Variable | Default | Meaning |
| unset (off) | Absolute path to a directory with |
Set it in the client's env block (§13), with an absolute path. If the directory does not exist the server logs a warning and leaves the fallback off — it will not pretend to be armed.
15. Security and environment variables
Full policy, threat model and reporting process: SECURITY.md. In brief:
Read-only toward the portal. No authentication, no
package_create, noresource_create. The only mutating tool isclear_cache, over the local cache.Two injection surfaces, both closed. User values entering CKAN
fqfilters pass through Solr escaping; every column identifier reaching DuckDB passes an allowlist regex plus a denylist of comment and terminator substrings, then is double-quoted.query_resourceis sandboxed. Beyond validating that the statement is a single read-only SELECT/WITH, the resource is materialized into an in-memory table and thenenable_external_access=false+lock_configuration=trueare set before the user's SQL runs — so DuckDB table functions (read_text,read_csv,glob, …) cannot reach the filesystem or the network.SSRF guard on every download, initial URL and each redirect hop: http/https only, and every address the hostname resolves to must be globally routable. Cloud metadata (
169.254.169.254), loopback, RFC-1918, link-local and IPv6 ULA are blocked. The guarded path covers the metadata HEAD probe as well as the download itself.Byte caps on remote fetches (5 MB preview, 100 MB analytics), streamed — bounding memory and decompression-bomb exposure.
save_query_to_csvrequires an absolute.csv/.tsvdestination, rejects..and system paths, and writes withO_NOFOLLOW.
Variable | Values | Meaning |
|
|
|
| comma-separated, | Operator-trusted hosts — the escape hatch for forks pointing at another CKAN portal. |
Set these in the client's
envblock, not in your shell — §13 shows the exact JSON. A stdio server inherits only a limited subset of the environment, soexport DATOSGOBDO_NETGUARD=strictleaves the server running with the default guard. There is no warning for this, because from the server's side nothing happened. To check:get_cache_statsreports the mode actually in force asserver.netguard_mode, and the startup line in the client's log records the effective mode.
The default is deliberately not a host allowlist: as §7 shows, legitimate resources live on 273 ministry sites, buckets and CDNs.
On the new primitives: prompts here are static templates with arguments interpolated into text — they perform no I/O. Resources are read-only reads of portal metadata. Neither adds a write path.
16. Architecture
src/datosgobdo_mcp/
server.py FastMCP server: 24 tools, 3 resources, 1 template, 6 prompts
ckan.py CKAN client: requests, Solr escaping, formatters, provenance
analytics.py DuckDB layer: typed query builders, coercion, SQL validation
download.py Capped streaming download, fetch headers, encoding detection
cache.py Parquet cache + index, keyed on source and parser build
preview.py Row-level preview parsers (CSV/TSV/XLSX/XLS/ODS/JSON)
pagelink.py Resolves a page URL to the data file it links
archive.py Archived-copy fallback with declared provenance
reachability.py check_resources: classifies why a URL cannot be read
netguard.py SSRF guard for URLs and every redirect hop
models.py Pydantic output models (typed outputSchema)
gcp.py Optional BigQuery/GCS pipeline toolsThe life of a question
What happens between "how many vehicles were registered in 2024?" and the answer.
flowchart TD
Q["Your question"] --> M["The model picks tools"]
M --> S["search_datasets<br/><small>ckan.py</small>"]
S --> G["get_dataset<br/><small>ckan.py</small>"]
G --> SC["get_resource_schema<br/><small>analytics.py</small>"]
SC --> QR["query_resource<br/><small>analytics.py</small>"]
QR --> A["Answer + source_sha256 + the SQL"]
S -.-> CK["CKAN API<br/><i>metadata only</i>"]
SC -.-> DL["The institution's server<br/><i>the actual file</i>"]
style A fill:#E6F4EA,stroke:#34A853The first three steps read the catalog. Only the fourth touches a file — and that is the step no CKAN wrapper can do here, because this portal has no DataStore (§12).
The life of a file
The path a resource takes from URL to answer. Every diamond is a defect found by auditing the real catalog, not a hypothetical.
flowchart TD
URL["Resource URL"] --> NG{"netguard<br/>is this address safe?"}
NG -- no --> STOP["Refused"]
NG -- yes --> CACHE{"Is it in the<br/>Parquet cache?"}
CACHE -- yes --> SQL
CACHE -- no --> DL["Download, capped at 100 MB<br/><small>download.py</small>"]
DL --> HTML{"Is it a web page?"}
HTML -- yes --> PL["pagelink: find the file<br/>the page links or opens"]
PL --> SNIFF
HTML -- no --> SNIFF{"Do the bytes match<br/>the declared format?"}
SNIFF -- no --> FIX["Correct it and say so<br/><small>format_corrected</small>"]
SNIFF -- yes --> ENC["Score the encoding<br/><small>A¤o → Año</small>"]
FIX --> ENC
ENC --> PQ["Parse to Parquet<br/>+ sha256 of the source"]
PQ --> SQL["DuckDB runs the SQL"]
SQL --> OUT["Answer + provenance"]
style STOP fill:#FCE8E6,stroke:#EA4335
style OUT fill:#E6F4EA,stroke:#34A853
style FIX fill:#FEF7E0,stroke:#FBBC04Design decisions
FastMCP over the low-level SDK. Tools are functions decorated with
@mcp.tool()and typed via Pydantic: less boilerplate, automatic argument validation.DuckDB + Parquet instead of pandas. Columnar cache, SQL engine, streaming from disk. A 94 MB payroll answers grouped aggregations in under a second warm, and memory stays bounded.
The cache key includes the parser build — package version plus DuckDB's, because DuckDB's sniffer decides column types. A parser upgrade must not serve types inferred by the old one.
DataStore is absent, so files are parsed client-side. See §12. This is the single decision the rest of the architecture follows from.
Encoding is scored, not guessed. Candidate decodings are ranked by the Spanish they recover, rather than trusting a confidence number — the fix for live mojibake like
A¤oforAño.ODS is parsed by streaming
content.xml. Loading the full DOM turned a 0.70 MB file into 0.41 GB of RSS; ODS is roughly a third of this catalog, so the naive path was untenable.Blocking work runs in
asyncio.to_thread(ODS transcode, encoding detection, Parquet COPY) so a long parse never stalls the event loop.Defensive truncation. Long descriptions — some institutions publish 5+ KB per organization — are cut to 300 characters in list responses, so one call cannot burn thousands of tokens of context.
list_recent_datasetsis reoriented. CKAN exposesrecently_changed_packages_activity_list, but it returns un-hydrated activities ({object_id: "uuid", activity_type: "changed package"}) the model cannot interpret. We usepackage_search?sort=metadata_modified+descand return formatted datasets in one call.All logging to stderr, and none over the protocol. Per the MCP debugging guidance, a stdio server must never write to stdout — it corrupts the protocol stream. The protocol's own logging channel (
notifications/message) was never used here, and as of spec2026-07-28it is deprecated: stderr is now what the specification recommends. Nothing to migrate — but do not "improve" this by adding protocol logging.
Stack
mcp (official Python SDK, FastMCP) · duckdb · httpx · openpyxl (read-only streaming XLSX) · pydantic · stdlib csv, json, xml.etree (streaming ODS).
17. Measured limitations
Measured against the whole catalog on 2026-08-08 — 1,056 resources, one per dataset, over real MCP sessions — not estimated. Every resource that had failed for a reason inside this server's control was re-measured against the live portal on 2026-08-13, after 0.14.0's format work; site refusals and 4xx were not retried, because nothing changed on our side that could affect them.
Not everything published is reachable. 561 of 1,056 resources can be read — 540 in the census, plus 21 recovered by 0.14.0, worth 82,490 rows. The recovery is exact rather than estimated, so the breakdown moves with it: of the 37 that served a web page, 19 now resolve to the file the page links, leaving 18; of the 8 unreadable files, 2 now parse, leaving 6.
cause | resources | can this server fix it? |
Site configuration refuses programmatic downloads | 360 across 98 institutions | No. From one address, 21 other government hosts behind the same CDN serve us normally, so it is per-site configuration rather than our network. |
Failed at transport level (cause not attributable) | 85 | Not established |
Serves a web page with no data file on it | 18 | No — catalogue entries pointing at a landing page |
Dead link | 15 | No |
Unreadable file | 6 | Two are pre-2007 |
CDN whose origin does not answer | 6 | No |
Portal error | 5 | No |
561 + 495 = 1,056. The 360 refusals are untouched by any of this and no version of this server can change them.
A second recovery does not appear in that count, and is larger. 0.14.0 also reads 11 sibling files — a second or third format of a dataset whose one-per-dataset representative was already counted — worth 846,388 rows, among them 622,630 in SeNaSa's ODS and the payroll and housing files MAP, MIVHED and MESCyT publish as JSON. They are excluded from the 561 on purpose: the census measures one resource per dataset, and counting siblings would compare against a denominator that never included them. What it means in practice is that a dataset whose CSV is unreadable may now be readable in another format — §14 explains how cache.format_corrected says so when it happens.
What is established: those sites refuse programmatic access to their own open data from the address measured. What is not established: that a person with a browser in Santo Domingo is refused. That test needs a Dominican residential vantage point and has not been run.
Formats. CSV, XLSX and ODS all read at roughly 93 % of what downloads. JSON was the weakest by a wide margin until 0.14.0 — read_json_auto rejected as malformed what these portals actually publish, which is often a record array wrapped in a metadata envelope, or one object per line — and the files recovered on 2026-08-13 are mostly of that kind. Legacy .xls (BIFF/OLE2) remains the worst served and cannot be read at all: 12 of 22. PDF is not parsed; only its download URL is exposed.
Size. download_resource_preview caps at 5 MB; analytics tools at 100 MB. A single value larger than 16 MB exceeds DuckDB's limit and the file cannot be parsed.
Shape. 41 resources put a title or logo above the real header row, which garbles the auto-detected schema — inspect with download_resource_preview and project columns explicitly. 25 come back with generic column names (column00, unnamed). 93 hold numbers as text, handled and declared per §14.
Formats can disagree with each other. 176 of 528 comparable multi-format datasets differ in row or column count, and in 11 cases one format is empty while another carries the full table. Reading a single format is not evidence of what the dataset contains.
Encoding is effectively solved: one file in the census's 540 still shows damaged accents, and that file is encoded in two codepages at once, so no single reading is correct for it.
Freshness cannot be read from metadata. periodicidad is empty for all 1,056 datasets.
Windows: tested on 2026-08-13, and here is exactly how far. Windows 11 (build 26200), Python 3.13, Defender's real-time protection on, a non-administrator account. The suite runs green — 518 passed, 5 skipped, the one skip being a POSIX-only O_NOFOLLOW test. Encoding holds end to end: a cp1252 payroll comes back with Año and UREÑA intact, 135 of 200 institution names carry non-ASCII and none arrive mangled, and paths with accents and spaces work. An aggregation over a 108,038-row payroll matched an independent Decimal recomputation to the cent. Defender cost nothing measurable — the 40 MB cold read is dominated by the publisher's ~1 MB/s, and repeated raw downloads varied more between themselves than Windows differed from macOS.
What is still not tested on Windows, and therefore not claimed: a user profile that is itself accented (C:\Users\José Pérez\, common in the Dominican Republic — only accented sub-folders were exercised), a Downloads folder redirected into OneDrive, Claude Desktop as the client (the transport was driven by a different MCP client), Windows installed on a drive other than C:, and a Defender exclusion measured before-and-after, which needs administrator rights. The Windows-only branch of the cache lock is likewise still awaiting a run on Windows: its retry policy is tested, its four-line msvcrt shim is not.
Untested, and therefore not claimed: the hosted streamable-http transport under real load, the three GCP tools against a live project, and concurrent use beyond four processes.
Part 6 — Development
18. Development, testing and the MCP Inspector
Local setup
git clone https://github.com/alcastaro/datos.gob.do-MCP-server.git
cd datos.gob.do-MCP-server
uv sync
uv run pytest # hermetic by default: no network requiredThe MCP Inspector
The MCP Inspector is the protocol's own developer tool. It speaks MCP directly, so it shows what the server actually exposes with no model in between — the best way to see tools, resources, templates and prompts as the protocol sees them. Requires Node 22.19+ and installs nothing permanent:
# The published package — no clone needed
npx -y @modelcontextprotocol/inspector uvx dominican-open-data-mcpIt prints a URL carrying a one-time token. Open it for four panels:
Tools — all 24 with their schemas. Call one and read the raw
structuredContent, includingnumeric_coercion,source_sha256andcomputation.Resources — the three URIs and the
datosgobdo://dataset/{dataset_id}template, with raw payloads.Prompts — the six, with their arguments, rendered to their expanded text before anything reaches a model.
Monitoring — live JSON-RPC traffic in both directions.
From a clone, scripts/inspector.sh wraps both cases:
./scripts/inspector.sh # published package
./scripts/inspector.sh dist/dominican_open_data_mcp-*.whl # a local build
./scripts/inspector.sh --cli --method tools/list --format jsonThe local-build path needs that wrapper: the Inspector reads everything after the server command as its own flags, so uvx --from ./dist/….whl … fails with Connection closed because --from never reaches uvx.
CLI mode exits with meaningful codes — 0 success, 3 needs auth, 4 unreachable, 5 the tool returned an error — so it drops straight into CI:
npx -y @modelcontextprotocol/inspector --cli uvx dominican-open-data-mcp \
--method tools/list --format json | jq -r '.result.tools[].name'
npx -y @modelcontextprotocol/inspector --cli uvx dominican-open-data-mcp \
--method prompts/list --format json | jq -r '.result.prompts[].name'
npx -y @modelcontextprotocol/inspector --cli uvx dominican-open-data-mcp \
--method resources/templates/list --format jsonLogs
Claude Desktop writes one log file per server, plus its own:
tail -f ~/Library/Logs/Claude/mcp-server-datosgobdo.log # macOS — this server
tail -n 20 -F ~/Library/Logs/Claude/mcp*.log # macOS — all servers + the clienttype "$env:AppData\Claude\logs\mcp*.log" # WindowsThe server logs startup (endpoint, transport, network-guard mode, archive on or off), cache hits and misses, page→file substitutions, suspicious parse shapes, misconfigured environment variables, fatal errors with traceback, and shutdown — all to stderr, which the client captures. Under DATOSGOBDO_TRANSPORT=streamable-http it does not: see §13.
Logs contain resource URLs, cache keys and destination paths. They contain no credentials — the server holds none for the portal — and the optional GCP tools authenticate through your own ADC, which is never logged.
When the client itself is the suspect rather than the server, Claude Desktop can open Chrome DevTools: write {"allowDevTools": true} to ~/Library/Application Support/Claude/developer_settings.json, then Cmd-Option-I. The Console panel shows client-side errors, the Network panel shows message payloads and timing.
Iteration
Commit and push to
main.Clear the
uvxcache to force a refresh:uv cache clean dominican-open-data-mcp(keyed on the distribution name, not the binary name).Restart the MCP client.
For faster loops, point the client at your clone: command: /path/to/clone/.venv/bin/datosgobdo-mcp.
Manual check against the live API
uv run python -c "
import asyncio
from datosgobdo_mcp import ckan
print(asyncio.run(ckan.get_site_stats()))
asyncio.run(ckan.close_client())
"19. Contributing, credits, how to cite, licence
Contributing
Pull requests welcome. Areas where help would land well:
Header detection. 41 resources put a banner above the real header row. In XLSX this can cost the whole file:
precios_productos_primera_necesidad(PROCONSUMIDOR) carries 890 rows in a sheet declaringdimension A1:K890, and reads as 1 column and 0 rows because cell A1 is a title. The CSV sibling recovers all 890 rows but names themcolumn00…column10. Detecting and skipping the banner would recover real data.Cross-format reconciliation. Given a dataset with several formats, pick the trustworthy one rather than the first one listed.
JSON parsing, the weakest format here.
Generalizing
ckan_endpointso the same file-reading pipeline serves other DataStore-less portals in the region.Windows testing, currently unclaimed.
Credits
Developed by Alberto Castillo Aroca (@alcastaro) with contributions from Juana Casique (@juanacasique).
Data published by the institutions of the Dominican State via datos.gob.do, a portal operated by OGTIC.
Inspired by datagouv-mcp (Etalab, Government of France). For CKAN portals that do have DataStore enabled, ondata/ckan-mcp-server is the reference implementation and worth using instead — see §12.
How to cite
If you use this server — or a figure obtained through it — in an article, report, dataset or talk, please cite it. GitHub's "Cite this repository" button reads CITATION.cff and offers APA and BibTeX directly.
Castillo Aroca, A. (2026). dominican-open-data-mcp: an MCP server for datos.gob.do [Computer software]. OLDS — Observatorio Latinoamericano de Desarrollo Sostenible. https://github.com/alcastaro/datos.gob.do-MCP-server
This is a request, not a licence condition: the MIT terms are unmodified, so nothing here restricts your use. Citation matters for a different reason — figures from this catalog carry caveats (what a numeric coercion excluded, which files could not be downloaded at all), and a citation is how a reader gets back to them.
Cite the institution too. This server reads data; it does not produce it. Every figure belongs to the Dominican government body that published it, and get_dataset returns that institution's name for exactly this purpose.
Licence
MIT. See LICENSE.
Data accessed through this MCP is subject to the licence under which each Dominican institution publishes it on datos.gob.do. Verified across the catalog: 1,020 datasets are ODbL, 15 CC-BY, 6 PDDL, 3 other public-domain terms, and 12 declare no licence at all — those twelve should stay out of any redistribution.
20. Privacy Policy
Full text: docs/PRIVACY.md · Spanish: docs/PRIVACIDAD.md · Contact: ai@olds2030.org
No accounts, no credentials, no telemetry. The server has no user registry and sends nothing to its authors. Everything it reads is public government data.
Your search terms do leave your machine. They travel to https://datos.gob.do/api/3/action, because that is where the search runs — the portal is operated by OGTIC under its own terms.
What it connects to. One catalog API (datos.gob.do) and 273 distinct file hosts, because institutions publish files on their own sites. Measured over the August 2026 census (1,056 resources, 258 institutions): 1,033 (97.8 %) sit on Dominican domains; 23 do not — 9 on drive.google.com, 10 across Microsoft SharePoint/Azure Blob/Office Online, 2 on Firebase. Reading one of those means Google or Microsoft sees the request. Full inventory.
What is stored. Downloaded public files are cached as Parquet (~/.cache/datosgobdo-mcp, evicted by size limit, removable with clear_cache). Prompts, questions and results are never stored. save_query_to_csv writes only where you point it.
Hosted mode. No accounts and no prompt logging, but the cache is ephemeral container disk, operational logs include the URLs fetched, and the infrastructure provider processes connection metadata under its own policy. Filesystem tools are disabled there.
Public datasets can contain personal data — payrolls, staff registers. The server neither enriches nor cross-references them; the responsible party is the publishing institution.
Available Tools
23 toolsaggregate_resourceARead-only
Run GROUP BY + aggregations against a cached resource without writing SQL.
Typed wrapper that builds safe DuckDB queries from JSON. Example usage: "How many employees by status in April 2026?" → aggregations=[{col: null, fn: count, alias: empleados}], group_by=["Estatus"], filters=[{col:"Año",op:"=",val:2026},{col:"Mes",op:"=",val:"Abril"}], order_by=[{col:"empleados",dir:"desc"}].
First call downloads + caches the file. Subsequent calls reuse the cache. Returns one row per group with the aggregation values.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Direct URL to the file (CKAN resource 'url' field). | |
| format | Yes | Format declared in CKAN. Accepts: csv, tsv, xlsx, json. | |
| aggregations | Yes | List of {col, fn, alias}. Valid fns: count, count_distinct, sum, avg, mean, median, min, max, stddev, variance. col=null or col='*' means COUNT(*). Example: [{"col":null,"fn":"count","alias":"empleados"},{"col":"Sueldo Bruto","fn":"sum","alias":"masa_salarial"}]. | |
| group_by | No | Columns to GROUP BY. Example: ["Estatus","Mes"]. | |
| filters | No | Same syntax as filter_resource.filters. Applied before grouping. | |
| having | No | Post-aggregation filter on aggregation aliases. Example: [{"col":"empleados","op":">","val":10}]. | |
| order_by | No | Same syntax as filter_resource.order_by. Refs aggregation aliases or group cols. | |
| limit | No | Max groups to return (1-1000). |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| source_url | No | |
| format | No | |
| cache | No | |
| groups_returned | No | |
| columns | No | |
| limit | No | |
| rows | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnlyHint=true and openWorldHint=true. The description adds caching behavior (first call downloads, subsequent reuse) and clarifies it returns one row per group. No contradictions, and additional context is provided.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is relatively concise and front-loaded with the main purpose. The example is lengthy but illustrative. Every sentence adds value, though the example could be shortened or placed in a separate section.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that context signals indicate an output schema exists (though not provided), the description need not explain return values. It covers caching, aggregation functions, and usage. For a tool with 8 parameters and no enums, it is sufficiently complete.
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 baseline is 3. The description adds value by providing valid functions for aggregations, specifying filter syntax reference, and including a detailed example that demonstrates parameter usage. Some details like format accept list are in schema but description elaborates.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Run' and the resource 'GROUP BY + aggregations against a cached resource'. It distinguishes itself from siblings like filter_resource and query_resource by emphasizing aggregation without SQL. The example solidifies the purpose.
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 provides an example and mentions caching behavior, but does not explicitly state when to use this tool vs alternatives like filter_resource or summarize_resource. Implicitly, it's for grouped aggregations, but explicit guidance would improve clarity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
autocompleteARead-only
Autocompleta nombres de datasets / organizaciones / grupos / tags.
Útil para resolver slugs cuando el usuario sólo da nombre parcial. Ej: kind='organization', query='hacienda' → sugiere 'ministerio-de-hacienda'.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | Tipo de entidad a autocompletar. | |
| query | Yes | Texto parcial a completar. | |
| limit | No | Sugerencias (1-30) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true. The description adds the context that the tool suggests completions, which aligns with readOnlyHint. No further behavioral traits (e.g., return format, pagination) are disclosed, but the safety profile 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise: three sentences that state the purpose, the utility case, and an example. No unnecessary words, every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple tool with 100% schema coverage and no output schema, the description is adequate but lacks any mention of the return format (e.g., list of strings). This is a minor gap that could help the agent understand the output.
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 parameters are already well documented. The description adds value with an example illustrating how kind and query work together, which enhances understanding beyond the schema 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?
The description clearly states it autocompletes names of datasets, organizations, groups, and tags. The example with kind='organization', query='hacienda' shows it resolves partial names to full slugs, distinguishing it from sibling tools like search_datasets or list_organizations.
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 explains it is useful for resolving slugs when the user provides only a partial name, with a concrete example. However, it does not explicitly state when not to use it or mention alternatives among siblings, which is a minor gap.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
clear_cacheADestructiveIdempotent
Remove all cached Parquet files. Returns the count removed.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| removed_entries | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate destructiveHint=true. Description adds that it removes Parquet files and returns the count, providing specific behavioral detail beyond annotations.
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?
One sentence, front-loaded with verb and resource, no wasted words.
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?
Fully describes the tool's action and return value; no gaps given zero parameters and simple 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?
No parameters exist, so schema coverage is 100%. The description correctly lacks parameter details as none are needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Remove' and the resource 'cached Parquet files', and distinguishes from sibling tools like get_cache_stats which reads cache status.
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 implies usage (clearing cache) but does not explicitly state when to use it or provide alternatives (e.g., checking cache stats first). No guidance on when not to use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
detect_outliers_resourceARead-only
Find rows where a numeric column falls outside the IQR fence.
Uses the standard IQR method: outliers are values below Q1 - 1.5IQR or above Q3 + 1.5IQR. Returns rows sorted by distance from the median. Useful for detecting data-entry errors in salary, budget, or census data. First call downloads + caches. Subsequent calls reuse the cache.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Direct URL to the file (CKAN resource 'url' field). | |
| format | Yes | Format declared in CKAN. Accepts: csv, tsv, xlsx, json. | |
| column | Yes | Numeric column to check. One column per call. | |
| filters | No | Same filter syntax as filter_resource. Applied before outlier detection. | |
| limit | No | Max outlier rows to return (1–500). |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| source_url | No | |
| format | No | |
| cache | No | |
| column | No | |
| method | No | |
| q1 | No | |
| q3 | No | |
| iqr | No | |
| lower_fence | No | |
| upper_fence | No | |
| outlier_count_estimate | No | |
| rows_returned | No | |
| columns | No | |
| rows | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses the IQR method, sorting by distance from median, and caching behavior (first call downloads, subsequent calls reuse). Annotations already indicate readOnlyHint and openWorldHint, and the description adds value beyond them without contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is very concise (5 lines) with clear structure: purpose, method, use case, caching. Every sentence adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity and existing output schema, the description covers caching, ordering, and use cases. It could mention handling of missing values or non-numeric columns, but overall it is informative enough.
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 baseline is 3. The description does not add significant meaning beyond the schema for parameters; it only provides context on the method and output ordering.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it finds rows where a numeric column falls outside the IQR fence, specifies the method, and gives concrete use cases (salary, budget, census data). It distinguishes from sibling tools by focusing on outlier detection via IQR.
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 provides clear context (detecting data-entry errors) and mentions caching behavior. However, it does not explicitly state when not to use this tool or provide direct comparisons to siblings like quantiles_resource or filter_resource.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
download_resource_previewARead-only
Download a resource and return N rows with their column headers.
The datos.gob.do portal has no DataStore (no SQL), so this tool downloads the file and parses it client-side. 5 MB cap to avoid huge files. Useful for inspecting the structure of the data before deciding how to query it. For analytical queries on big files, use get_resource_schema + summarize_resource (v0.2) or aggregate_resource (v0.3+).
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Direct URL to the file (CKAN resource 'url' field). Supports CSV, TSV, XLSX, JSON. | |
| format | Yes | Format declared in CKAN ('format' field). Accepts: csv, tsv, xlsx, xls, json. | |
| rows | No | Rows to return (1-200). Default 20. | |
| sample | No | Which slice to return: 'head' (first N), 'tail' (last N of downloaded portion), or 'random' (uniform sample). For large files, prefer summarize_resource or aggregate_resource. | head |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| format | No | |
| source_url | No | |
| columns | No | |
| total_rows_in_download | No | |
| rows_returned | No | |
| sample_mode | No | |
| bytes_downloaded | No | |
| download_truncated | No | |
| rows | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnlyHint=true and openWorldHint=true. Description adds the 5 MB cap and client-side parsing rationale. Minor lack of detail on error handling (e.g., if file exceeds 5 MB).
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?
Five sentences, front-loaded with core action, no fluff. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers purpose, size limit, why no SQL, usage guidance, and parameter hints. Output schema exists, so return values not needed. Minor gap: no mention of error behavior for unsupported formats or size limits.
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%, but description adds context (why client-side parsing, guidance to use alternatives for large files, format support). Adds value beyond schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it downloads a resource and returns N rows with headers, distinguishing it from siblings like aggregate_resource and summarize_resource.
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 when to use (inspecting structure before querying) and when not to (for analytical queries on big files, use alternative tools). Clearly differentiates from siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
filter_resourceARead-only
Run a typed WHERE/SELECT/ORDER BY/LIMIT against a cached resource.
First call downloads the file (up to 100 MB) and caches it as Parquet at ~/.cache/datosgobdo-mcp/. Subsequent calls hit cache (<1s). Returns requested columns + matching rows (capped at limit) plus the total count of matching rows. Use this when you need actual records, not aggregates.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Direct URL to the file (CKAN resource 'url' field). | |
| format | Yes | Format declared in CKAN. Accepts: csv, tsv, xlsx, json. | |
| filters | No | Optional list of filter conditions, AND-combined. Each item is {col, op, val}. Valid ops: =, !=, <, <=, >, >=, in, not_in, contains, starts_with, ends_with, is_null, is_not_null. Example: [{"col":"Año","op":"=","val":2026},{"col":"Mes","op":"=","val":"Abril"}]. | |
| columns | No | Columns to SELECT. None = all columns. | |
| order_by | No | List of {col, dir} where dir is "asc" or "desc". Example: [{"col":"Sueldo Bruto","dir":"desc"}]. | |
| limit | No | Max rows to return (1-1000). | |
| offset | No | Rows to skip (for paginating). |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| source_url | No | |
| format | No | |
| cache | No | |
| matching_rows_total | No | |
| rows_returned | No | |
| columns | No | |
| limit | No | |
| offset | No | |
| rows | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (readOnlyHint, openWorldHint), the description discloses caching behavior, file size limit (100 MB), cache location, expected speed, and return details (capped rows + total count). No contradictions with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise with two short paragraphs. The key purpose is front-loaded, and every sentence adds value. No fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (7 parameters, multiple options), the description covers caching behavior, size limits, return format, and use case. The output schema exists, so return details are unnecessary. The description is complete for effective use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description does not need to add parameter details, and it doesn't. The overall usage context provided is sufficient; baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool does a typed WHERE/SELECT/ORDER BY/LIMIT against a cached resource, specifying the verb and resource. It also distinguishes from siblings by stating 'Use this when you need actual records, not aggregates,' which differentiates it from aggregate_resource.
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 provides clear usage context: first call downloads and caches, subsequent calls are fast. It advises using when actual records are needed, implying not for aggregates. However, it doesn't explicitly differentiate from query_resource (a sibling), so a slight gap in when-not-to-use exists.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_duplicates_resourceARead-only
Find rows that appear more than once on the given columns (or all columns).
Returns duplicate groups sorted by frequency descending. Useful for detecting data-quality issues in payroll, census, and registry datasets. First call downloads + caches. Subsequent calls reuse the cache.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Direct URL to the file (CKAN resource 'url' field). | |
| format | Yes | Format declared in CKAN. Accepts: csv, tsv, xlsx, json. | |
| columns | No | Columns to check for duplication. None = all columns. Example: ['Nombre', 'Cedula'] checks for rows with same name and ID. | |
| filters | No | Same filter syntax as filter_resource. Applied before duplicate check. | |
| limit | No | Max duplicate groups to return (1–500). |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| source_url | No | |
| format | No | |
| cache | No | |
| columns_checked | No | |
| duplicate_groups_found | No | |
| groups_returned | No | |
| total_duplicate_rows | No | |
| columns | No | |
| rows | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true. The description adds caching behavior ('First call downloads + caches. Subsequent calls reuse the cache.') and sorting by frequency descending, which are valuable beyond annotations. No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences, front-loaded with the main purpose, then adds sorting and caching. No unnecessary words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the output schema exists, the description covers purpose, use cases, caching, and column options. Could mention result grouping format, but sorting is covered. Adequate for a tool with 5 parameters.
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 description adds limited value. It includes an example for 'columns' and cross-references 'filters' to filter_resource syntax, but baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Find rows that appear more than once' and specifies columns or all columns, with sorting by frequency and use cases (payroll, census). This distinguishes it from siblings like filter_resource or detect_outliers_resource.
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 says 'Useful for detecting data-quality issues' but does not explicitly state when to use this tool vs alternatives, nor gives exclusion criteria. The caching behavior is mentioned but not as usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_cache_statsARead-only
Return on-disk Parquet cache stats: entry count, total bytes, max bytes.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| cache_dir | No | |
| entries | No | |
| total_bytes | No | |
| max_bytes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the description's mention of returning stats adds context but no new behavioral disclosure. No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single concise sentence, front-loaded with the purpose, and every word adds value. No wasted text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no parameters and an output schema, the description fully covers what the tool does. No missing information for the agent to invoke 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?
There are no parameters, so the schema covers 100%. The description adds no parameter information, which is unnecessary. Baseline of 4 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns cache stats for the on-disk Parquet cache, specifying the three return fields: entry count, total bytes, max bytes. It is distinct from sibling tools like clear_cache.
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 implies usage for monitoring cache health but does not explicitly state when to use it versus alternatives. No exclusions or alternative tools are mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_datasetARead-only
Obtiene metadatos completos de un dataset, incluyendo todos sus recursos descargables.
Devuelve: título, descripción, organización, licencia, lista completa de recursos (archivos CSV/XLSX/PDF/etc) con URLs de descarga directa.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ID UUID o slug del dataset. Ej: 'nomina-general', '40bc3980-625b-4d7b-9ccc-304878126f8f'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the description adds value by specifying the returned fields (title, description, etc.) but does not disclose additional behaviors like pagination, rate limits, or required permissions.
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 concise sentences with no wasted words. The purpose and return values are front-loaded, making it easy for an AI agent to quickly understand 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 simple read-only tool with one parameter and no output schema, the description is complete. It adequately explains what is returned (metadata and resource URLs) and the scope (full dataset with all resources).
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% with a single 'id' parameter already well-documented. The description does not add new semantic information beyond what the schema provides, so a baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool retrieves full dataset metadata and downloadable resources, listing specific fields returned. It distinguishes from siblings like 'get_resource' (single resource) and 'search_datasets' (search).
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 implies usage for obtaining complete dataset metadata and resources. However, it does not explicitly state when not to use it or name alternative tools like 'get_resource' for individual resources, though context from siblings provides some clarity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_organizationBRead-only
Información detallada de una institución: descripción, número de datasets, URL.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ID o slug de la organización. Ej: 'ministerio-de-hacienda', 'bcrd', 'indotel'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations provide readOnlyHint and openWorldHint. The description adds that the tool returns description, number of datasets, and URL. No contradictions. However, it lacks additional behavioral details beyond these fields, such as return format or error conditions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise: one sentence that specifies the resource and key output fields. No redundant information, well front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple tool with one parameter and no output schema, the description adequately communicates the purpose and output fields. It could be slightly more complete by clarifying that it returns a single organization object, but overall sufficient.
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%; the schema already describes parameter 'id' with examples. The description does not add extra semantics to the parameter, so baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it provides detailed information about an institution, listing specific fields (description, dataset count, URL). The annotations title 'Get organization' reinforces this. It distinguishes from sibling list_organizations by focusing on a single organization, but does not explicitly say 'get a single organization'.
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 guidance on when to use this tool versus alternatives like list_organizations. It does not mention context such as needing the ID first or when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_resourceARead-only
Metadatos de un recurso (archivo) específico: URL de descarga, formato, tamaño.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | UUID del recurso. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, providing safety and unpredictability context. Description adds that it returns download URL, format, size, but no additional behavioral traits like caching, rate limits, or result format. Adequate but not rich.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence is concise and front-loaded. However, it is only in Spanish, which may limit utility for English-speaking agents. Otherwise, no wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read-only tool with one parameter and no output schema, description covers the main return values. Lacks mention of potential variability due to openWorldHint, and does not specify output format. Mostly complete given simplicity.
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 provides description for the single parameter ('UUID del recurso.'). Description does not add any new parameter semantics beyond what the schema already offers. Baseline 3 applies due to 100% schema coverage.
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?
Description clearly states it retrieves metadata of a specific resource (file), listing specific attributes (download URL, format, size). This distinguishes it from sibling tools that perform data operations (e.g., query_resource, filter_resource).
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 explicit guidance on when to use this tool versus alternatives. Sibling tool 'get_resource_schema' could be confused, but no differentiation provided. Missing when-not-to-use or prerequisite context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_resource_schemaARead-only
Return column names, inferred types, and sample values for a resource.
Cheap reconnaissance step. Downloads file (up to 100 MB), opens it in DuckDB, and runs DESCRIBE + per-column DISTINCT sampling. Does NOT return raw rows. Use this before summarize_resource or aggregate_resource so the model knows column names and types.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Direct URL to the file (CKAN resource 'url' field). | |
| format | Yes | Format declared in CKAN. Accepts: csv, tsv, xlsx, json. | |
| sample_rows | No | Distinct values per column to include as samples (1-1000). |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| source_url | No | |
| format | No | |
| cache | No | |
| row_count | No | |
| column_count | No | |
| columns | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds behavioral details beyond annotations: 'Downloads file (up to 100 MB), opens it in DuckDB, and runs DESCRIBE + per-column DISTINCT sampling. Does NOT return raw rows.' This discloses side effects and limitations. Annotations already set readOnlyHint and openWorldHint, so no contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is very concise: two short paragraphs. The first sentence states the purpose, and the rest adds essential context. Every sentence is informative with no waste.
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 description is complete for a reconnaissance tool. It explains what it does, its limitations (no raw rows, file size limit), and how it fits into a workflow. With good annotations and an output schema (implied), nothing 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% for 3 parameters. The description does not add significant new meaning beyond the schema; it mentions 'Distinct values per column to include as samples' but that is already in the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states 'Return column names, inferred types, and sample values for a resource.' It uses a specific verb ('return') and resource ('resource schema'), and distinguishes from siblings by recommending use before summarize_resource or aggregate_resource.
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 provides clear guidance: 'Cheap reconnaissance step' and 'Use this before summarize_resource or aggregate_resource'. It tells when to use (before aggregation) and implies it should not be used to return raw rows.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_site_statsARead-only
Estadísticas generales del portal datos.gob.do.
Devuelve: total de datasets, organizaciones, grupos, tags.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint=true, indicating safe read-only operation. The description adds that it returns totals of datasets, organizations, groups, and tags, which is the core behavioral output. However, there is no additional disclosure beyond the annotations (e.g., rate limits, data freshness). Score 3 is appropriate as annotations cover safety and description adds return value context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the portal name, and conveys all necessary information without any fluff. Every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the absence of parameters and output schema, the description is complete. It clearly states what the tool returns (total datasets, organizations, groups, tags). No additional information is necessary for a simple stat 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?
There are zero parameters, so schema coverage is 100% and the description need not add parameter semantics. Baseline for zero parameters is 4. The description does not need to compensate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it returns general statistics (total datasets, organizations, groups, tags) of the portal datos.gob.do. The verb 'Devuelve' (returns) and resource 'estadísticas generales' are specific. It distinguishes from siblings like get_dataset or get_organization which focus on individual entities.
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 implies usage for high-level portal statistics. It does not explicitly state when not to use or name alternatives, but the sibling list includes specific tools for datasets, organizations, etc., making the tool's context clear. A slightly higher score would require explicit guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_groupsARead-only
Categorías temáticas en datos.gob.do (economía, salud, gestión pública, etc.).
| 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?
Annotations already declare readOnlyHint=true and openWorldHint=true, indicating a safe read operation returning all groups. The description adds value by specifying the domain (datos.gob.do) and giving examples of categories, which helps the agent understand the content. No contradictions with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded sentence that conveys the tool's purpose efficiently. Every word is meaningful. There is no wasted text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no parameters, annotations covering safety and open world, and an existing output schema, the description is largely complete. It could explicitly mention that it returns a list of all available groups, but the name and openWorldHint imply this. Adequate for a simple listing 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?
There are no parameters, so the schema coverage is 100%. The description does not need to explain parameters. Baseline score of 4 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that the tool lists thematic categories from datos.gob.do, with examples. The verb 'list' is implied by the name, and the resource 'groups/categorías' is specified. This distinctly separates it from siblings like list_organizations or list_tags.
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 provides no guidance on when to use this tool versus alternatives like list_organizations or list_tags. It does not mention any prerequisites, limitations, or specific use cases.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_organizationsARead-only
Lista instituciones gubernamentales que publican en datos.gob.do.
Devuelve ministerios, organismos autónomos, municipios, etc., con conteo de datasets por institución. Sin descripciones largas.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Máximo (1-200) |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint and openWorldHint. Description adds that results include dataset counts and no long descriptions, which provides some additional context but does not significantly extend beyond annotations.
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?
Description is three sentences, front-loaded with the main purpose, and each sentence adds value. No extraneous information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simplicity of the tool (one optional parameter, output schema present), the description adequately covers what is returned. Could mention limit or pagination, but the schema handles that.
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% with the 'limit' parameter described. The description does not mention the parameter or add any additional semantics 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?
Description clearly states it lists government institutions that publish on datos.gob.do, with specific types and dataset counts. It distinguishes from sibling tools like 'get_organization' (single org) and 'list_groups' (different entity).
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 explicit guidance on when to use this tool versus alternatives like 'search_datasets' or 'get_organization'. The description only states what it returns, not the context or preferences.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_recent_datasetsARead-only
Datasets modificados más recientemente en datos.gob.do.
Útil para monitorear actualizaciones del portal gubernamental. Devuelve metadatos hidratados, no actividades crudas.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Cantidad (1-30) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes beyond the readOnlyHint annotation by specifying that the tool returns hydrated metadata, not raw activities. This gives the agent a clear idea of what the output contains, which is valuable given no 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?
The description is concise with three short sentences, each adding distinct value: purpose, use case, and behavior. No fluff or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simplicity of the tool (one optional param, no output schema), the description adequately covers its behavior and return type. It could potentially mention ordering or that it returns metadata, but it already does so minimally. Slight deduction for not confirming the output is a list of datasets.
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 schema coverage is 100% with the single 'limit' parameter fully documented in the schema (min, max, default). The description adds no additional information about the parameter, so it meets the baseline but does not exceed it.
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?
Description clearly states it lists the most recently modified datasets from datos.gob.do. It uses specific verb 'list' and resource 'datasets', and implies a temporal scope. It distinguishes from sibling tools like search_datasets or get_dataset which serve different purposes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states it is useful for monitoring updates to the government portal, giving a clear use case. However, it does not provide explicit when-not-to-use guidance or mention specific alternatives, though the context of sibling tools fills some of this gap.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_tagsARead-only
Lista etiquetas disponibles, opcionalmente filtradas por prefijo.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Prefijo para filtrar tags. | |
| limit | No | Máximo (1-100) |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint. The description adds filtering behavior but does not contradict annotations. It adequately specifies the tool's safe, open-world nature.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence, no waste, front-loaded with verb and resource. Perfectly concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple listing tool with full schema and output schema, the description is complete. Minor gap: no mention of default limit or maximum, but these are in 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 the description adds no new meaning beyond what's already in parameter descriptions. Baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists available tags with optional prefix filtering, directly matching the name and distinguishing it from sibling tools which are not tag-related.
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 implies use when needing to list tags, but does not provide explicit when-not-to-use or alternatives. However, the context of sibling tools doesn't demand exclusion guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
quantiles_resourceARead-only
Percentile distribution (p25/p50/p75/p90/p95/p99) of numeric columns.
Fills the gap left by aggregate_resource, which only exposes median. First call downloads + caches the file. Subsequent calls reuse the cache. Useful for salary analysis, budget distributions, and statistical profiling.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Direct URL to the file (CKAN resource 'url' field). | |
| format | Yes | Format declared in CKAN. Accepts: csv, tsv, xlsx, json. | |
| columns | No | Numeric columns to analyze. None = all numeric columns. | |
| percentiles | No | Percentiles to compute (0–1 exclusive). Default: [0.25, 0.5, 0.75, 0.90, 0.95, 0.99]. | |
| filters | No | Same filter syntax as filter_resource. Applied before computing. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| source_url | No | |
| format | No | |
| cache | No | |
| row_count | No | |
| percentiles | No | |
| columns | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint: true and openWorldHint: true. The description adds caching behavior (first call downloads and caches, subsequent calls reuse cache), which is useful beyond annotations. No contradictions.
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 sentences: purpose, caching behavior, and use cases. No redundant information. Front-loaded with the primary function.
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 description, combined with schema and annotations, provides sufficient context. The caching behavior and sibling comparison are included. Could mention that columns must be numeric, but schema already enforces that.
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 baseline is 3. The description does not add new parameter details beyond what the schema already provides. It mentions default percentiles, but that is also in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it computes percentile distributions of numeric columns and distinguishes itself from the sibling aggregate_resource which only provides median. The verb 'computes' and resource 'numeric column quantiles' are specific.
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 explains it fills the gap left by aggregate_resource, indicating when to use it over that sibling. It also provides example use cases (salary analysis, budget distributions, statistical profiling). However, it lacks explicit 'when not to use' guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_resourceARead-only
Run an ad-hoc read-only SQL query against a cached resource via DuckDB.
Power-user escape hatch when filter_resource / aggregate_resource don't cover the case. The cached resource is exposed as the in-memory table 'data'. SQL is DuckDB dialect — see https://duckdb.org/docs/sql/introduction. Supports CSV, TSV, XLSX, XLS, JSON, and ODS (auto-converted to CSV).
Safety:
Only SELECT/WITH statements (CTEs allowed); multi-statement blocked.
DDL/DML keywords (INSERT/UPDATE/DELETE/DROP/CREATE/ALTER/COPY/EXPORT/ IMPORT/TRUNCATE/GRANT/REVOKE/PRAGMA/SET/LOAD/INSTALL/ATTACH/DETACH/ VACUUM/ANALYZE) rejected outright.
Sandboxed: the resource is materialized in memory and external access is disabled, so table functions (read_text/read_csv/glob/...) cannot read local files or reach the network.
Row cap always applied via outer wrapper.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Direct URL to the file (CKAN resource 'url' field). | |
| format | Yes | Format declared in CKAN. Accepts: csv, tsv, xlsx, xls, json, ods. | |
| sql | Yes | Read-only SQL query against table 'data'. Only SELECT/WITH allowed; DDL/DML rejected. The query is wrapped in 'SELECT * FROM (<your sql>) LIMIT <limit>' so a row cap is always enforced. Example: "SELECT Estatus, COUNT(*) c FROM data WHERE Año=2026 AND Mes='Abril' GROUP BY Estatus ORDER BY c DESC" | |
| limit | No | Hard cap on returned rows (1-1000). |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| source_url | No | |
| format | No | |
| cache | No | |
| sql_executed | No | |
| rows_returned | No | |
| columns | No | |
| rows | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond annotations by detailing SQL dialect (DuckDB), sandboxing, file format support, and the fact that the query is wrapped with a LIMIT, providing comprehensive behavioral context.
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?
Well-structured with sections for purpose, dialect link, supported formats, and safety bullet points. Slightly lengthy but every sentence is informative; front-loaded with purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the presence of an output schema, the description adequately covers purpose, usage, safety, and edge cases, making it fully informative for an agent.
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% with good per-parameter descriptions. The description adds value by clarifying the underlying table name 'data' and the wrapping behavior of the SQL query, though not essential.
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?
Clearly states it runs an ad-hoc read-only SQL query against a cached resource via DuckDB, distinguishing itself from filter_resource and aggregate_resource as a power-user escape hatch.
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 tells when to use ('when filter_resource/aggregate_resource don't cover the case') and provides safety constraints like only SELECT/WITH and row cap, guiding appropriate usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_query_to_csvA
Write a query or filter result to a local CSV file.
Export endpoint for analysis workflows — run your filter or SQL, then save the result to open in Excel or another tool. Returns the file path and row count. First call downloads + caches the source file. Subsequent calls reuse the cache.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Direct URL to the file (CKAN resource 'url' field). | |
| format | Yes | Format declared in CKAN. Accepts: csv, tsv, xlsx, json. | |
| dest | No | Absolute path for the output file (.csv or .tsv). If None, saves to ~/Downloads/datosgobdo-exports/<slug>-<timestamp>.csv. Must not contain '..'. Cannot write to system paths (/etc, /usr, /bin, ...). | |
| sql | No | Read-only SQL query against table 'data' (same rules as query_resource). If provided, takes precedence over filters/columns. | |
| filters | No | Same filter syntax as filter_resource. Used if sql is None. | |
| columns | No | Columns to include. None = all. Ignored if sql is provided. | |
| limit | No | Max rows to write (1–100000). Default 10000. | |
| overwrite | No | Overwrite dest if it already exists. Default False. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| path | No | |
| rows_written | No | |
| columns | No | |
| bytes_written | No | |
| cache | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations provide readOnlyHint=false (modifies state) and openWorldHint=true (file system side effects). Description adds key behavioral details: caching behavior (first call downloads+caches, subsequent reuse) and return value (file path, row count). No contradiction.
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 sentences front-load the purpose, add usage context, and disclose caching behavior. No fluff, every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers main purpose, caching, return values, and usage context. Missing explicit parameter precedence (e.g., sql vs filters), but schema descriptions handle this. Output schema exists, so return value detail is not required. Adequate for complexity.
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 baseline is 3. The description does not add new parameter-level meaning beyond summarizing the tool's purpose. All parameter details are well-covered in 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?
Description clearly states the verb 'write' and resource 'query or filter result to local CSV file'. It distinguishes from siblings like 'filter_resource' and 'query_resource' by specifying export to file for analysis workflows.
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?
Describes when to use (after running filter or SQL) and context (export for Excel/other tools). Does not explicitly list alternatives or exclusions, but context from sibling tools implies differentiation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_datasetsARead-only
Busca datasets en datos.gob.do (datos abiertos de República Dominicana).
Filtra por palabra clave, organización, tag o grupo temático. Devuelve metadatos resumidos: título, organización, formatos disponibles, URL.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Término de búsqueda en texto libre. Ej: 'presupuesto', 'salud pública', 'educación'. Omitir para listar todos. | |
| organization | No | Slug de la institución gubernamental. Ej: 'ministerio-de-salud-publica', 'bcrd', 'digepres'. Usar 'autocomplete' con kind='organization' si no estás seguro del slug. | |
| tag | No | Etiqueta temática. Ej: 'finanzas', 'poblacion'. | |
| group | No | Grupo o categoría. Ej: 'economia', 'salud'. | |
| limit | No | Resultados (1-50) | |
| offset | No | Offset para paginación |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds context that the tool returns summarized metadata (title, organization, formats, URL), which goes beyond the annotation 'readOnlyHint'. However, it does not disclose pagination behavior, potential rate limits, or the extent of data freshness. The annotation already indicates a safe read operation, so the description provides moderate additional transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two concise sentences in Spanish, efficiently front-loaded with the primary action and resource in the first sentence. Every word serves a purpose with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has six parameters (none required), no output schema, and annotations indicating read-only and open-world behavior, the description adequately covers the source, filters, and return fields. It could mention pagination (handled via limit/offset) but is otherwise sufficient for a search 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?
With 100% schema coverage, the schema already documents all six parameters with clear descriptions and examples. The main description only reiterates the filter dimensions (keyword, organization, tag, group) without adding new semantic information, so it meets the baseline of 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Busca datasets' and the resource 'datos.gob.do', with specific filter dimensions (keyword, organization, tag, group). It distinguishes from siblings like 'search_resources' by focusing on datasets and summary metadata.
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 implies usage for searching public datasets in the Dominican Republic open data portal, but does not explicitly state when to use this tool versus alternatives like 'search_resources' or 'list_recent_datasets'. No exclusions or prerequisites are mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_resourcesARead-only
Busca recursos (archivos individuales) por nombre. Devuelve URLs de descarga.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Nombre o parte del nombre del recurso. | |
| limit | No | Resultados (1-50) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint and openWorldHint. Description adds that it returns download URLs, which is behavioral context. No contradictions.
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, front-loaded with purpose and output. No unnecessary words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given full parameter schema coverage and annotations, description sufficiently covers main behavior. Could mention scope of search (all resources or within context), but overall adequate.
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 description does not add meaning beyond what schema already provides. Baseline 3.
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?
Description clearly states it searches for resources (individual files) by name and returns download URLs. Distinguishes from sibling tools like search_datasets and get_resource.
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?
Implied usage from description (search files by name), but no explicit when to use vs alternatives or when-not conditions. Could reference sibling tools for differentiation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
summarize_resourceARead-only
Auto-generated profile: row count, types, nulls, distinct, min/max/mean, top values.
Downloads file (up to 100 MB), runs DuckDB COUNT/DISTINCT/AGG queries per column. Returns one compact dict per column with stats. The model uses this to decide which filters and aggregations to apply next, without any raw rows in its context. For columns with many distinct values (e.g. names), 'top_values' is omitted; only counts are returned.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Direct URL to the file (CKAN resource 'url' field). | |
| format | Yes | Format declared in CKAN. Accepts: csv, tsv, xlsx, json. | |
| max_categorical_top_n | No | Top-N most-frequent values per categorical column (1-50). |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| hint | No | |
| source_url | No | |
| format | No | |
| cache | No | |
| row_count | No | |
| column_count | No | |
| columns | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnlyHint, openWorldHint), the description adds specific behavioral traits: downloads file up to 100 MB, runs DuckDB COUNT/DISTINCT/AGG queries per column, returns one compact dict per column, and omits top_values for columns with many distinct values. This transparency helps the agent understand constraints and output characteristics.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise with four sentences. The first sentence front-loads the core purpose. Each subsequent sentence adds necessary detail (process, return format, usage context, omission behavior) without redundancy. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (3 parameters, output schema present), the description adequately covers the primary behavior, constraints (file size limit, omission policy), and context (use for decision-making). It does not detail the output schema (unnecessary since it exists separately) or mention format-specific handling, but overall it provides sufficient completeness for an AI agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides descriptions for all three parameters (100% coverage). The description adds some context about the behavior of max_categorical_top_n (implicitly via top_values omission) but does not significantly enhance understanding of url or format beyond the schema. Per calibration, baseline 3 is appropriate when schema coverage is high.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Auto-generated profile: row count, types, nulls, distinct, min/max/mean, top values.' It also explains the process (downloads file, runs DuckDB queries) and the output format (compact dict per column). This distinguishes it from sibling tools like aggregate_resource or filter_resource, which perform different operations.
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 mentions that 'The model uses this to decide which filters and aggregations to apply next,' implying it's a preliminary analysis step. However, it does not explicitly state when to use this tool versus alternatives like aggregate_resource or query_resource, nor does it provide when-not-to-use guidance. The context is helpful but lacks explicit directives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
TDQS
Each tool has a clearly distinct purpose: resource analysis tools (aggregate, filter, summarize, etc.) are differentiated by operation type, metadata tools cover different entities, and auxiliary tools (cache, autocomplete, export) are non-overlapping.
All tool names follow a consistent verb_noun pattern with underscores (e.g., aggregate_resource, get_dataset, list_organizations), making it easy to predict functionality.
With 23 tools covering search, metadata retrieval, data analysis, caching, and export, the count is well-suited for a government open data portal without being excessive or insufficient.
The tool set covers the full lifecycle for a read-only data portal: discovery (search, list, autocomplete), inspection (schema, preview, metadata), analysis (filter, aggregate, SQL, outliers, quantiles), and export (CSV), with no obvious gaps.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
The Mercado Pago MCP Server implements the Model Context Protocol to provide AI agents and LLMs with access to Mercado Pago's APIs and tools within compatible development environments. It acts as an intermediary that translates Mercado Pago resources into executable functions (tools) that AI applications can invoke to perform actions and automate flows. The server simplifies integration, enables using documentation to implement or improve code, and optimizes operations through natural language interactions without manual implementations.
Model Context Protocol server for the Apideck Unified API. Connect any MCP-compatible agent framework to 100+ accounting systems, HRIS platforms, file storage providers, and more through one integration. More information https://www.apideck.com/mcp-server
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
A Model Context Protocol server for Wix AI tools
Related MCP Servers
- AlicenseAqualityDmaintenanceA Model Context Protocol server that connects AI assistants to Brazilian public data services, providing access to postal codes, company registrations, bank information, area codes, IBGE data, currency exchange rates, and domain registration status.11134MIT
- FlicenseDqualityNot gradedmaintenanceA Model Context Protocol server implementation that provides structured, AI-friendly access to eRegulations data, making it easier for AI models to answer user questions about administrative procedures.419
- AlicenseNot gradedqualityDmaintenanceModel Context Protocol server that standardizes tool discovery, execution, and context management for AI applications.MIT
- FlicenseNot gradedqualityCmaintenanceA lightweight and extensible Model Context Protocol server built with Node.js, exposing custom tools and resources for AI clients.88
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/alcastaro/datos.gob.do-MCP-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server