@opendata.cat/mcp-server 0.1.0 → 0.1.2

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
@@ -142,45 +142,18 @@ Llista els portals disponibles amb el nombre de datasets de cadascun. No requere
142
142
 
143
143
  Llista totes les categories i temes de datasets disponibles amb comptadors per portal. Ideal per descobrir quins tipus de dades hi ha.
144
144
 
145
- ## Prompts disponibles
146
-
147
- Prompts predefinits que guien l'LLM pas a pas per fer analisis completes:
148
-
149
- | Prompt | Descripcio | Arguments |
150
- |--------|-----------|-----------|
151
- | `estat_embassaments` | Estat actual dels embassaments amb grafiques d'evolucio | — |
152
- | `trens_fgc_temps_real` | Retards, alertes i posicions dels trens FGC en temps real | — |
153
- | `qualitat_aire` | Analisi de la qualitat de l'aire amb comparativa OMS/UE | `lloc` (opcional) |
154
- | `accidents_transit` | Analisi d'accidents de transit amb punts negres i tendencies | `municipi` (opcional) |
155
- | `pressupostos_municipals` | Pressupostos municipals amb desglossament per partides | `municipi` (opcional) |
156
- | `compara_municipis` | Compara dos municipis en totes les dades disponibles | `municipi_a`, `municipi_b` |
157
- | `descobreix_dades` | Mapa complet de dades obertes sobre un tema | `tema` |
158
- | `analisi_bombers` | Actuacions dels Bombers: emergencies, distribucio, tendencies | `comarca` (opcional) |
159
- | `novetats` | Datasets actualitzats mes recentment | `portal` (opcional) |
160
- | `datasets_populars` | Datasets mes consultats pels usuaris | — |
161
- | `explorar_portal` | Guia completa d'un portal: categories, exemples, destacats | `portal` |
162
- | `dades_municipi` | Fitxa completa d'un municipi amb totes les dades disponibles | `municipi` |
163
- | `datasets_temps_real` | Datasets amb dades en temps real o actualitzacio frequent | — |
164
- | `resum_portals` | Visio panoramica de tots els portals de dades obertes | — |
165
-
166
145
  ## Exemples d'us
167
146
 
168
147
  Un cop configurat, pots fer preguntes al teu LLM com:
169
148
 
170
- - *"Quin es l'estat dels embassaments de Catalunya?"* → prompt `estat_embassaments`
171
- - *"Hi ha algun tren de FGC amb retard ara mateix?"* → prompt `trens_fgc_temps_real`
172
- - *"Analitza la qualitat de l'aire a Terrassa"* → prompt `qualitat_aire`
173
- - *"Fes unes grafiques amb l'evolucio dels accidents de transit a Barcelona"* → prompt `accidents_transit`
174
- - *"Compara Girona i Tarragona en dades obertes"* → prompt `compara_municipis`
175
- - *"Quines dades obertes hi ha sobre educacio a Catalunya?"* → prompt `descobreix_dades`
176
- - *"Dona'm les ultimes dades de pressupostos de Reus"* → prompt `pressupostos_municipals`
177
- - *"Analitza les actuacions dels Bombers al Valles"* → prompt `analisi_bombers`
178
- - *"Quines novetats hi ha en dades obertes?"* → prompt `novetats`
179
- - *"Quins son els datasets mes consultats?"* → prompt `datasets_populars`
180
- - *"Explora'm el portal de la Generalitat"* → prompt `explorar_portal`
181
- - *"Quines dades obertes te Sabadell?"* → prompt `dades_municipi`
182
- - *"Quines dades en temps real hi ha?"* → prompt `datasets_temps_real`
183
- - *"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?"*
184
157
 
185
158
  ## Com funciona
186
159
 
@@ -3,14 +3,23 @@
3
3
  * Consulta indicadors estadístics de Catalunya amb sèries temporals.
4
4
  */
