regionecalabria-opendata-mcp
v0.2.0
Published
Model Context Protocol (MCP) server for Regione Calabria open data.
Maintainers
Readme
regionecalabria-opendata-mcp
A Model Context Protocol (MCP) server, scaffolded with the official
TypeScript SDK (@modelcontextprotocol/sdk v1, stable).
Exposes the following tools:
| Tool | Description | Input | Output |
| ---------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------ | ----------------------------------------------- |
| get_version | Returns this server's own name and version, read from package.json. | none | { name: string, version: string } |
| package_list | Lists dataset names/identifiers from the Regione Calabria open data portal (CKAN package_list). | { limit?: number, offset?: number } | { names: string[] } |
| package_show | Returns the full metadata + resources of one dataset by id/name (CKAN package_show). | { id: string } | { dataset: object } (raw CKAN dataset dict) |
| package_search | Full-text search over datasets (CKAN package_search); preferred over package_list for browsing/filtering. | { q?: string, rows?: number, start?: number, sort?: string } | { count: number, results: object[] } (raw CKAN dataset dicts) |
| datastore_search | Queries the actual data rows inside a DataStore-enabled resource (CKAN datastore_search), not just dataset metadata. Requires a resource_id with datastore_active: true, found via package_show. | { resourceId: string, q?: string, filters?: object, fields?: string[], sort?: string, limit?: number, offset?: number } | { total?: number, fields: object[], records: object[] } |
| group_list | Lists the thematic categories ("temi", CKAN groups, e.g. Agricoltura, Ambiente, Economia) used to classify datasets on the portal, each with its dataset count (CKAN group_list). | none | { groups: { name: string, title: string, description: string, packageCount: number }[] } |
| group_show | Shows one theme's details plus a lightweight list (id/name/title) of the datasets in it (CKAN group_show); reshaped, not a raw passthrough, to avoid a heavy nested payload and to omit user/account data. | { id: string } | { name: string, title: string, description: string, packageCount: number, datasets: { id: string, name: string, title: string }[] } |
No other tools, resources, or prompts are registered. This is intentional scaffolding — new capabilities should be added deliberately in follow-up work.
This portal has only a single CKAN organization (regione-calabria), so an organization_list/
organization_show tool would add no browsing value (confirmed live: organization_list returns
a 1-element list, and organization_list?all_fields=true currently errors with HTTP 500 on this
portal). The 17 CKAN "groups" (themes/"temi", e.g. Agricoltura, Ambiente, Economia) are the
real, well-populated categorization axis on this portal, hence group_list/group_show instead.
datastore_search only works against resources that have CKAN's DataStore extension enabled for
them (datastore_active: true in package_show's resources array); this portal has DataStore
enabled and most CSV/XLSX resources are queryable this way. limit is capped at 1000 rows per
call regardless of the value requested, to keep responses a reasonable size to return to a model.
package_show and package_search return CKAN's dataset dictionaries as-is (loosely typed).
This portal uses the Italian DCAT-AP_IT metadata profile, whose fields vary between datasets and
sometimes contain JSON-encoded strings (e.g. theme, creator), so no strict schema is enforced
on dataset shape.
Requirements
- Node.js
>=22(LTS) - pnpm (project package manager)
Getting started
Run via npx (no local clone needed)
Once published to the npm registry (see Publishing), any MCP client can run the stdio server without installing anything up front:
npx regionecalabria-opendata-mcpExample MCP client config using npx:
{
"mcpServers": {
"regionecalabria-opendata-mcp": {
"command": "npx",
"args": ["-y", "regionecalabria-opendata-mcp"]
}
}
}Run from source
pnpm install
pnpm run buildRun over stdio (for MCP clients that spawn a process, e.g. Claude Desktop, VS Code)
pnpm run start
# or, during development, without a build step:
pnpm run devRun over Streamable HTTP (for remote/networked clients)
pnpm run start:http
# or, during development:
pnpm run dev:httpEnvironment variables:
| Variable | Default | Description |
| ---------------- | ------------------------------------------------------------- | ---------------------------------------------------------------- |
| PORT | 3000 | TCP port to listen on. |
| HOST | 127.0.0.1 | Host to bind to. Also used for DNS-rebinding host validation. |
| CKAN_BASE_URL | https://dati.regione.calabria.it/opendata/api/3/action | Base URL of the CKAN Action API used by the package_* tools. Override to point at another CKAN portal. |
The HTTP transport is served in stateless mode (no session tracking) via POST /mcp, since a
single side-effect-free tool doesn't need session state, resumability, or server-initiated
notifications. GET/DELETE /mcp return 405 Method Not Allowed.
DNS-rebinding protection is enabled via the SDK's createMcpExpressApp() helper, which validates
the Host header against the configured HOST. Do not bind HOST=0.0.0.0 without adding your own
host/CORS validation in front of it.
Testing in VS Code Copilot Chat
This repo includes a workspace .vscode/mcp.json that registers this server
(over stdio, running the compiled build/stdio.js) for Copilot Chat:
pnpm run build(re-run after any change tosrc/, sincemcp.jsonruns the compiled output).- Open the Chat view, and start the
regionecalabria-opendata-mcpserver from the MCP Servers UI (or run MCP: List Servers from the Command Palette) — confirm the trust prompt. - Ask Copilot to use
get_version,package_list,package_show,package_search,datastore_search,group_list, orgroup_show, e.g. "Use package_search to find Calabria open datasets about acque".
Project structure
src/
server.ts # createServer(): builds an McpServer and registers all tools
package-metadata.ts # reads name/version from package.json
ckan-client.ts # shared CKAN Action API client (callCkanAction, getCkanBaseUrl)
tools/
version-tool.ts # the get_version tool
package-list-tool.ts # the package_list tool
package-show-tool.ts # the package_show tool
package-search-tool.ts # the package_search tool
datastore-search-tool.ts # the datastore_search tool
group-list-tool.ts # the group_list tool
group-show-tool.ts # the group_show tool
stdio.ts # stdio transport entrypoint
http.ts # Streamable HTTP transport entrypoint (stateless)
test/
version-tool.test.ts # end-to-end test via an in-memory client/server pair
ckan-client.test.ts # unit tests for the CKAN client (mocked fetch)
package-tools.test.ts # end-to-end tool tests (mocked fetch)
datastore-search-tool.test.ts # end-to-end datastore_search tool tests (mocked fetch)
group-tools.test.ts # end-to-end group_list/group_show tool tests (mocked fetch)
package-show.integration.test.ts # live network sanity check against the real portalDevelopment scripts
| Script | Purpose |
| --------------------- | ----------------------------------------------------- |
| pnpm run build | Type-check and compile src/ to build/. |
| pnpm run typecheck | Type-check only, no output. |
| pnpm run lint | Lint + check formatting with Biome. |
| pnpm run lint:fix | Lint and auto-fix + format with Biome. |
| pnpm run test | Run the Vitest test suite once. |
| pnpm run test:watch | Run Vitest in watch mode. |
| pnpm run verify | Typecheck + lint + test + build (local "definition of done" check). |
Publishing / releasing
This package is published to the public npm registry as regionecalabria-opendata-mcp, so it can be
run with npx regionecalabria-opendata-mcp by any MCP client without cloning this repo.
Releases are published from .github/workflows/publish.yml via npm's
trusted publishing (OIDC) — no NPM_TOKEN secret is stored
in this repo, and provenance attestations are generated automatically.
One-time setup (already done / to do once by a maintainer with npm publish rights):
- Optionally, tag the release commit locally now:
git tag v0.1.0(don't push it yet — a local-only tag doesn't trigger anything on GitHub Actions). - First publish must be done manually (a package must exist on npm before a trusted publisher can be
configured for it):
npm login, thennpm publish --access publicfrom a cleanpnpm install && pnpm run build. - On npmjs.com → this package → Settings → Trusted Publisher → GitHub Actions,
configure: organization
francescopersico, repositoryregionecalabria-opendata-mcp, workflow filenamepublish.yml, allowed actionnpm publish. - Recommended: afterwards, under Settings → Publishing access, enable "Require two-factor authentication and disallow tokens" so only the trusted GitHub Actions workflow (or a maintainer with 2FA) can publish.
- Push the tag whenever you're ready:
git push origin v0.1.0. This still triggers the workflow, but its "already published?" check (npm view, unauthenticated) detects the version is already on the registry and skips publishing instead of failing — safe to push right after step 2, or only after step 4, whichever you prefer.
Every subsequent release:
- Bump
versioninpackage.json(follow SemVer). - Commit, then tag and push:
git tag vX.Y.Z && git push origin vX.Y.Z. - The
Publish to npmGitHub Actions workflow runspnpm run verifyand publishes automatically.
Definition of Done for this scaffold
- [x]
pnpm run verifypasses (typecheck, lint, tests, build all green). - [x]
get_version,package_list,package_show,package_search,datastore_search,group_list,group_showare the only tools registered; no other tools/resources/prompts exist. - [x] Both stdio and Streamable HTTP entrypoints start and respond correctly.
- [x] DNS-rebinding protection is enabled on the HTTP transport.
- [x]
.gitignoreexcludesnode_modules/,build/, and local env files. - [ ]
regionecalabria-opendata-mcpis published on the npm registry and runnable vianpx regionecalabria-opendata-mcp(first publish is a manual, one-time step; see Publishing / releasing). - [x] A
publish.ymlGitHub Actions workflow exists to publish future releases via npm trusted publishing (OIDC), triggered by pushing avX.Y.Ztag. - [x]
package_*tools call the real Regione Calabria CKAN API (base URL overridable viaCKAN_BASE_URL) and surface CKAN/network errors as tool errors instead of throwing. - [x] Unit tests mock
fetchfor determinism; one live integration test confirms the real portal'spackage_showresponse still matches our assumptions.
Connecting an MCP client (stdio)
Example client configuration (paths are absolute):
{
"mcpServers": {
"regionecalabria-opendata-mcp": {
"command": "node",
"args": ["/ABSOLUTE/PATH/TO/regionecalabria-opendata-mcp/build/stdio.js"]
}
}
}License
MIT — see LICENSE.
