@opendata.cat/mcp-server 0.0.19 → 0.1.1

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
 
@@ -140,45 +142,18 @@ Llista els portals disponibles amb el nombre de datasets de cadascun. No requere
140
142
 
141
143
  Llista totes les categories i temes de datasets disponibles amb comptadors per portal. Ideal per descobrir quins tipus de dades hi ha.
142
144
 
143
- ## Prompts disponibles
144
-
145
- Prompts predefinits que guien l'LLM pas a pas per fer analisis completes:
146
-
147
- | Prompt | Descripcio | Arguments |
148
- |--------|-----------|-----------|
149
- | `estat_embassaments` | Estat actual dels embassaments amb grafiques d'evolucio | — |
150
- | `trens_fgc_temps_real` | Retards, alertes i posicions dels trens FGC en temps real | — |
151
- | `qualitat_aire` | Analisi de la qualitat de l'aire amb comparativa OMS/UE | `lloc` (opcional) |
152
- | `accidents_transit` | Analisi d'accidents de transit amb punts negres i tendencies | `municipi` (opcional) |
153
- | `pressupostos_municipals` | Pressupostos municipals amb desglossament per partides | `municipi` (opcional) |
154
- | `compara_municipis` | Compara dos municipis en totes les dades disponibles | `municipi_a`, `municipi_b` |
155
- | `descobreix_dades` | Mapa complet de dades obertes sobre un tema | `tema` |
156
- | `analisi_bombers` | Actuacions dels Bombers: emergencies, distribucio, tendencies | `comarca` (opcional) |
157
- | `novetats` | Datasets actualitzats mes recentment | `portal` (opcional) |
158
- | `datasets_populars` | Datasets mes consultats pels usuaris | — |
159
- | `explorar_portal` | Guia completa d'un portal: categories, exemples, destacats | `portal` |
160
- | `dades_municipi` | Fitxa completa d'un municipi amb totes les dades disponibles | `municipi` |
161
- | `datasets_temps_real` | Datasets amb dades en temps real o actualitzacio frequent | — |
162
- | `resum_portals` | Visio panoramica de tots els portals de dades obertes | — |
163
-
164
145
  ## Exemples d'us
165
146
 
166
147
  Un cop configurat, pots fer preguntes al teu LLM com:
167
148
 
168
- - *"Quin es l'estat dels embassaments de Catalunya?"* → prompt `estat_embassaments`
169
- - *"Hi ha algun tren de FGC amb retard ara mateix?"* → prompt `trens_fgc_temps_real`
170
- - *"Analitza la qualitat de l'aire a Terrassa"* → prompt `qualitat_aire`
171
- - *"Fes unes grafiques amb l'evolucio dels accidents de transit a Barcelona"* → prompt `accidents_transit`
172
- - *"Compara Girona i Tarragona en dades obertes"* → prompt `compara_municipis`
173
- - *"Quines dades obertes hi ha sobre educacio a Catalunya?"* → prompt `descobreix_dades`
174
- - *"Dona'm les ultimes dades de pressupostos de Reus"* → prompt `pressupostos_municipals`
175
- - *"Analitza les actuacions dels Bombers al Valles"* → prompt `analisi_bombers`
176
- - *"Quines novetats hi ha en dades obertes?"* → prompt `novetats`
177
- - *"Quins son els datasets mes consultats?"* → prompt `datasets_populars`
178
- - *"Explora'm el portal de la Generalitat"* → prompt `explorar_portal`
179
- - *"Quines dades obertes te Sabadell?"* → prompt `dades_municipi`
180
- - *"Quines dades en temps real hi ha?"* → prompt `datasets_temps_real`
181
- - *"Dona'm un resum de tots els portals"* → prompt `resum_portals`
149
+ - *"Quin es l'estat dels embassaments de Catalunya?"*
150
+ - *"Hi ha algun tren de FGC amb retard ara mateix?"*
151
+ - *"Quina es la poblacio de Catalunya? I l'atur?"*
152
+ - *"Analitza la qualitat de l'aire a Terrassa"*
153
+ - *"Compara Girona i Tarragona en dades obertes"*
154
+ - *"Quines dades obertes hi ha sobre educacio a Catalunya?"*
155
+ - *"Dona'm les ultimes dades de pressupostos de Reus"*
156
+ - *"Tenim informacio d'actuacions de bombers o del 112?"*
182
157
 
183
158
  ## Com funciona
