@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 +19 -37
- package/dist/clients/idescat.d.ts +8 -0
- package/dist/clients/idescat.js +30 -0
- package/dist/index.js +57 -10
- package/package.json +1 -1
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
|
|
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?"*
|
|
169
|
-
- *"Hi ha algun tren de FGC amb retard ara mateix?"*
|
|
170
|
-
- *"
|
|
171
|
-
- *"
|
|
172
|
-
- *"Compara Girona i Tarragona en dades obertes"*
|
|
173
|
-
- *"Quines dades obertes hi ha sobre educacio a Catalunya?"*
|
|
174
|
-
- *"Dona'm les ultimes dades de pressupostos de Reus"*
|
|
175
|
-
- *"
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
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(
|
|
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", "
|
|
55
|
-
dataset_id: z.string().describe("ID del dataset
|
|
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.
|
|
521
|
+
res.end(JSON.stringify({ status: "ok", name: "opendata-cat", version: "0.1.1" }));
|
|
475
522
|
return;
|
|
476
523
|
}
|
|
477
524
|
// MCP endpoint
|