5
5
  export async function queryIdescat(endpoint) {
6
- const resp = await fetch(endpoint);
6
+ // Extract indicator ID from endpoint (n=XXXXX or n=mXXXXX)
7
+ const idMatch = endpoint.match(/[?&]n=m?(\d+)/);
8
+ const targetId = idMatch ? `m${idMatch[1]}` : null;
9
+ // Fetch ALL indicators (max=200) and filter client-side
10
+ const allUrl = endpoint.replace(/&?n=m?\d+/, "").replace(/&?max=\d+/, "") + "&max=200";
11
+ const resp = await fetch(allUrl);
7
12
  if (!resp.ok)
8
13
  throw new Error(`Idescat error ${resp.status}: ${resp.statusText}`);
9
14
  const data = (await resp.json());
10
15
  const raw = data?.indicadors?.i;
11
16
  if (!raw)
12
17
  return { indicators: [], count: 0 };
13
- const items = Array.isArray(raw) ? raw : [raw];
18
+ let items = Array.isArray(raw) ? raw : [raw];
19
+ // Filter by target indicator if specified
20
+ if (targetId) {
21
+ items = items.filter((item) => item.id === targetId);
22
+ }
14
23
  const indicators = items
15
24
  .filter((item) => item.id)
16
25
  .map((item) => {
package/dist/index.js CHANGED
@@ -12,22 +12,59 @@ import { queryCido } from "./clients/cido.js";
12
12
  import { queryOpendatasoft } from "./clients/opendatasoft.js";
13
13
  import { decodeGtfsRt } from "./clients/gtfsrt.js";
14
14
  import { queryIdescat } from "./clients/idescat.js";
15
- const server = new McpServer({
16
- name: "opendata-cat",
17
- version: "0.1.0",
18
- });
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: camps dia, estaci, volum_embassat, percentatge_volum_embassat, nivell_absolut
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
+
41
+ NOTES:
42
+ - Socrata: pots filtrar per qualsevol camp. Ex: filters: {"estaci": "Embassament de Sau"}
43
+ - CKAN AOC municipals: filtra per NOM_ENS (ex: "Ajuntament de Tiana"). Camps sovint en castellà.
44
+ - Idescat: cada dataset_id retorna 1 indicador específic amb valor, unitat, període i sèrie temporal.
45
+ - FGC GTFS-RT: decodificat automàticament. vehicle-positions dona GPS, alerts dona alertes en català.
46
+ - Usa search_datasets només quan no saps quin dataset necessites.`;
47
+ const server = new McpServer({ name: "opendata-cat", version: "0.1.2" }, { instructions: INSTRUCTIONS });
19
48
  // Tool 1: search_datasets
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à.", {
49
+ 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.", {
21
50
  query: z.string().describe("Text de cerca (ex: 'qualitat aire', 'pressupostos')"),
22
51
  portal: z.string().optional().describe("Filtrar per portal: 'generalitat', 'barcelona', 'diba', 'aoc', 'reus', 'girona', 'fgc', 'idescat'"),
23
52
  category: z.string().optional().describe("Filtrar per categoria"),
24
53
  limit: z.number().optional().default(20).describe("Nombre màxim de resultats (defecte: 20)"),
25
54
  }, async ({ query, portal, category, limit }) => {
26
55
  const result = await searchDatasets(query, portal, category, limit);
56
+ const queryableTypes = new Set(["socrata", "ckan", "opendatasoft", "idescat", "diba", "diba_cido"]);
57
+ const enriched = {
58
+ ...result,
59
+ items: result.items.map((item) => ({
60
+ ...item,
61
+ queryable: queryableTypes.has(item.api_type),
62
+ })),
63
+ };
27
64
  return {
28
65
  content: [{
29
66
  type: "text",
30
- text: JSON.stringify(result, null, 2),
67
+ text: JSON.stringify(enriched, null, 2),
31
68
  }],
32
69
  };
33
70
  });
@@ -52,8 +89,8 @@ server.tool("list_dataset_fields", "Llista els camps d'un dataset amb el seu nom
52
89
  return { content: [{ type: "text", text: JSON.stringify(dataset.fields, null, 2) }] };
53
90
  });
54
91
  // Tool 4: query_dataset
55
- server.tool("query_dataset", "Executa una consulta contra un dataset i retorna files de dades reals del portal origen.", {
56
- dataset_id: z.string().describe("ID del dataset a consultar"),
92
+ 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-*.", {
93
+ 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)"),
57
94
  filters: z.record(z.string(), z.string()).optional().describe("Filtres clau-valor (ex: {\"ciutat\": \"Barcelona\"})"),
58
95
  search: z.string().optional().describe("Cerca de text lliure dins el dataset"),
59
96
  limit: z.number().optional().default(20).describe("Files a retornar (defecte: 20, màxim: 100)"),
@@ -487,7 +524,7 @@ async function main() {
487
524
  // Health check
488
525
  if (req.url === "/health") {
489
526
  res.writeHead(200, { "Content-Type": "application/json" });
490
- res.end(JSON.stringify({ status: "ok", name: "opendata-cat", version: "0.1.0" }));
527
+ res.end(JSON.stringify({ status: "ok", name: "opendata-cat", version: "0.1.2" }));
491
528
  return;
492
529
  }
493
530
  // MCP endpoint
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.0",
6
+ "version": "0.1.2",
7
7
  "description": "Servidor MCP per consultar les dades obertes públiques de Catalunya",
8
8
  "type": "module",
9
9
  "main": "dist/index.js",