184
159
 
@@ -224,6 +199,13 @@ Les contribucions son benvingudes! Per afegir un nou portal de dades obertes:
224
199
 
225
200
  ## Changelog
226
201
 
202
+ ### v0.1.0 (2026-04-14)
203
+ - Nou portal Idescat (Institut d'Estadistica de Catalunya) — 138 indicadors estadistics
204
+ - Client Idescat amb series temporals (poblacio, PIB, atur, inflacio, educacio, salut...)
205
+ - Millora ranking de cerca: datasets amb el terme al nom surten primer
206
+ - Instruccio multi-cerca a search_datasets per cobrir temes amplis
207
+ - Sinonims ampliats: emergencies (112, SEM, ambulancia, bombers, mossos)
208
+
227
209
  ### v0.0.17 (2026-04-14)
228
210
  - Simplificacio automatica de geometries GIS: centroide + bounding box en lloc de milers de coordenades
229
211
  - 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,22 +11,54 @@ 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
- const server = new McpServer({
15
- name: "opendata-cat",
16
- version: "0.0.19",
17
- });
14
+ import { queryIdescat } from "./clients/idescat.js";
15
+ const INSTRUCTIONS = `Servidor MCP de dades obertes de Catalunya. Pots consultar dades reals directament amb query_dataset si coneixes el dataset_id.
16
+
17
+ DATASETS DESTACATS (pots fer query_dataset directament sense cercar):
18
+ - generalitat:gn9e-3qhr → Embassaments: volum, % ple, per estació
19
+ - generalitat:i5n8-43cw → Estat de sequera per municipi
20
+ - generalitat:rmgc-ncpb → Accidents de trànsit amb morts o ferits greus
21
+ - generalitat:jq8m-d7cw → Incidents operatius gestionats pel CAT 112
22
+ - generalitat:mfqb-sbx4 → Trucades operatives gestionades pel CAT 112
23
+ - generalitat:g2ay-3vnj → Actuacions dels Bombers de la Generalitat
24
+ - generalitat:j6ii-t3w2 → Certificats d'eficiència energètica d'edificis
25
+ - fgc:vehicle-positions-gtfs_realtime → Posició GPS dels trens FGC en temps real
26
+ - fgc:alerts-gtfs_realtime → Alertes de servei FGC en temps real
27
+ - fgc:trip-updates-gtfs_realtime → Retards dels trens FGC en temps real
28
+ - idescat:m10328 → Població de Catalunya
29
+ - idescat:m10234 → Confiança empresarial
30
+ - barcelona:accidents-gu-bcn → Accidents gestionats per la Guàrdia Urbana BCN
31
+
32
+ DADES MUNICIPALS (filtra per NOM_ENS amb query_dataset):
33
+ - aoc:ge-ge-cost-efectiu-serveis-minhap → Cost dels serveis de +1.000 municipis
34
+ - aoc:ge-p-pressupostos-i-plantilles → Pressupostos i plantilles municipals
35
+ - aoc:ge-ge-endeutament → Endeutament municipal
36
+ - aoc:ge-p-liquidacions-per-programes-detallat → Liquidació pressupostos per programes
37
+ - aoc:ge-ge-termini-pagament-proveidors → Termini pagament a proveïdors
38
+
39
+ PORTALS: generalitat (Socrata), barcelona (CKAN), diba (REST), aoc (CKAN), reus (CKAN), girona (CKAN), fgc (Opendatasoft+GTFS-RT), idescat (API indicadors)
40
+ Usa search_datasets per temes que no siguin als destacats. Fes múltiples cerques amb termes diferents per cobrir temes amplis.`;
41
+ const server = new McpServer({ name: "opendata-cat", version: "0.1.1" }, { instructions: INSTRUCTIONS });
18
42
  // Tool 1: search_datasets
19
- 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à.", {
43
+ server.tool("search_datasets", "Cerca datasets per text lliure. Mira primer les instructions del servidor: molts datasets es poden consultar directament amb query_dataset sense cercar. Usa search_datasets només quan no saps quin dataset necessites.", {
20
44
  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'"),
45
+ portal: z.string().optional().describe("Filtrar per portal: 'generalitat', 'barcelona', 'diba', 'aoc', 'reus', 'girona', 'fgc', 'idescat'"),
22
46
  category: z.string().optional().describe("Filtrar per categoria"),
23
47
  limit: z.number().optional().default(20).describe("Nombre màxim de resultats (defecte: 20)"),
24
48
  }, async ({ query, portal, category, limit }) => {
25
49
  const result = await searchDatasets(query, portal, category, limit);
50
+ const queryableTypes = new Set(["socrata", "ckan", "opendatasoft", "idescat", "diba", "diba_cido"]);
51
+ const enriched = {
52
+ ...result,
53
+ items: result.items.map((item) => ({
54
+ ...item,
55
+ queryable: queryableTypes.has(item.api_type),
56
+ })),
57
+ };
26
58
  return {
27
59
  content: [{
28
60
  type: "text",
29
- text: JSON.stringify(result, null, 2),
61
+ text: JSON.stringify(enriched, null, 2),
30
62
  }],
31
63
  };
32
64
  });
@@ -51,8 +83,8 @@ server.tool("list_dataset_fields", "Llista els camps d'un dataset amb el seu nom
51
83
  return { content: [{ type: "text", text: JSON.stringify(dataset.fields, null, 2) }] };
52
84
  });
