@opendata.cat/mcp-server 0.0.18 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,10 +27,11 @@ Un projecte d'**[opendata.cat](https://opendata.cat)** — associacio sense anim
27
27
  | [Ajuntament de Reus](https://opendata.reus.cat) | 119 | CKAN datastore |
28
28
  | [Ajuntament de Girona](https://www.girona.cat/opendata/) | 53 | CKAN datastore |
29
29
  | [FGC (Ferrocarrils)](https://dadesobertes.fgc.cat) | 50 | Opendatasoft |
30
+ | [Idescat](https://www.idescat.cat) | 138 | Idescat API |
30
31
 
31
- El Consorci AOC inclou datasets de les **diputacions de Tarragona, Girona i Lleida**, ajuntaments, consells comarcals i altres organismes publics catalans.
32
+ El Consorci AOC inclou datasets de les **diputacions de Tarragona, Girona i Lleida**, ajuntaments, consells comarcals i altres organismes publics catalans. Idescat proporciona indicadors estadistics de Catalunya (poblacio, economia, treball, salut...) amb series temporals.
32
33
 
33
- **+2.800 datasets** de 7 portals. La majoria queryables amb filtres, cerca i paginacio.
34
+ **+2.800 datasets** de 8 portals. La majoria queryables amb filtres, cerca i paginacio.
34
35
 
35
36
  El cataleg s'actualitza automaticament cada setmana. Cada endpoint es valida per assegurar que funciona.
36
37
 
@@ -40,6 +41,7 @@ El cataleg s'actualitza automaticament cada setmana. Cada endpoint es valida per
40
41
  - **Diba REST**: API do.diba.cat amb paginacio i filtres (Diputacio BCN)
41
42
  - **CIDO JSON:API**: api.diba.cat per contractacions, normatives, subvencions, oposicions, convenis (Diputacio BCN)
42
43
  - **Opendatasoft**: API records amb filtres i cerca (FGC — horaris GTFS, trens temps real, estacions esqui)
44
+ - **Idescat**: indicadors estadistics amb series temporals (poblacio, economia, treball, salut, educacio)
43
45
  - **File download**: descarrega directa de CSV, JSON, XLSX o fitxers GIS
44
46
  - **Restricted**: requereix token d'autenticacio (4 datasets BSM)
45
47
 
@@ -224,6 +226,13 @@ Les contribucions son benvingudes! Per afegir un nou portal de dades obertes:
224
226
 
225
227
  ## Changelog
226
228
 
229
+ ### v0.1.0 (2026-04-14)
230
+ - Nou portal Idescat (Institut d'Estadistica de Catalunya) — 138 indicadors estadistics
231
+ - Client Idescat amb series temporals (poblacio, PIB, atur, inflacio, educacio, salut...)
232
+ - Millora ranking de cerca: datasets amb el terme al nom surten primer
233
+ - Instruccio multi-cerca a search_datasets per cobrir temes amplis
234
+ - Sinonims ampliats: emergencies (112, SEM, ambulancia, bombers, mossos)
235
+
227
236
  ### v0.0.17 (2026-04-14)
228
237
  - Simplificacio automatica de geometries GIS: centroide + bounding box en lloc de milers de coordenades
229
238
  - Decodificador GTFS-RT integrat: trens FGC en temps real (retards, alertes, posicions GPS)
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Client per a l'API d'Idescat (Institut d'Estadística de Catalunya).
3
+ * Consulta indicadors estadístics de Catalunya amb sèries temporals.
4
+ */
5
+ export declare function queryIdescat(endpoint: string): Promise<{
6
+ indicators: Record<string, unknown>[];
7
+ count: number;
8
+ }>;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Client per a l'API d'Idescat (Institut d'Estadística de Catalunya).
3
+ * Consulta indicadors estadístics de Catalunya amb sèries temporals.
4
+ */
5
+ export async function queryIdescat(endpoint) {
6
+ const resp = await fetch(endpoint);
7
+ if (!resp.ok)
8
+ throw new Error(`Idescat error ${resp.status}: ${resp.statusText}`);
9
+ const data = (await resp.json());
10
+ const raw = data?.indicadors?.i;
11
+ if (!raw)
12
+ return { indicators: [], count: 0 };
13
+ const items = Array.isArray(raw) ? raw : [raw];
14
+ const indicators = items
15
+ .filter((item) => item.id)
16
+ .map((item) => {
17
+ const unit = item.u;
18
+ const period = item.r;
19
+ return {
20
+ indicador: item.c ?? "",
21
+ valor: item.v ?? null,
22
+ unitat: typeof unit === "object" ? (unit?.content ?? "") : (unit ?? ""),
23
+ periode: typeof period === "object" ? (period?.title ?? "") : (period ?? ""),
24
+ font: item.s ?? "",
25
+ serie_temporal: item.ts ?? "",
26
+ link: item.l ?? "",
27
+ };
28
+ });
29
+ return { indicators, count: indicators.length };
30
+ }
package/dist/index.js CHANGED
@@ -11,14 +11,15 @@ import { queryDiba } from "./clients/diba.js";
11
11
  import { queryCido } from "./clients/cido.js";
12
12
  import { queryOpendatasoft } from "./clients/opendatasoft.js";
13
13
  import { decodeGtfsRt } from "./clients/gtfsrt.js";
14
+ import { queryIdescat } from "./clients/idescat.js";
14
15
  const server = new McpServer({
15
16
  name: "opendata-cat",
16
- version: "",
17
+ version: "0.1.0",
17
18
  });
18
19
  // Tool 1: search_datasets
19
20
  server.tool("search_datasets", "Cerca datasets de dades obertes catalanes per text lliure. IMPORTANT: fes múltiples cerques amb termes diferents per cobrir un tema ampli. Ex: si l'usuari pregunta per 'emergències', cerca 'bombers', '112 trucades', 'mossos policia', 'SEM ambulància' per separat. La cerca inclou sinònims en català i castellà.", {
20
21
  query: z.string().describe("Text de cerca (ex: 'qualitat aire', 'pressupostos')"),
21
- portal: z.string().optional().describe("Filtrar per portal: 'generalitat', 'barcelona', 'diba', 'aoc', 'reus', 'girona', 'fgc'"),
22
+ portal: z.string().optional().describe("Filtrar per portal: 'generalitat', 'barcelona', 'diba', 'aoc', 'reus', 'girona', 'fgc', 'idescat'"),
22
23
  category: z.string().optional().describe("Filtrar per categoria"),
23
24
  limit: z.number().optional().default(20).describe("Nombre màxim de resultats (defecte: 20)"),
24
25
  }, async ({ query, portal, category, limit }) => {
@@ -114,6 +115,20 @@ server.tool("query_dataset", "Executa una consulta contra un dataset i retorna f
114
115
  }
115
116
  results = data.records;
116
117
  }
118
+ else if (dataset.api_type === "idescat") {
119
+ const data = await queryIdescat(dataset.api_endpoint);
120
+ return {
121
+ content: [{
122
+ type: "text",
123
+ text: JSON.stringify({
124
+ dataset: dataset.name,
125
+ portal: "Idescat",
126
+ count: data.count,
127
+ data: data.indicators,
128
+ }, null, 2),
129
+ }],
130
+ };
131
+ }
117
132
  else {
118
133
  return {
119
134
  content: [{
@@ -148,6 +163,7 @@ server.tool("list_portals", "Llista els portals de dades obertes catalans dispon
148
163
  { id: "reus", name: "Ajuntament de Reus", url: "https://opendata.reus.cat", api: "CKAN" },
149
164
  { id: "girona", name: "Ajuntament de Girona", url: "https://www.girona.cat/opendata/", api: "CKAN" },
150
165
  { id: "fgc", name: "Ferrocarrils de la Generalitat de Catalunya", url: "https://dadesobertes.fgc.cat", api: "Opendatasoft" },
166
+ { id: "idescat", name: "Idescat (Institut d'Estadística de Catalunya)", url: "https://www.idescat.cat", api: "Idescat API" },
151
167
  ];
152
168
  const cats = await getCategories();
153
169
  const portalCounts = new Map(cats.portals.map((p) => [p.portal_id, p.total]));
@@ -471,7 +487,7 @@ async function main() {
471
487
  // Health check
472
488
  if (req.url === "/health") {
473
489
  res.writeHead(200, { "Content-Type": "application/json" });
474
- res.end(JSON.stringify({ status: "ok", name: "opendata-cat", version: "" }));
490
+ res.end(JSON.stringify({ status: "ok", name: "opendata-cat", version: "0.1.0" }));
475
491
  return;
476
492
  }
477
493
  // MCP endpoint
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.0.18",
6
+ "version": "0.1.0",
7
7
  "description": "Servidor MCP per consultar les dades obertes públiques de Catalunya",
8
8
  "type": "module",
9
9
  "main": "dist/index.js",