npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

regionecalabria-opendata-mcp

v0.2.0

Published

Model Context Protocol (MCP) server for Regione Calabria open data.

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-mcp

Example MCP client config using npx:

{
  "mcpServers": {
    "regionecalabria-opendata-mcp": {
      "command": "npx",
      "args": ["-y", "regionecalabria-opendata-mcp"]
    }
  }
}

Run from source

pnpm install
pnpm run build

Run 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 dev

Run over Streamable HTTP (for remote/networked clients)

pnpm run start:http
# or, during development:
pnpm run dev:http

Environment 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:

  1. pnpm run build (re-run after any change to src/, since mcp.json runs the compiled output).
  2. Open the Chat view, and start the regionecalabria-opendata-mcp server from the MCP Servers UI (or run MCP: List Servers from the Command Palette) — confirm the trust prompt.
  3. Ask Copilot to use get_version, package_list, package_show, package_search, datastore_search, group_list, or group_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 portal

Development 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):

  1. 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).
  2. First publish must be done manually (a package must exist on npm before a trusted publisher can be configured for it): npm login, then npm publish --access public from a clean pnpm install && pnpm run build.
  3. On npmjs.com → this package → Settings → Trusted Publisher → GitHub Actions, configure: organization francescopersico, repository regionecalabria-opendata-mcp, workflow filename publish.yml, allowed action npm publish.
  4. 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.
  5. 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:

  1. Bump version in package.json (follow SemVer).
  2. Commit, then tag and push: git tag vX.Y.Z && git push origin vX.Y.Z.
  3. The Publish to npm GitHub Actions workflow runs pnpm run verify and publishes automatically.

Definition of Done for this scaffold

  • [x] pnpm run verify passes (typecheck, lint, tests, build all green).
  • [x] get_version, package_list, package_show, package_search, datastore_search, group_list, group_show are 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] .gitignore excludes node_modules/, build/, and local env files.
  • [ ] regionecalabria-opendata-mcp is published on the npm registry and runnable via npx regionecalabria-opendata-mcp (first publish is a manual, one-time step; see Publishing / releasing).
  • [x] A publish.yml GitHub Actions workflow exists to publish future releases via npm trusted publishing (OIDC), triggered by pushing a vX.Y.Z tag.
  • [x] package_* tools call the real Regione Calabria CKAN API (base URL overridable via CKAN_BASE_URL) and surface CKAN/network errors as tool errors instead of throwing.
  • [x] Unit tests mock fetch for determinism; one live integration test confirms the real portal's package_show response 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.