53
85
  // Tool 4: query_dataset
54
- server.tool("query_dataset", "Executa una consulta contra un dataset i retorna files de dades reals del portal origen.", {
55
- dataset_id: z.string().describe("ID del dataset a consultar"),
86
+ server.tool("query_dataset", "Consulta dades reals d'un dataset. Mira les instructions per dataset_ids destacats. Per dades municipals, usa filters: {\"NOM_ENS\": \"Ajuntament de X\"} amb els datasets aoc:ge-*.", {
87
+ dataset_id: z.string().describe("ID del dataset (ex: 'generalitat:gn9e-3qhr' per embassaments, 'aoc:ge-ge-cost-efectiu-serveis-minhap' per cost serveis municipal)"),
56
88
  filters: z.record(z.string(), z.string()).optional().describe("Filtres clau-valor (ex: {\"ciutat\": \"Barcelona\"})"),
57
89
  search: z.string().optional().describe("Cerca de text lliure dins el dataset"),
58
90
  limit: z.number().optional().default(20).describe("Files a retornar (defecte: 20, màxim: 100)"),
@@ -114,6 +146,20 @@ server.tool("query_dataset", "Executa una consulta contra un dataset i retorna f
114
146
  }
115
147
  results = data.records;
116
148
  }
149
+ else if (dataset.api_type === "idescat") {
150
+ const data = await queryIdescat(dataset.api_endpoint);
151
+ return {
152
+ content: [{
153
+ type: "text",
154
+ text: JSON.stringify({
155
+ dataset: dataset.name,
156
+ portal: "Idescat",
157
+ count: data.count,
158
+ data: data.indicators,
159
+ }, null, 2),
160
+ }],
161
+ };
162
+ }
117
163
  else {
118
164
  return {
119
165
  content: [{
@@ -148,6 +194,7 @@ server.tool("list_portals", "Llista els portals de dades obertes catalans dispon
148
194
  { id: "reus", name: "Ajuntament de Reus", url: "https://opendata.reus.cat", api: "CKAN" },
149
195
  { id: "girona", name: "Ajuntament de Girona", url: "https://www.girona.cat/opendata/", api: "CKAN" },
150
196
  { id: "fgc", name: "Ferrocarrils de la Generalitat de Catalunya", url: "https://dadesobertes.fgc.cat", api: "Opendatasoft" },
197
+ { id: "idescat", name: "Idescat (Institut d'Estadística de Catalunya)", url: "https://www.idescat.cat", api: "Idescat API" },
151
198
  ];
152
199
  const cats = await getCategories();
153
200
  const portalCounts = new Map(cats.portals.map((p) => [p.portal_id, p.total]));
@@ -471,7 +518,7 @@ async function main() {
471
518
  // Health check
472
519
  if (req.url === "/health") {
473
520
  res.writeHead(200, { "Content-Type": "application/json" });
474
- res.end(JSON.stringify({ status: "ok", name: "opendata-cat", version: "0.0.19" }));
521
+ res.end(JSON.stringify({ status: "ok", name: "opendata-cat", version: "0.1.1" }));
475
522
  return;
476
523
  }
477
524
  // MCP endpoint
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.0.19",
6
+ "version": "0.1.1",
7
7
  "description": "Servidor MCP per consultar les dades obertes públiques de Catalunya",
8
8
  "type": "module",
9
9
  "main": "dist/index.js",