- Fix (scoring v2):
scoreDatasetRelevancenow readsholder_nameandpublisher_namefrom CKAN datasetextras[]first (authoritative DCAT-AP_IT source), with fallback to root-level fields for non-DCAT-AP_IT catalogs - Rationale: on dati.gov.it (ckanext-dcatapit),
package_searchreturns DCAT-AP_IT fields in two locations that often disagree. Insideextras[]the values reflect the truedct:rightsHolder/dct:publisherset by the publisher; at root level the same field names are populated by the harvester with the harvesting organization's name, NOT the data owner - Concrete example: dataset
defibrillatori-esternion dati.gov.it has rootholder_name = "GAL Terra dei Messapi"(the harvester) andextras.holder_name = "Comune di Mesagne"(the actual owner per DCAT-AP_IT). Without this fix, v1 of the scoring change still producedholder = 0on queries mentioning the real owner - The v1 fix (peer commit) introduced the holder/publisher fields but read them from the root level only; this commit completes the fix by sourcing them from the correct DCAT-AP_IT location
- Added helper
readDcatExtra(dataset, key)to encapsulate the lookup logic - Validated against real
package_searchpayloads onhttps://dati.gov.it/opendata: Mesagne score 6→12, Lecce/Cardioprotetto score 10→13
- Fix (scoring):
ckan_find_relevant_datasetsnow scoresholder_name(DCAT-AP_ITdct:rightsHolder) andpublisher_name(dct:publisher) as distinct weighted fields, separate fromorganization - Rationale: on federated catalogs (e.g.
dati.gov.it),organizationis the harvesting catalog (e.g.regione-puglia), NOT the data owner. Queries like "datasets from Comune di Lecce" previously scored 0 on the owner field when the dataset was harvested via Regione Puglia or a GAL, missing the actualrightsHolder - Bug surfaced on real-world Puglia datasets:
defibrillatori-esterni(holder: Comune di Mesagne, organization: GAL Terra dei Messapi) anddefibrillatori-dae-progetto-comune-cardioprotetto(holder: Comune di Lecce, organization: Regione Puglia) - Defaults:
holder=4(peer withtitle— actual institutional owner per DCAT-AP_IT),publisher=2(lower because sometimes a technical role like "Redazione OD" rather than the institution) - API:
weightsobject accepts two new optional fields (holder,publisher); backward-compatible — clients not setting them get the improved scoring by default - Types: added
holder_name?: stringandpublisher_name?: stringtoCkanPackageinterface (previously accessed via index signature) - Score breakdown markdown and JSON outputs include
holderandpublisherper dataset
- Security: add optional domain allowlist (
CKAN_ALLOWED_DOMAINSenv var) invalidateServerUrl(); blocks requests to unlisted public domains — CERT-AgID MCP recommendation - Security: add structured audit logging to stderr for all
makeCkanRequestcalls in Node modes (stdio + http); fields:ts,server,action,cache_hit, query params — CERT-AgID MCP recommendation - Tests: 8 new tests (4 allowlist, 3 audit log) → 383 total (377 pass, 6 skipped)
- Add
cache_hitfield to worker telemetry log entries getLastCacheHit()exported fromsrc/utils/http.ts;worker.tslogs after response withcache_hit: true/false- Enables cache hit rate analysis in
worker_events_flat.jsonl
- Add per-portal upstream rate limiter (
src/utils/rate-limiter.ts) - Token bucket algorithm, one independent bucket per hostname
- Integration in
makeCkanRequest: cache hit bypasses limiter; only upstream fetches consume tokens - Config:
CKAN_RATE_LIMIT_ENABLED,CKAN_RATE_LIMIT_RPS(default 5),CKAN_RATE_LIMIT_BURST(default 10),CKAN_RATE_LIMIT_MAX_WAIT_MS(default 5000) - Per-call bypass via
opts.rateLimit: false; disabled by default in Vitest runs RateLimitErrorthrown when wait exceedsmaxWaitMs(includes hostname + wait ms)- 13 new unit tests; suite now 370 tests, all green
- OpenSpec:
add-upstream-rate-limiter
- Add read-through HTTP cache layer in
makeCkanRequest(src/utils/cache.ts) - Backends: Cloudflare Cache API on Workers, bounded in-memory LRU on Node
- Action-based TTL: metadata 300s, datastore 60s, status 3600s; fallback configurable
- Env vars:
CKAN_CACHE_ENABLED,CKAN_CACHE_TTL_DEFAULT,CKAN_CACHE_MAX_ENTRIES,CKAN_CACHE_MAX_ENTRY_BYTES - Per-call bypass via
makeCkanRequest(..., { cache: false }); errors and oversize payloads never cached - 29 new unit tests; suite now 357 tests, all green
- Measured locally: 348 ms cache miss → 13 ms cache hit on identical
package_searchcalls (~27×) - OpenSpec:
add-http-cache-layer(proposal + design + spec)
- Fix
resolveSearchQuery:*:*and fielded queries (e.g.title:X) are no longer wrapped intext:(...)when auto-detected parser override is "text" —escapeSolrQuerywas turning*:*into\*\:\*, returning 0 results on portals like dati.comune.messina.it
- Fix
ckan_package_search: quoted phrases (e.g."aree protette") now work insidetext:(...)wrapping — removed"fromescapeSolrQueryspecial chars so phrase queries are preserved
- Improve tool parameter descriptions for Code Mode compatibility (group, tag, package tools)
- Add
.describe()to all parameters inckan_group_list,ckan_group_show,ckan_group_search - Add
.describe()to all parameters inckan_tag_list - Improve
ckan_package_showparameter descriptions (server_url example, id UUID/slug hint)
- Add
dati.regione.sicilia.it(Sicily open data portal) toportals.jsonwithforce_text_field: false— CKAN 2.6.8 does not support thetext:(...)query wrapper
- Show
Portal Locale(locale_default) inckan_status_showmarkdown output - Add query language hint to
ckan_package_searchdescription: check locale before searching - Update skill query construction rule: dynamic locale check via
ckan_status_showinstead of hardcoded portal table
- Add
data.gov.ua(Ukraine open data portal) toportals.jsonwith explicitforce_text_field: falseto prevent auto-detection from incorrectly wrapping Solr queries intext:(...), which caused 0 results
- fix(worker): return 405 immediately for GET /mcp — prevents bot scanners from hanging the Worker and consuming CPU quota
- fix(http): create server + transport per request — SDK 1.26.0 security fix enforces stateless transport cannot be reused across requests
- fix(worker): create server + transport per request — SDK 1.27.1 enforces stateless transport cannot be reused across requests
- chore(deps): update
@modelcontextprotocol/sdk1.25.2 → 1.27.1 — fixes Claude Desktop crash with MCP protocol2025-11-25
- fix(
tools/organization.ts):ckan_organization_searchlowercases pattern before Solr query — fixes case-sensitive search (e.g. "Roma" → no results)
-
fix(
tools/organization.ts):ckan_organization_searchnow showsview_urlin markdown table and JSON output;ckan_organization_showJSON includesview_url— all usingportals.jsoncustom patterns -
evals(
evals/tool-selection): tool selection eval pipeline — synthetic NL query generation (Gemini, 1583 records), train/eval split, fine-tuning Qwen2.5-0.5B with Unsloth on Colab T4; 86.3% accuracy on 8 tools; model published at huggingface.co/aborruso/ckan-tool-selector
- fix(
tools/sparql.ts): add; charset=utf-8to POST Content-Type — fixes accented chars corruption in SPARQL queries (issue #22)
- docs(
tools/datastore.ts): add security note tockan_datastore_search_sql— clarifies SQL forwarding boundary; bump v0.4.86 - security(
tools/sparql.ts): applyvalidateServerUrl()tosparql_query— blocks SSRF via private IPs (gap from GHSA-3xm7-qw7j-qc8v); 1 new test
- security(
utils/http.ts): addvalidateServerUrl()— blocks SSRF via private/loopback IPs, link-local, and non-HTTP/S protocols; 15 new tests
- fix(
worker.ts): add OPTIONS preflight handler at/mcp— was returning 405, blocking CORS-based connectors (e.g. Dify)
- chore(
package.json,server.json): fix mcpName toio.github.aborruso/ckan-mcp-server(ondata org not authorized); bump to v0.4.83 - chore(
package.json,server.json): addmcpNamefor MCP Registry publication (io.github.ondata/ckan-mcp-server); bump to v0.4.82
- fix(
worker.ts+worker_telemetry_flatten.py):ckan_find_portalsnow logscountry,language,has_datastore,min_datasets— previously alwaysnullinworker_events_flat.jsonl - fix(
worker.ts):sparql_querynow logs correctserverfield — was always""because logging code readserver_urlbut SPARQL tool usesendpoint_url; fix:a['server_url'] ?? a['endpoint_url'] ?? ''
- fix(skill): bilingual query rule made conditional — use native language only on monolingual portals (dati.gov.it → IT, data.gov → EN); bilingual only on multilingual portals (data.europa.eu, open.canada.ca)
- fix(skill): geographic qualifiers (city/region/country) must never be OR-joined with topic terms — always use
fqor AND; OR-joining a place name inflates results with off-topic datasets
- feat(
package.ts): auto-detect Solr parser mode for unknown portals via probe querydata OR dati(2 parallel rows=0 calls); if text count > default count × 2 → use text:(...) wrapping; result cached per portal URL for session lifetime - feat(
portal-config.ts): addisPortalSearchExplicitlyConfigured()to distinguish known vs unknown portals - fix(
package.ts): auto-convertNOWdate math to ISO dates forissuedandmodifiedfields inqandfq— these are CKAN extra fields not native Solr fields, soNOWsyntax returned 0 results on all portals tested (dati.gov.it, catalog.data.gov); ISO dates work universally - fix(
package.ts): fixcontent_recentclause to use ISO date forissuedfield (same root cause) - docs(
tools.md): clarify date math limitation —NOWonly works onmetadata_modified/metadata_created;issued/modifiedrequire explicit ISO dates (now auto-converted by server)
- fix(
http.ts): removeReferer,Sec-Fetch-*,Upgrade-Insecure-Requestsfrom axios headers — these triggered WAF block on BA Data (data.buenosaires.gob.ar) and other portals with strict WAF rules; dati.gov.it unaffected
- fix(
ckan_find_portals): deduplicate portals by hostname, preferring https over http - feat: new tool
ckan_find_portals— discovers CKAN portals from datashades.info registry (~950 portals); filters by country, keyword, min_datasets, language, has_datastore; LLM translates country to English
- fix: use
z.coerce.number()for all numeric tool parameters — fixes validation errors when MCP clients pass numbers as strings (closes #16)
- feat:
package_shownow includesapi_json_urlfor dataset and each resource (direct CKAN API JSON link) - fix:
api_json_urluses portal-specific API path viagetPortalApiPathinstead of hardcoded/api/3/action
- fix:
package_showJSON now includeshvd_category,applicable_legislation,frequency,language,publisher_name,holder_name - fix:
fqparameter docs warn about correct Solr OR syntax andextras_prefix for CKAN extras fields
- feat: new MCP prompt
ckan-search-hvd— guided HVD search usinghvd.category_fieldfromportals.json; fallback to keyword search for unconfigured portals
- feat:
ckan_status_shownow shows HVD dataset count when portal hashvd.category_fieldconfigured inportals.json
- feat:
ckan_package_searchandckan_package_showJSON output now includeview_urlfield pointing to the dataset page on the source portal
- feat: SPARQL endpoint config in
portals.json—sparql.endpoint_url+sparql.methodper portal (Italy:lod.dati.gov.it/sparql, GET-only) - feat:
ckan_status_shownow shows SPARQL endpoint when configured for the portal - fix:
sparql_query— GET fallback on 403/405 for endpoints that reject POST; User-Agent set toMozilla/5.0 (compatible; CKAN-MCP-Server/1.0)(required by AWS WAF on lod.dati.gov.it) - refactor:
portal-config.ts— addedSparqlConfigtype,getSparqlConfig(endpointUrl),getPortalSparqlConfig(serverUrl); tool count 18→19
- improve:
sparql_query— validate SELECT-only, auto-inject LIMIT (default 25, max 1000), truncate output at CHARACTER_LIMIT; +11 tests (310 total) - remove:
europa_dataset_searchtool and related files (src/tools/europa.ts,src/utils/europa-http.ts, Europa types insrc/types.ts, docs, tests); tool count 19→18
- feat: add
sparql_querytool — execute SPARQL SELECT against any public HTTPS endpoint (e.g. data.europa.eu/sparql, DBpedia, Wikidata) - feat(europa): update
europa_dataset_searchdescription to suggestsparql_queryfor publisher aggregations and queries not exposed as facets - test: add 12 tests for
sparql_query(querySparqlEndpoint, formatSparqlMarkdown, formatSparqlJson)
- feat(europa): add
publishertoALLOWED_FACETSineuropa_dataset_search— now shows top publishers per query
- fix(europa):
q=*now correctly returns all 1.7M+ datasets — Europa API ignoresqwhen omitted; sendingq=*was causing Elasticsearch to return only ~6k results; match-all queries (*,*:*) now omit theqparameter
- feat: expose Europa API facets in
europa_dataset_search— country, format, categories, and 15 more facet types now rendered as tables (markdown) and compact objects (JSON, top 15 items per facet) - feat: add
is_hvdboolean filter toeuropa_dataset_search— search only among High Value Datasets - refactor: filter Europa facets to 8 useful ones (country, categories, format, is_hvd, scoring, language, subject, hvdCategory), resolve multilingual titles to requested lang, JSON output 52KB→12KB
- feat: compact JSON output for heavy tools —
package_search,package_show,organization_list/show,group_list/show,datastore_search/search_sqlnow return only essential fields in JSON mode (~70% token reduction) - feat:
truncateJson()— JSON-safe truncation that shrinks arrays instead of cutting mid-string, always produces valid JSON - fix: filter
_idfield from datastore JSON output (already done in markdown) - docs: add
docs/JSON-OUTPUT.md— complete field schema for all tools in JSON mode - feat: add
europa_dataset_searchtool for European Data Portal (data.europa.eu) — searches 1.7M+ datasets across all EU countries with country filter, multilingual support, and HVD badge - new files:
src/utils/europa-http.ts,src/tools/europa.ts,tests/integration/europa.test.ts - types: add
EuropaDataset,EuropaDistribution,EuropaMultilingualField,EuropaLabelledValueinterfaces - JSON output filters multilingual fields to requested language only (compact response)
- fix(http): add AbortController 30s timeout to Workers fetch — prevents hang when CKAN server is slow (root cause of Worker timeout errors)
- packaging: add DXT one-click install support —
manifest.json+npm run pack:dxtscript producesckan-mcp-server.dxtfor Claude Desktop - docs: add "One-click install" section in README Claude Desktop section
- release workflow: DXT artifact now uploaded to GitHub releases
- tools: add HVD note on synthesis queries — when
q=*:*+ org/tag facets (orrows=0) on dati.gov.it, auto-fetch real-time HVD count and append EU Reg. 2023/138 note to markdown output - portals: add
hvd.category_fieldconfig to dati.gov.it inportals.json - utils: export
getPortalHvdConfig()fromportal-config.ts
- worker: enable Cloudflare Workers Logs (
[observability]inwrangler.toml) - worker: log structured JSON for every tool call (tool, server, q, fq, id, sql, etc.)
- portals: add
data.stadt-zuerich.ch(City of Zurich) with customorganization_view_urlpointing to CKAN backend - docs: add
data.stadt-zuerich.chto verified portals table insrc/README.md - docs: add
data.opentransportdata.swissto known issues (API on separate domain, requires API key) - tools: add "no data found" note to all no-results responses to discourage LLM hallucination
- docs: add LLM hallucination note to README Troubleshooting section
- docs: rewrite README intro — plain-language description, two-path table (local vs hosted), audience statement
- docs: add MIT license badge to README badge row
- docs: add "Use it in your favorite tool" section (ChatGPT, Claude Code, Claude Desktop, Gemini CLI, VS Code)
- docs: backup of previous README saved as README.bak.md
- feat: new tool
ckan_analyze_datasets— search + DataStore schema introspection in one call; includesinfo.label/info.notesfrom DataStore Dictionary when available - feat: new tool
ckan_catalog_stats— portal overview (total datasets, categories, formats, organizations) via single faceted query - refactor:
quality.tstools migrated from deprecatedserver.tool()toregisterTool()with full annotations - tests: 287 passing (+15 new tests)
- fix: HTTP transport —
/.well-known/oauth-authorization-servernow returns JSON 404 instead of HTML; fixes Claude Code HTTP transport connection failure - fix:
ckan_datastore_search—limitmin changed 1→0; allows column discovery without fetching data - docs:
ckan_datastore_searchdescription updated — fields always returned,limit=0pattern documented
- refactor: domain types for all tool files —
CkanTag,CkanResource,CkanPackage,CkanOrganization,CkanField,CkanDatastoreResultinsrc/types.ts;anyreduced 32 → 1 - refactor: extract rendering functions from handler closures → named exports in
datastore.ts,organization.ts,group.ts,status.ts; +26 unit tests - fix: datastore table — skip
_idcolumn, increase cell truncation 50 → 80 chars - fix: org/group show — dataset heading now shows
showing M of N returned — T total - tests: 191 → 272; all passing
- fix: datastore table — skip
_idcolumn, increase cell truncation 50→80 chars - fix: org/group show —
## Datasets (N)→## Datasets (showing M of N returned — T total) - tests: 270 → 272; all passing
- refactor: extract markdown rendering from handler closures into exported functions in
datastore.ts,organization.ts,group.ts,status.ts - add 26 unit tests across 4 new test files (
datastore-formatting,organization-formatting,group-formatting,status-formatting) - test count: 244 → 270; all passing
- refactor: add CKAN domain types (
CkanTag,CkanResource,CkanPackage,CkanOrganization,CkanField,CkanDatastoreResult) tosrc/types.ts - replace
anyin exported tool functions acrosspackage.ts,datastore.ts,organization.ts,group.ts,quality.ts,tag.ts— 32 → 1 remaining (internal handler variable) - no behavioral change; 244 tests passing
ckan_list_resources: addformat_filterparam (case-insensitive, client-side) — e.g. 72 resources → 8 CSV; header shows "Total: 72 (showing 8 CSV)"ckan_package_search: OR tip on zero results — when a plain multi-term query returns 0, suggest the OR version (e.g."a b c"→"a OR b OR c")ckan_package_search: accent fallback — if query returns 0 results and contains accented chars, retry with accent-stripped query; note shown in outputckan_package_show: always show DataStore status per resource✅ Available/❌ Not available/❓ Not reported by portal- Previously silent when field absent (e.g. dati.gov.it); now explicit
- Disable DataStore Table UI component (MCP Apps) pending use-case design
- Reverted
registerAppTool→server.registerToolin datastore.ts and package.ts - Removed
_meta.ui,structuredContentfrom tool responses - Commented out UI resource registration in resources/index.ts
- Skipped UI test suite (6 tests)
- All source files preserved (
src/ui/,src/resources/datastore-table-ui.ts) - Tests: 227 passed, 6 skipped
- Reverted
- Add workflow guidance ("Typical workflow: ...") to all 15 tool docstrings
- Inspired by datagouv-mcp pattern; steers LLMs toward correct multi-step usage
- Add
ckan_list_resourcestool (16th tool)- Compact table of resources: name, format, size, DataStore flag, resource ID
- Helps LLMs assess available files before deciding how to access data
- Tested against dati.gov.it and dati.comune.messina.it (DataStore active)
- Update
docs/future-ideas.mdwith datagouv-mcp analysis - Tests: 233 (was 228)
- DataStore Table UI: add hyperlinks on dataset titles, open in new tab
- DataStore Table UI: increase spacing (body padding, header/controls gap, th/td padding)
- Fix Workers: use
Accept-Encoding: identityin fetch branch to avoid gzip decompression failures- Cloudflare Workers
DecompressionStreamwas silently failing on gzip responses Accept-Encoding: identityprevents server from sending compressed responses- Eliminates the binary garbage output on all CKAN API calls from Workers
- Cloudflare Workers
- Fix MCP Apps: add timeout fallback to SEP-1865 handshake
- If host doesn't respond to
ui/initializewithin 1.5s, sendinitializedanyway - Handles hosts with partial spec support without blocking the widget forever
- Fixed in both
src/ui/datastore-table.htmland inlined HTML insrc/resources/datastore-table-ui.ts
- If host doesn't respond to
- Fix MCP Apps: implement mandatory SEP-1865 handshake in DataStore Table Viewer HTML
- Widget now sends
ui/initializerequest to host on load - After host response, sends
ui/notifications/initialized - Without this handshake, compliant hosts (MCPJam, Goose, etc.) never send
tool-result - Fixed in both
src/ui/datastore-table.htmland inlined HTML insrc/resources/datastore-table-ui.ts - Confirmed working in MCPJam (interactive table renders correctly)
- Widget now sends
-
Fix MCP Apps message listener to use correct JSON-RPC method per ext-apps spec:
ui/notifications/tool-result(wasui/toolResult) with data inmsg.params.structuredContent- Fixed in both
src/ui/datastore-table.htmland inlined HTML insrc/resources/datastore-table-ui.ts - Kept fallbacks for older/alternative message shapes
-
Audit and improve Zod parameter descriptions across all tools for "code mode" SDK compatibility:
ckan_organization_list/show/search: added.describe()on all parameters (previously none)ckan_datastore_search: added.describe()on all parameters (previously none)ckan_datastore_search_sql: added.describe()onserver_urlandsql(with double-quote hint)ckan_package_search: improvedfqdescription (was missing)ckan_find_relevant_datasets: improved tool description with "when to use vs ckan_package_search", improvedqueryandweightsdescriptions
- Fix MCP Apps implementation (was broken in v0.4.40):
_meta.ui.resourceUrimoved to tool DEFINITION (not result) usingregisterAppToolfrom@modelcontextprotocol/ext-apps/server- URI scheme changed from
ckan-ui://toui://ckan/(required by spec) - Resource now uses
RESOURCE_MIME_TYPEfrom ext-apps (required by clients) - Resource registered via
registerAppResourceinstead ofserver.registerResource - Data passed to UI via
structuredContentin tool result; HTML listener updated to handleui/toolResultJSON-RPC format - Table viewer extended to
ckan_package_search: datasets flattened to title/organization/formats/num_resources/modified/license columns - Added
@modelcontextprotocol/ext-appsas runtime dependency
⚠️ MCP Apps not yet supported by Claude.ai/Claude Desktop clients:_meta.ui.resourceUriis silently ignored — the interactive table viewer never appears. The server-side implementation is correct and ready; waiting for client support. Feature is effectively dormant until Anthropic ships MCP Apps in public clients.- Add DataStore Table Viewer (MCP Apps interactive UI)
- New MCP resource
ckan-ui://datastore-tableserves self-contained HTML table viewer ckan_datastore_searchnow returns_meta.ui.resourceUri+_meta.ui.datafor MCP Apps clients- Interactive features: sortable columns (numeric/date/string type-aware), text filter, pagination (10/25/50/100 rows/page)
- Works in Node.js and Cloudflare Workers (HTML inlined in TypeScript module, no fs dependency)
- Non-breaking: text/markdown output unchanged; non-MCP-Apps clients ignore
_meta - Files:
src/ui/datastore-table.html,src/resources/datastore-table-ui.ts, updatedsrc/tools/datastore.tsandsrc/resources/index.ts - Tests: 228 passing (7 new)
- Ideas:
docs/future-ideas.mdupdated with MCP Apps section - OpenSpec:
add-datastore-table-viewercreated and implemented
- New MCP resource
- Created issue #11 for one-click MCP server installation support
- Proposes
claude://install-mcp-serverprotocol integration - Based on Anthropic Desktop Extensions announcement
- Updated proposal: Two one-click installers to serve different use cases
- 🚀 "Try it now" → HTTP Worker (instant, zero install, shared quota)
- 💪 "Install locally" → npx (unlimited, requires Node.js)
- User journey: try demo first, install locally when ready for production
- Files: GitHub issue #11 + comment
Objective: Strongly encourage users to install locally instead of using shared Cloudflare Workers demo.
Changes:
- Added prominent banner in README recommending local npm installation
- Simplified Installation section - npm as primary method
- Repositioned Workers endpoint as "testing only" option
- Added visible footer to all tool responses when running on Workers
- Added HTTP headers to Workers responses (X-Service-Notice, X-Recommendation)
- Updated all MCP client configuration examples to prioritize local install
- Added note in DEPLOYMENT.md clarifying it's internal team documentation
Footer shown to Workers users:
ℹ️ Demo instance (100k requests/day shared quota). For unlimited access: https://github.com/ondata/ckan-mcp-server#installation
Files modified:
- README.md (banner, installation section, client config examples)
- src/utils/formatting.ts (isWorkers(), addDemoFooter() functions)
- src/tools/*.ts (7 tool files - applied footer to markdown responses)
- src/worker.ts (HTTP headers for debugging)
- docs/DEPLOYMENT.md (internal documentation note)
No breaking changes - All existing functionality preserved
- Fix: Replace emoji flags with SVG images from Twemoji CDN
- Problem: Country flag emojis (🇮🇹, 🇺🇸, 🇨🇦, 🇦🇺, 🇬🇧, 🇨🇭) not visible on Linux/desktop browsers lacking color emoji fonts
- Solution: Use
<img>tags pointing tohttps://cdn.jsdelivr.net/gh/twitter/twemoji@latest/assets/svg/ - Benefits: Works on all platforms, consistent appearance, better accessibility
- Files:
website/src/pages/index.astro(6 flag replacements),website/src/styles/global.css(removed.emoji-flagclass) - Impact: Flags now visible on all devices (Linux, Windows, macOS, mobile)
- Enhancement: add portal entries and API base URLs for catalog.data.gov, open.canada.ca, data.gov.au, and opendata.swiss
- Fix: align server and worker reported version with package version
- Files:
src/portals.json,package.json,package-lock.json,src/server.ts,src/worker.ts - No breaking changes
- Feature: add support for custom API paths in portal configuration
- Feature: add data.gov.uk portal support (uses
/api/action/instead of/api/3/action/) - Enhancement: extend portal configuration with
api_pathfield - Enhancement: dynamic API path construction based on portal config
- Files:
src/utils/portal-config.ts,src/utils/http.ts,src/portals.json - No breaking changes
- Fix: align server and worker reported version with package version
- Fix: update worker health tool count
- Files:
src/server.ts,src/worker.ts
- Docs: clarify GitHub release notes formatting (use here-doc + --notes-file)
- Files:
AGENTS.md,CLAUDE.md
- Tests: adjust MQA metrics details fixture to include scoring entries
- Files:
tests/integration/quality.test.ts
- MQA: add detailed quality reasons tool with metrics flag parsing
- MQA: add guidance note to use metrics endpoint for score deductions
- Tests: cover detailed MQA reasons output and guidance note
- Docs: list
ckan_get_mqa_quality_detailstool - Files:
src/tools/quality.ts,tests/integration/quality.test.ts,README.md,docs/architecture-flow.md
- None
- Docs: clarify natural language date-field mapping for package search and document
content_recentusage with example - Files:
src/tools/package.ts,src/server.ts,src/worker.ts,package.json,package-lock.json
- Workers: align browser-like headers for fetch path to avoid 403 on dati.gov.it
- Files:
src/utils/http.ts,package.json,package-lock.json
- Workers: decode compressed responses via DecompressionStream when available
- Docs: avoid demo.ckan.org in tests (use https://www.dati.gov.it/opendata)
- Files:
src/utils/http.ts,AGENTS.md,CLAUDE.md,package.json,package-lock.json
- Fix Workers build by avoiding static node:zlib import while keeping decompression in Node
- Files:
src/utils/http.ts,package.json,package-lock.json
- Decode compressed/binary CKAN responses (gzip/br/deflate) to fix DataStore calls on Messina portal
- Tests: cover gzip, brotli, deflate payloads for HTTP client
- Files:
src/utils/http.ts,tests/unit/http.test.ts,package.json,package-lock.json
- ckan_package_show: clarify dates (Issued/Modified vs harvest) and add metadata_harvested_at
- Resources: surface Access Service endpoints and effective download URL fallback
- Docs: add SPARQL examples + CKAN vs SPARQL comparison
- Tests: add package_show formatting/unit coverage
- Files:
src/tools/package.ts,tests/fixtures/responses/package-show-success.json,tests/unit/package-show-formatting.test.ts,docs/sparql-examples.md,src/server.ts,src/worker.ts,package.json,package-lock.json
- Resolve portal hostname to API URL in CKAN requests
- Tests: add unit coverage for URL resolution
- Files:
src/utils/http.ts,tests/unit/http.test.ts,src/server.ts,src/worker.ts,package.json,package-lock.json
- Add ANAC open data portal entry and aliases
- Files:
src/portals.json,src/server.ts,src/worker.ts,package.json,package-lock.json
- Use browser-like headers (UA + Sec-* + Referer) to avoid WAF blocks on some portals
- Deps: npm audit fix (hono transitive)
- Files:
src/utils/http.ts,tests/unit/http.test.ts,src/server.ts,src/worker.ts,package.json,package-lock.json,README.md
- Source: https://www.philschmid.de/mcp-best-practices
- Score: 4/6 (B+ grade)
- Doc:
docs/mcp-best-practices-evaluation.md - Key findings:
- ✅ Excellent: Tool naming, flat arguments
- ✅ Good: Instructions as context
⚠️ Partial: Outcomes focus, curation, pagination metadata
- Top recommendations:
- Add structured pagination metadata to all responses
- Create outcome-focused tools (discover_datasets, find_organization)
- Improve tool categorization (beginner/advanced/expert)
- Extracted with:
agent-browser get text 'article'(readability-like)
- Fix MQA quality score maximum from 450 to 405 (correct max: 100+100+110+75+20)
- Files modified:
src/tools/quality.ts:442,tests/integration/quality.test.ts:279,302
- Fix MQA identifier normalization to handle dot separators (e.g.
c_g273:D.1727->c_g273-d-1727) - Workers deploy: https://ckan-mcp-server.andy-pr.workers.dev (2026-01-26)
- Fix metrics parsing in Workers by switching to fetch and mocking fetch in tests
- Fix Worker metrics parsing fallback to ensure dimension scores are populated
- Fix metrics JSON-LD parsing in Workers to restore dimension score breakdown
- Ensure ld+json responses are parsed even when returned as strings
- MQA quality output now includes dimension score breakdown with ✅/
⚠️ indicators - Metrics endpoint link added for direct score inspection
- Non-max dimension(s) highlighted for quick diagnosis
- Files:
src/tools/quality.ts,tests/integration/quality.test.ts,tests/fixtures/responses/mqa-metrics-success.json
- Feature: Add dimension score breakdown with ✅/
⚠️ indicators and non-max dimensions - Source: Fetch metrics JSON-LD from data.europa.eu for scoring details
- Output: Markdown and JSON include metrics endpoint link and derived scores
- Files:
src/tools/quality.ts,tests/integration/quality.test.ts,tests/fixtures/responses/mqa-metrics-success.json
- Fix: Added custom color definitions to
tailwind.config.mjs - Colors: navy (#0A1628), data-blue (#0066CC), teal (#0D9488), coral (#F97316), amber (#F59E0B), cream (#FFFEF9)
- Impact: CTA section and all custom-colored elements now render correctly with proper contrast
- Before: Custom Tailwind classes (bg-navy, text-data-blue, etc.) were ignored, causing transparent backgrounds and unreadable text
- After: All colors apply correctly, navy CTA section has proper dark background with white/gray text
- Website: Created production-ready landing page in
website/directory - Stack: Astro v5 + React + Tailwind CSS + TypeScript strict mode
- Deployment: GitHub Actions workflow for automatic GitHub Pages deployment
- URL: https://ondata.github.io/ckan-mcp-server/
- Content:
- Hero section with value proposition for open data researchers
- Features section (6 key capabilities)
- Quick start with copy-paste configs (Claude Desktop, VS Code, Cloudflare Workers, global npm)
- Use cases (researchers, data scientists, students, journalists, etc.)
- Supported portals showcase (dati.gov.it, data.gov, data.europa.eu, etc.)
- SEO optimized (meta tags, Open Graph, sitemap)
- Responsive design (mobile-first, accessible WCAG AA)
- Assets:
- SVG favicon with network graph icon
- manifest.json for PWA support
- robots.txt and sitemap
- Script for PNG favicon generation (
generate-favicons.sh)
- Files:
website/src/pages/index.astro(main landing page)website/src/layouts/Layout.astro(SEO layout)website/src/components/Footer.astrowebsite/public/favicon.svg,manifest.json,robots.txt.github/workflows/deploy-website.yml(deployment automation)website/README.md(documentation)
- Build: 396 packages, builds successfully in ~1s
- Deployment trigger: Push to main branch with changes in
website/directory
- README: Added "Exploring the Server" section before "Manual Testing"
- Tool: MCP Inspector for interactive server exploration
- Usage:
npx @modelcontextprotocol/inspector node dist/index.js - Features: Browse tools/resources, test calls with auto-complete, real-time responses, debug errors
- Impact: Developers can quickly explore and test server without manual JSON-RPC
- Published to npm:
@aborruso/ckan-mcp-server@0.4.17 - Aligned with GitHub tag
v0.4.17
- Fix: Normalize identifiers for data.europa.eu lookups (lowercase, collapse hyphens)
- Fix: Retry with disambiguation suffixes (
~~1,~~2) when base identifier 404s - Result: MQA quality now matches portal IDs for datasets like Beinasco (with
~~1) - Improved errors: clearer message when identifier is not aligned
- Files modified:
src/tools/quality.ts,tests/integration/quality.test.ts - Deployed: Cloudflare Workers v0.4.17
- Published to npm:
@aborruso/ckan-mcp-server@0.4.16 - Aligned with GitHub tag
v0.4.16
- Bug fix: Identifier transformation for data.europa.eu API compatibility
- Issue: CKAN identifiers with colon separator (e.g.,
c_f158:224c373e...) were not recognized by MQA API - Root cause: data.europa.eu uses hyphen-separated identifiers (
c_f158-224c373e...) - Solution: Replace colons with hyphens before API call:
.replace(/:/g, '-') - Impact: MQA quality metrics now work for all dati.gov.it datasets, including municipal portals
- Example: Messina air quality dataset now returns score 405/560 (Eccellente)
- File modified:
src/tools/quality.ts(line 41) - Deployed: Cloudflare Workers v0.4.16
- Feature: Added
ckan_get_mqa_qualitytool for retrieving quality metrics from data.europa.eu MQA API - Scope: Only works with dati.gov.it datasets (server validation enforced)
- Data source: Queries https://data.europa.eu/api/mqa/cache/datasets/{identifier}
- Identifier logic: Uses
identifierfield from CKAN metadata, falls back tonameif identifier is empty - Metrics returned:
- Overall score (max 405 points)
- Accessibility (URL status, download availability)
- Reusability (license, contact point, publisher)
- Interoperability (format, media type)
- Findability (keywords, category, spatial/temporal coverage)
- Output formats: Markdown (default, human-readable) or JSON (structured data)
- Error handling: Dataset not found, MQA API unavailable, invalid server URL
- Tests: +11 integration tests (212 total, all passing)
- Server validation (www/non-www dati.gov.it URLs)
- Quality retrieval with identifier
- Fallback to name field
- Error scenarios (404, network errors)
- Markdown formatting (complete/partial data, availability indicators)
- Documentation: README.md (new Quality Metrics section), EXAMPLES.md (usage example with expected metrics)
- Files:
src/tools/quality.ts(new, 194 lines)src/server.ts(register quality tools)tests/integration/quality.test.ts(new, 11 tests)tests/fixtures/responses/mqa-quality-success.json(new)tests/fixtures/responses/package-show-{with,without}-identifier.json(new)
- OpenSpec: Proposal in
openspec/changes/add-mqa-quality-tool/(4 requirements, 11 scenarios)
- Feature: Auto-convert NOW-based date math for
modifiedandissuedfields - Problem: CKAN Solr supports
NOW-XDAYSsyntax only onmetadata_modifiedandmetadata_createdfields - Solution: New
convertDateMathForUnsupportedFields()automatically converts queries likemodified:[NOW-30DAYS TO NOW]to ISO datesmodified:[2025-12-23T... TO 2026-01-22T...] - Supported fields:
modified,issued(auto-converted) |metadata_modified,metadata_created(native NOW support) - Supported units: DAYS, MONTHS, YEARS (singular and plural forms)
- Tests: +10 unit tests (201 total, all passing)
- Documentation: Updated tool description with NOW syntax limitations and examples
- Files:
src/utils/search.ts,src/tools/package.ts,tests/unit/search.test.ts - No breaking changes: Backward compatible - existing queries work unchanged
- Fix: Escape Solr special characters when forcing
text:(...)queries - Tests: Added unit coverage for escaping and forced parser output
- Files:
src/utils/search.ts,tests/unit/search.test.ts,README.md
- Test counts updated: README.md (184→190), PRD.md (130→190)
- Version updated: PRD.md version 0.4.7→0.4.12
- Date updated: PRD.md last updated 2026-01-10→2026-01-17
- Verification: All 13 tools, 7 resources, 5 prompts implemented and documented
- Files:
README.md,PRD.md
- Issue templates: Added bug report and feature request YAML forms
- Bug report: CKAN portal URL, steps, expected/actual, error, Node version
- Feature request: problem/use case, proposed solution, alternatives
- Auto-labels:
bugandenhancement
- Issue chooser: Routes questions to Discussions Q&A
- PR template: Description, related issue, test/docs checklist
- Files:
.github/ISSUE_TEMPLATE/{bug_report,feature_request,config}.yml,.github/PULL_REQUEST_TEMPLATE.md
- Documentation: Created complete demo video documentation suite in
docs/video/demo-script.md- Commands and technical notes for 4 use casesdemo-expected-results.md- Actual test results with data samplesdemo-recording-guide.md- Step-by-step recording guide with voiceover scriptspre-recording-checklist.md- Practical day-of checklistdemo-fallback-options.md- Comprehensive fallback strategies for 8 scenariosdemo-timing-report.md- Performance analysis and timing verification
- Testing: Verified all demo commands working with dati.gov.it
- Portal overview: 67,614 datasets, top 10 organizations (~5s)
- Targeted search: 263 Milano transport datasets (~10s)
- Dataset details: Complete metadata with CSV/JSON resources (~10s)
- DuckDB analysis: DESCRIBE, SUMMARIZE, SAMPLE all working (~8s total)
- Target: 5-7 minute video demonstrating MCP convenience for Italian open data
- Status: Ready for recording with high confidence level
- Feature: Added
ckan://{server}/.../datasetsresource templates for group, organization, tag, and format filters - Fix: Map
ckan://hostnames to portal API base URLs (e.g., dati.gov.it → /opendata) - Fix: Format filtering now matches
res_formatanddistribution_format(with case variants) - Docs: Updated README and future ideas with new URI templates
- Docs: Updated
docs/proposta-spunti-datos-gob-es-mcp.mdmarking resource templates as completed - Tests: Added unit tests for dataset filter resource templates
- Files: New
src/resources/dataset-filters.ts, updates insrc/resources/index.ts,src/worker.ts
- Fix: Prompt arguments now coerce numeric strings (e.g., rows) for MCP prompt requests
- Docs: Updated evaluation notes for 0.4.11
- No breaking changes: Prompt names and outputs unchanged
- Feature: Added 5 guided MCP prompts (theme, organization, format, recent datasets, dataset analysis)
- Docs: README and new
docs/prompts.mdupdated with usage examples - Tests: Added prompt unit tests; total now 184 tests (all passing)
- Files: New
src/prompts/*, updates insrc/server.ts,src/worker.ts, README.md
- Security: Updated @modelcontextprotocol/sdk from 1.25.1 to 1.25.2 (fixes HIGH severity ReDoS vulnerability)
- Testing: Added 49 new unit tests for package.ts scoring functions
- Coverage: Improved from 37.33% to 38.63% (package.ts: 12.5% to 15%)
- Total tests: 179 tests (all passing, +49 from 130)
- Documentation: Corrected test coverage claims (was "113 tests, 97%+" now accurate "179 tests, ~39%")
- Deployment: Added npm audit check to DEPLOYMENT.md
- Files modified: package.json, src/server.ts, src/worker.ts, README.md, CLAUDE.md, docs/DEPLOYMENT.md
- New file: tests/unit/package-scoring.test.ts
- No breaking changes: All existing functionality preserved
- Added: 49 new unit tests for package.ts scoring functions
- Coverage improvement: package.ts from 12.5% to 15%
- Overall coverage: 37.33% to 38.63%
- Total tests: 130 to 179 tests (all passing)
- New test file: tests/unit/package-scoring.test.ts
- Functions tested:
- extractQueryTerms (10 tests)
- escapeRegExp (6 tests)
- textMatchesTerms (10 tests)
- scoreTextField (6 tests)
- scoreDatasetRelevance (17 tests with edge cases)
- Exports: Made internal functions testable (extractQueryTerms, escapeRegExp, textMatchesTerms, scoreTextField)
- Impact: Better coverage of dataset relevance scoring logic
- Fix: Corrected test coverage claims in README.md and CLAUDE.md
- Previous claim: "113 tests, 97%+ coverage"
- Actual values: 130 tests passing, ~37% overall coverage
- Utility modules: 98% coverage (excellent)
- Tool handlers: 12-20% coverage (needs improvement)
- Impact: Documentation now accurately reflects project state
- Files modified: README.md, CLAUDE.md
- Added: npm audit check to DEPLOYMENT.md (Step 4.5)
- Added: Security audit to pre-release checklist
- Recommendation: Always run
npm auditbefore production deployment
- Fix: Update @modelcontextprotocol/sdk from 1.25.1 to 1.25.2
- Reason: Resolves HIGH severity ReDoS vulnerability (GHSA-8r9q-7v3j-jr4g)
- Tests: All 130 tests passing
- Audit: 0 vulnerabilities
- Fix: On CKAN 500, fall back to
package_searchfacets for org counts - Output: Facet lists show top 10; suggest
response_format: jsonandfacet_limit
- MCP tool awareness: Gemini now selects appropriate tool from 15 available
- Loads tool list on startup via
tools/list - Passes available tools to Gemini with descriptions
- Gemini chooses tool and generates arguments based on query type
- Examples:
ckan_organization_listfor "organizations with most datasets" ckan_find_relevant_datasetsfor smart searchesckan_tag_listfor tag statistics
- Loads tool list on startup via
- Multi-type results: UI handles datasets, organizations, tags
- Organization cards show package count
- Dataset cards show resources and org name
- Status shows tool being used ("Using ckan_organization_list...")
- Fallback: Defaults to
ckan_package_searchif Gemini fails - Fix: Query "quali organizzazioni con il maggior numero di dataset" now works correctly
- UI redesign: Dark theme with data editorial aesthetic
- Typography: DM Serif Display + IBM Plex Sans
- Color scheme: Deep charcoal (#0f1419) with cyan accent (#06b6d4)
- Glass morphism effects, gradient text, subtle grid background
- Smooth animations: slide-in, hover transitions, status pulse
- Collapsible settings panel with icon-based controls
- Enhanced dataset cards with hover lift and glow
- Custom scrollbar, loading shimmer, SVG icons throughout
- Conversation context: Added history management
- Gemini receives conversation history for contextual refinement
- Users can ask follow-up queries ("only from Tuscany", "last 5 years")
- History limited to 10 messages (5 exchanges) to avoid token overflow
- Reset button to clear conversation and start fresh
- UX improvements: Better visual hierarchy, spacing, interaction patterns
- Responsive: Mobile-friendly layout maintained
- Web GUI: Replaced landing with MCP-backed chat UI (vanilla + Tailwind)
- MCP: Added JSON-RPC search flow with dataset cards
- Fix: Added
Acceptheader for MCP 406 requirement - Fix: Normalize natural-language queries before search
- Gemini: Added API key input and NL→Solr query call
- Config: Added per-portal search parser config
- Tool: Added query parser override for package search and relevance
- Tool: Added
ckan_find_relevant_datasets - Docs: Updated README/EXAMPLES
- Tests: Added relevance scoring checks
- Workers: /health version/tools updated
- Tool: Added
ckan_datastore_search_sql - Docs: Updated README/EXAMPLES/PRD for SQL support
- Tests: Added SQL fixture and checks
- Tags: Added
ckan_tag_listwith faceting and filtering - Groups: Added
ckan_group_list,ckan_group_show,ckan_group_search - Docs: Updated README with examples and tool list
- Tests: Added tag/group fixtures and tests
- npm package: Added
.npmignoreto exclude dev artifacts
- Date formatting: ISO
YYYY-MM-DDoutput, tests aligned - HTTP transport: Single shared transport per process
- Registration: Centralized tool/resource setup via
registerAll() - Docs: Updated CLAUDE/PRD/REFACTORING notes
- New section in EXAMPLES.md: "Understanding Solr Field Types: Exact vs Fuzzy Search"
- Documents difference between
type=string(exact match) andtype=text(fuzzy) - String fields: res_format, tags, organization, license, state, name (case-sensitive)
- Text fields: title, notes, author, maintainer (normalized, fuzzy enabled)
- Practical example:
res_format:CSV(43,836 results) vsres_format:csv(0 results) - Links to CKAN Solr schema on GitHub
- Explains why some searches are exact and others are fuzzy
- Documents difference between
- Impact: Users understand when exact matching is required vs when fuzzy search works
-
Production deployment: Server now live on Cloudflare Workers
- Public endpoint:
https://ckan-mcp-server.andy-pr.workers.dev - Global edge deployment (low latency worldwide)
- Free tier: 100,000 requests/day
- Bundle size: 398KB (minified: 130KB gzipped)
- Cold start time: 58ms
- Public endpoint:
-
New files:
src/worker.ts(95 lines): Workers entry point using Web Standards transportwrangler.toml(5 lines): Cloudflare Workers configuration
-
New npm scripts:
build:worker: Compile for Workers (browser platform, ESM format)dev:worker: Local testing with wrangler devdeploy: Build and deploy to Cloudflare
-
Architecture:
- Uses
WebStandardStreamableHTTPServerTransportfrom MCP SDK - Compatible with Workers runtime (no Node.js APIs)
- Stateless mode (no session management)
- JSON responses enabled for simplicity
- CORS enabled for browser access
- Uses
-
Testing: All 7 MCP tools verified in Workers environment
- Health check: ✅ Working
- tools/list: ✅ Returns all 7 tools
- ckan_status_show: ✅ External CKAN API calls working
- Response times: < 2s for typical queries
-
Documentation:
- Updated README.md with "Deployment Options" section
- Added Option 4 to Claude Desktop config (Workers HTTP transport)
- Created OpenSpec proposal in
openspec/changes/add-cloudflare-workers/
-
No breaking changes: stdio and self-hosted HTTP modes still fully supported
- Dual build system: Node.js bundle unchanged
- Existing tests (113) all passing
- Version bumped to 0.4.0
- npm Publication: Published to npm registry as
@aborruso/ckan-mcp-server- Package size: 68.6 KB (236 KB unpacked)
- Public access configured
- Installation time: 5 min → 30 sec (90% faster)
- User actions: 6 steps → 2 steps (67% reduction)
- Global Command Support: Added
binfield to package.json- Direct command:
ckan-mcp-server(no node path required) - Works system-wide after global install
- Direct command:
- Documentation Enhancement: Three installation options in README
- Option 1: Global installation (recommended)
- Option 2: Local project installation
- Option 3: From source (development)
- Platform-specific paths (macOS, Windows, Linux)
- GitHub Release: Tagged v0.3.2 with release notes
- Impact: Low barrier to entry, standard npm workflow, better discoverability
- Overall Rating: 9.0/10 (local evidence; external distribution not verified)
- Distribution readiness: 9/10 (metadata and CLI entry point verified)
- Testing: 113 tests passing; coverage 97%+ (2026-01-09)
- Status: Packaging and docs production-ready; npm/GitHub release require external verification
- See
docs/evaluation-v0.3.2.mdfor full assessment
- Added unit tests for HTTP error branches and URL generator org paths
- Tests:
tests/unit/http.test.ts,tests/unit/url-generator.test.ts
- Tests:
npm test: 113 tests passingnpm run test:coverage: 97.01% statements, 89.36% branches, 100% functions, 96.87% lines
- New Section: "Advanced Query Examples" in README.md
- 4 real-world examples tested on dati.gov.it portal
- English explanations with Italian query terms maintained
- Each example includes: use case, query, techniques, results
- Example 1: Fuzzy search + date math + boosting (871 healthcare datasets)
- Example 2: Proximity search + complex boolean (306 air quality datasets)
- Example 3: Wildcard + range + field existence (5,318 regional datasets)
- Example 4: Date ranges + exclusive bounds (demonstrates precise constraints)
- Solr Syntax Reference: Quick reference table for all query operators
- Impact: Users have practical, tested examples for advanced searches
- Tool Description: Enhanced
ckan_package_searchtool description with comprehensive Solr query syntax- Added boolean operators (AND, OR, NOT, +, -, grouping)
- Added wildcards, fuzzy search, proximity search
- Added range queries (inclusive/exclusive bounds)
- Added date math (NOW-1YEAR, NOW/DAY, etc.)
- Added field existence checks
- Added boosting/relevance scoring (^, ^=)
- 15+ inline examples in tool description
- EXAMPLES.md: New "Advanced Solr Query Features" section (~280 lines)
- Fuzzy search examples (edit distance matching)
- Proximity search (words within N positions)
- Boosting examples (relevance scoring)
- Field existence checks
- Date math with relative dates
- Complex nested queries
- Range queries with different bounds
- Wildcard patterns
- Practical advanced examples
- Impact: LLMs calling MCP server now have comprehensive query syntax reference
- Portal-Specific URLs: Introduced configuration system for non-standard CKAN portals
- New
src/portals.json: Configurable mapping for portals likedati.gov.it - New
src/utils/url-generator.ts: Utility for generating context-aware view URLs - Fixed issue where
dati.gov.itlinks pointed to standard CKAN paths instead of custom/view-dataset/paths - Automated replacement of
{id},{name}and{server_url}placeholders in URL templates - Updated
ckan_package_search,ckan_package_show,ckan_organization_listandckan_organization_showtools to use the new system
- New
- MCP Resource Templates: Direct data access via
ckan://URI schemeckan://{server}/dataset/{id}- Dataset metadatackan://{server}/resource/{id}- Resource metadatackan://{server}/organization/{name}- Organization metadata- New
src/resources/module (5 files, ~240 lines)
- Tests: 101 tests total (was 79), 100% passing
- Cleanup: Removed
src/index-old.ts, standardized to English
- Overall Rating: 9/10 - Production-ready with excellent architecture
- Improvements from v0.2.0:
- Removed legacy code (index-old.ts)
- Standardized documentation to English
- Added MCP Resource Templates
- Expanded test suite (79 → 101 tests)
- Remaining enhancements: Caching layer, configurable limits, authentication
- See
docs/evaluation-v0.3.0.mdfor full assessment
- Test Suite: Added comprehensive automated testing infrastructure
- 79 tests total: 100% passing
- Unit tests (25): formatting utilities, HTTP client
- Integration tests (54): all 7 CKAN API tools
- Coverage: vitest with v8 coverage support
- Test fixtures for all CKAN endpoints + error scenarios
- Scripts:
npm test,npm run test:watch,npm run test:coverage
- Documentation: Translated to English
- README.md: comprehensive project overview
- EXAMPLES.md: detailed usage patterns
- CLAUDE.md: AI assistant instructions
- OpenSpec: Added change proposals
- Test suite implementation proposal
- Documentation translation spec
- Major refactoring: Restructured codebase from monolithic file to modular structure
- Before: 1 file (
src/index.ts) - 1021 lines - After: 11 organized modules - 1097 total lines
- Structure:
src/ ├── index.ts (39) # Entry point ├── server.ts (12) # MCP server config ├── types.ts (16) # Types & schemas ├── utils/ # Utilities (88 lines) │ ├── http.ts # CKAN API client │ └── formatting.ts # Output formatting ├── tools/ # Tool handlers (903 lines) │ ├── package.ts # 2 tools │ ├── organization.ts # 3 tools │ ├── datastore.ts # 1 tool │ └── status.ts # 1 tool └── transport/ # Transports (39 lines) ├── stdio.ts └── http.ts - Benefits:
- Smaller files (max 350 lines vs 1021)
- Localized and safe changes
- Isolated testing possible
- Simplified maintenance
- Zero breaking changes
- Performance: Build time 16ms, bundle 33KB (unchanged)
- Testing: All 7 tools working
- Before: 1 file (
- Created
REFACTORING.md- Complete refactoring documentation - Updated
CLAUDE.md- Updated with new modular structure - Updated
PRD.md- Added npm publication requirement- Goal: Simple installation like PyPI in Python
npm install -g ckan-mcp-servernpx ckan-mcp-server
- Comprehensive testing on https://www.dati.gov.it/opendata
- Server status: CKAN 2.10.3, 66,937 datasets
- COVID search: 90 datasets found
- Organization search: Regione Toscana (10,988 datasets)
- Faceting statistics: Top orgs, formats, tags
- Dataset details: Vaccini COVID-19 2024 (Puglia)
- Response times: 3-5 seconds (network + CKAN API)
- All 7 tools working perfectly
- Code refactored and modular
- Fully tested and functional
- Documentation complete
- Ready for npm publication
- New tool:
ckan_organization_search- search organizations by name pattern- Simple input: only
pattern(automatic wildcards) - Output: only matching organizations (zero datasets downloaded)
- Efficient: server-side filtering, token savings
- Example: pattern "toscana" -> 2 orgs, 11K total datasets
- Simple input: only
- Initial release
- MCP server for CKAN open data portals
- 7 tools: package_search, package_show, organization_list, organization_show, organization_search, datastore_search, status_show
- Build system: esbuild (ultra-fast, 47ms build)
- Fixed TypeScript memory issues by switching from tsc to esbuild
- Corrected dati.gov.it URL to https://www.dati.gov.it/opendata
- Created CLAUDE.md for repository guidance
- Tested successfully with dati.gov.it (4178 datasets on "popolazione" query)