@opendata.cat/mcp-server 0.0.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 +155 -0
- package/dist/api.d.ts +49 -0
- package/dist/api.js +30 -0
- package/dist/clients/ckan.d.ts +4 -0
- package/dist/clients/ckan.js +20 -0
- package/dist/clients/socrata.d.ts +1 -0
- package/dist/clients/socrata.js +15 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +106 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="banner.png" alt="opendata.cat MCP — Connecta el teu LLM amb les dades obertes de Catalunya" width="100%">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# Opendata.cat MCP Server
|
|
6
|
+
|
|
7
|
+
Servidor [MCP](https://modelcontextprotocol.io/) (Model Context Protocol) que connecta els models de llenguatge (Claude, ChatGPT, Gemini...) amb les **dades obertes públiques de Catalunya**. Cerca datasets, explora metadades i consulta dades reals de la Generalitat, l'Ajuntament de Barcelona i la Diputació de Barcelona directament des del teu assistent d'IA.
|
|
8
|
+
|
|
9
|
+
Un projecte d'**[opendata.cat](https://opendata.cat)** — associació sense ànim de lucre fundada el 2012 que promou la transparència, la difusió i l'estandardització de les dades obertes a Catalunya. Organitza formacions, conferències i desenvolupa plataformes per facilitar l'accés a la informació pública.
|
|
10
|
+
|
|
11
|
+
## Portals disponibles
|
|
12
|
+
|
|
13
|
+
| Portal | Datasets | API | Dades |
|
|
14
|
+
|--------|----------|-----|-------|
|
|
15
|
+
| [Generalitat de Catalunya](https://analisi.transparenciacatalunya.cat) | ~1.480 | Socrata | Medi ambient, salut, educació, economia, transport... |
|
|
16
|
+
| [Ajuntament de Barcelona](https://opendata-ajuntament.barcelona.cat) | ~1.036 | CKAN | Urbanisme, mobilitat, cultura, demografia, pressupostos... |
|
|
17
|
+
| [Diputació de Barcelona](https://dadesobertes.diba.cat) | ~124 | CKAN | Municipis, equipaments, patrimoni, energia, territori... |
|
|
18
|
+
|
|
19
|
+
**+2.600 datasets** actualitzats automàticament cada setmana.
|
|
20
|
+
|
|
21
|
+
## Instal·lació ràpida
|
|
22
|
+
|
|
23
|
+
### Claude Desktop
|
|
24
|
+
|
|
25
|
+
Afegeix al fitxer de configuració (`~/Library/Application Support/Claude/claude_desktop_config.json` a macOS o `%APPDATA%\Claude\claude_desktop_config.json` a Windows):
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"opendata-cat": {
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": ["-y", "opendata-cat-mcp-server"]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Claude Code (CLI)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
claude mcp add opendata-cat -- npx -y opendata-cat-mcp-server
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### VS Code / Cursor
|
|
45
|
+
|
|
46
|
+
Afegeix al fitxer `.vscode/mcp.json` del teu projecte:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"servers": {
|
|
51
|
+
"opendata-cat": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["-y", "opendata-cat-mcp-server"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Tools disponibles
|
|
60
|
+
|
|
61
|
+
| Tool | Descripció |
|
|
62
|
+
|------|-----------|
|
|
63
|
+
| `search_datasets` | Cerca datasets per text lliure al catàleg de +2.600 datasets |
|
|
64
|
+
| `get_dataset_info` | Retorna metadades completes: camps, tipus, llicència, endpoint |
|
|
65
|
+
| `list_dataset_fields` | Llista els camps d'un dataset amb nom, tipus i descripció |
|
|
66
|
+
| `query_dataset` | Consulta dades reals directament al portal origen |
|
|
67
|
+
| `list_portals` | Llista els portals disponibles amb estadístiques |
|
|
68
|
+
| `list_categories` | Llista categories i temes disponibles amb comptadors |
|
|
69
|
+
|
|
70
|
+
### search_datasets
|
|
71
|
+
|
|
72
|
+
Cerca datasets per text lliure.
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
query: "qualitat aire"
|
|
76
|
+
portal: "barcelona" # opcional: generalitat, barcelona, diba
|
|
77
|
+
category: "Medi Ambient" # opcional
|
|
78
|
+
limit: 20 # opcional (defecte: 20)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### get_dataset_info
|
|
82
|
+
|
|
83
|
+
Retorna totes les metadades d'un dataset.
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
dataset_id: "generalitat:gn9e-3qhr"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### list_dataset_fields
|
|
90
|
+
|
|
91
|
+
Llista els camps d'un dataset amb nom, tipus i descripció.
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
dataset_id: "generalitat:gn9e-3qhr"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### query_dataset
|
|
98
|
+
|
|
99
|
+
Executa una consulta directament contra el portal origen i retorna dades reals.
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
dataset_id: "generalitat:gn9e-3qhr"
|
|
103
|
+
filters: {"estaci": "Sau"} # opcional
|
|
104
|
+
search: "embassament" # opcional
|
|
105
|
+
limit: 20 # opcional (defecte: 20, màx: 100)
|
|
106
|
+
offset: 0 # opcional
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### list_portals
|
|
110
|
+
|
|
111
|
+
Llista els portals disponibles amb el nombre de datasets de cadascun. No requereix paràmetres.
|
|
112
|
+
|
|
113
|
+
### list_categories
|
|
114
|
+
|
|
115
|
+
Llista totes les categories i temes de datasets disponibles amb comptadors per portal. Ideal per descobrir quins tipus de dades hi ha.
|
|
116
|
+
|
|
117
|
+
## Exemples d'ús
|
|
118
|
+
|
|
119
|
+
Un cop configurat, pots fer preguntes al teu LLM com:
|
|
120
|
+
|
|
121
|
+
- *"Quins datasets hi ha sobre mobilitat a Barcelona?"*
|
|
122
|
+
- *"Mostra'm les dades de qualitat de l'aire d'ahir"*
|
|
123
|
+
- *"Quants equipaments culturals té Girona?"*
|
|
124
|
+
- *"Dona'm les últimes dades de pressupostos municipals"*
|
|
125
|
+
- *"Quin és l'estat dels embassaments de Catalunya?"*
|
|
126
|
+
- *"Compara les dades de transport públic entre Barcelona i la Diputació"*
|
|
127
|
+
- *"Quines dades obertes hi ha sobre educació a Catalunya?"*
|
|
128
|
+
|
|
129
|
+
## Com funciona
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
Usuari → LLM → MCP opendata.cat → API opendata.cat (catàleg)
|
|
133
|
+
→ Portal origen (dades reals)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
1. L'MCP consulta l'[API d'opendata.cat](https://opendata.cat) per descobrir datasets rellevants
|
|
137
|
+
2. Quan l'usuari vol dades concretes, l'MCP fa la consulta directament al portal origen (Socrata o CKAN)
|
|
138
|
+
3. Les dades tornen a l'LLM, que les interpreta i presenta a l'usuari
|
|
139
|
+
|
|
140
|
+
No emmagatzema ni fa de proxy de dades. Cada consulta va directament a la font oficial.
|
|
141
|
+
|
|
142
|
+
## Sobre opendata.cat
|
|
143
|
+
|
|
144
|
+
[opendata.cat](https://opendata.cat) és una associació catalana sense ànim de lucre fundada el 2012 (registre 47468) dedicada a promoure la transparència i l'accés a la informació pública. Treballa en tres eixos: **estandardització** de formats i protocols, **formació** especialitzada per a professionals i administracions, i **col·laboració** público-privada per a l'obertura de dades.
|
|
145
|
+
|
|
146
|
+
## Contribuir
|
|
147
|
+
|
|
148
|
+
Les contribucions són benvingudes! Per afegir un nou portal de dades obertes:
|
|
149
|
+
|
|
150
|
+
1. Obre una [issue](https://github.com/xaviviro/Opendata.cat-MCP-Server/issues) amb la URL del portal i el tipus d'API
|
|
151
|
+
2. O envia un pull request
|
|
152
|
+
|
|
153
|
+
## Llicència
|
|
154
|
+
|
|
155
|
+
MIT
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface DatasetSummary {
|
|
2
|
+
dataset_id: string;
|
|
3
|
+
portal_id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
category: string;
|
|
7
|
+
formats: string[];
|
|
8
|
+
api_type: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DatasetDetail {
|
|
11
|
+
dataset_id: string;
|
|
12
|
+
portal_id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
description: string;
|
|
15
|
+
category: string;
|
|
16
|
+
tags: string[];
|
|
17
|
+
api_type: "socrata" | "ckan";
|
|
18
|
+
api_endpoint: string;
|
|
19
|
+
formats: string[];
|
|
20
|
+
fields: {
|
|
21
|
+
name: string;
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
}[];
|
|
25
|
+
row_count: number | null;
|
|
26
|
+
last_updated: string;
|
|
27
|
+
license: string;
|
|
28
|
+
}
|
|
29
|
+
export interface SearchResult {
|
|
30
|
+
items: DatasetSummary[];
|
|
31
|
+
total: number;
|
|
32
|
+
limit: number;
|
|
33
|
+
offset: number;
|
|
34
|
+
}
|
|
35
|
+
export declare function searchDatasets(query: string, portal?: string, category?: string, limit?: number, offset?: number): Promise<SearchResult>;
|
|
36
|
+
export interface CategoriesResult {
|
|
37
|
+
total_datasets: number;
|
|
38
|
+
portals: {
|
|
39
|
+
portal_id: string;
|
|
40
|
+
total: number;
|
|
41
|
+
}[];
|
|
42
|
+
categories: {
|
|
43
|
+
portal_id: string;
|
|
44
|
+
category: string;
|
|
45
|
+
total: number;
|
|
46
|
+
}[];
|
|
47
|
+
}
|
|
48
|
+
export declare function getCategories(): Promise<CategoriesResult>;
|
|
49
|
+
export declare function getDatasetInfo(datasetId: string): Promise<DatasetDetail | null>;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const API_BASE = "https://opendata.cat/api";
|
|
2
|
+
export async function searchDatasets(query, portal, category, limit = 20, offset = 0) {
|
|
3
|
+
const params = new URLSearchParams();
|
|
4
|
+
if (query)
|
|
5
|
+
params.set("q", query);
|
|
6
|
+
if (portal)
|
|
7
|
+
params.set("portal", portal);
|
|
8
|
+
if (category)
|
|
9
|
+
params.set("category", category);
|
|
10
|
+
params.set("limit", String(limit));
|
|
11
|
+
params.set("offset", String(offset));
|
|
12
|
+
const resp = await fetch(`${API_BASE}/datasets.php?${params}`);
|
|
13
|
+
if (!resp.ok)
|
|
14
|
+
throw new Error(`API error ${resp.status}`);
|
|
15
|
+
return (await resp.json());
|
|
16
|
+
}
|
|
17
|
+
export async function getCategories() {
|
|
18
|
+
const resp = await fetch(`${API_BASE}/categories.php`);
|
|
19
|
+
if (!resp.ok)
|
|
20
|
+
throw new Error(`API error ${resp.status}`);
|
|
21
|
+
return (await resp.json());
|
|
22
|
+
}
|
|
23
|
+
export async function getDatasetInfo(datasetId) {
|
|
24
|
+
const resp = await fetch(`${API_BASE}/dataset.php?id=${encodeURIComponent(datasetId)}`);
|
|
25
|
+
if (resp.status === 404)
|
|
26
|
+
return null;
|
|
27
|
+
if (!resp.ok)
|
|
28
|
+
throw new Error(`API error ${resp.status}`);
|
|
29
|
+
return (await resp.json());
|
|
30
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export async function queryCkan(endpoint, filters, search, limit = 20, offset = 0) {
|
|
2
|
+
const url = new URL(endpoint);
|
|
3
|
+
if (filters && Object.keys(filters).length > 0) {
|
|
4
|
+
url.searchParams.set("filters", JSON.stringify(filters));
|
|
5
|
+
}
|
|
6
|
+
if (search)
|
|
7
|
+
url.searchParams.set("q", search);
|
|
8
|
+
url.searchParams.set("limit", String(Math.min(limit, 100)));
|
|
9
|
+
url.searchParams.set("offset", String(offset));
|
|
10
|
+
const resp = await fetch(url.toString());
|
|
11
|
+
if (!resp.ok)
|
|
12
|
+
throw new Error(`CKAN error ${resp.status}: ${resp.statusText}`);
|
|
13
|
+
const data = await resp.json();
|
|
14
|
+
if (!data.success)
|
|
15
|
+
throw new Error(`CKAN error: ${JSON.stringify(data.error)}`);
|
|
16
|
+
return {
|
|
17
|
+
records: data.result.records ?? [],
|
|
18
|
+
total: data.result.total ?? 0,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function querySocrata(endpoint: string, filters?: Record<string, string>, search?: string, limit?: number, offset?: number): Promise<Record<string, unknown>[]>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export async function querySocrata(endpoint, filters, search, limit = 20, offset = 0) {
|
|
2
|
+
const url = new URL(endpoint);
|
|
3
|
+
if (filters && Object.keys(filters).length > 0) {
|
|
4
|
+
const clauses = Object.entries(filters).map(([key, value]) => `${key}='${value.replace(/'/g, "''")}'`);
|
|
5
|
+
url.searchParams.set("$where", clauses.join(" AND "));
|
|
6
|
+
}
|
|
7
|
+
if (search)
|
|
8
|
+
url.searchParams.set("$q", search);
|
|
9
|
+
url.searchParams.set("$limit", String(Math.min(limit, 100)));
|
|
10
|
+
url.searchParams.set("$offset", String(offset));
|
|
11
|
+
const resp = await fetch(url.toString());
|
|
12
|
+
if (!resp.ok)
|
|
13
|
+
throw new Error(`Socrata error ${resp.status}: ${resp.statusText}`);
|
|
14
|
+
return (await resp.json());
|
|
15
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { searchDatasets, getDatasetInfo, getCategories } from "./api.js";
|
|
6
|
+
import { querySocrata } from "./clients/socrata.js";
|
|
7
|
+
import { queryCkan } from "./clients/ckan.js";
|
|
8
|
+
const server = new McpServer({
|
|
9
|
+
name: "opendata-cat",
|
|
10
|
+
version: "0.0.1",
|
|
11
|
+
});
|
|
12
|
+
// Tool 1: search_datasets
|
|
13
|
+
server.tool("search_datasets", "Cerca datasets de dades obertes catalanes per text lliure. Retorna nom, descripció, portal i formats.", {
|
|
14
|
+
query: z.string().describe("Text de cerca (ex: 'qualitat aire', 'pressupostos')"),
|
|
15
|
+
portal: z.string().optional().describe("Filtrar per portal: 'generalitat', 'barcelona' o 'diba'"),
|
|
16
|
+
category: z.string().optional().describe("Filtrar per categoria"),
|
|
17
|
+
limit: z.number().optional().default(20).describe("Nombre màxim de resultats (defecte: 20)"),
|
|
18
|
+
}, async ({ query, portal, category, limit }) => {
|
|
19
|
+
const result = await searchDatasets(query, portal, category, limit);
|
|
20
|
+
return {
|
|
21
|
+
content: [{
|
|
22
|
+
type: "text",
|
|
23
|
+
text: JSON.stringify(result, null, 2),
|
|
24
|
+
}],
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
// Tool 2: get_dataset_info
|
|
28
|
+
server.tool("get_dataset_info", "Retorna totes les metadades d'un dataset: camps, tipus, descripció, endpoint API, llicència.", {
|
|
29
|
+
dataset_id: z.string().describe("ID del dataset (ex: 'generalitat:gn9e-3qhr')"),
|
|
30
|
+
}, async ({ dataset_id }) => {
|
|
31
|
+
const dataset = await getDatasetInfo(dataset_id);
|
|
32
|
+
if (!dataset) {
|
|
33
|
+
return { content: [{ type: "text", text: `Dataset '${dataset_id}' no trobat.` }] };
|
|
34
|
+
}
|
|
35
|
+
return { content: [{ type: "text", text: JSON.stringify(dataset, null, 2) }] };
|
|
36
|
+
});
|
|
37
|
+
// Tool 3: list_dataset_fields
|
|
38
|
+
server.tool("list_dataset_fields", "Llista els camps d'un dataset amb el seu nom, tipus i descripció.", {
|
|
39
|
+
dataset_id: z.string().describe("ID del dataset"),
|
|
40
|
+
}, async ({ dataset_id }) => {
|
|
41
|
+
const dataset = await getDatasetInfo(dataset_id);
|
|
42
|
+
if (!dataset) {
|
|
43
|
+
return { content: [{ type: "text", text: `Dataset '${dataset_id}' no trobat.` }] };
|
|
44
|
+
}
|
|
45
|
+
return { content: [{ type: "text", text: JSON.stringify(dataset.fields, null, 2) }] };
|
|
46
|
+
});
|
|
47
|
+
// Tool 4: query_dataset
|
|
48
|
+
server.tool("query_dataset", "Executa una consulta contra un dataset i retorna files de dades reals del portal origen.", {
|
|
49
|
+
dataset_id: z.string().describe("ID del dataset a consultar"),
|
|
50
|
+
filters: z.record(z.string(), z.string()).optional().describe("Filtres clau-valor (ex: {\"ciutat\": \"Barcelona\"})"),
|
|
51
|
+
search: z.string().optional().describe("Cerca de text lliure dins el dataset"),
|
|
52
|
+
limit: z.number().optional().default(20).describe("Files a retornar (defecte: 20, màxim: 100)"),
|
|
53
|
+
offset: z.number().optional().default(0).describe("Desplaçament per paginació"),
|
|
54
|
+
}, async ({ dataset_id, filters, search, limit, offset }) => {
|
|
55
|
+
const dataset = await getDatasetInfo(dataset_id);
|
|
56
|
+
if (!dataset) {
|
|
57
|
+
return { content: [{ type: "text", text: `Dataset '${dataset_id}' no trobat.` }] };
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
let results;
|
|
61
|
+
if (dataset.api_type === "socrata") {
|
|
62
|
+
results = await querySocrata(dataset.api_endpoint, filters, search, limit, offset);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const data = await queryCkan(dataset.api_endpoint, filters, search, limit, offset);
|
|
66
|
+
results = data.records;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
content: [{
|
|
70
|
+
type: "text",
|
|
71
|
+
text: JSON.stringify({ dataset: dataset.name, count: results.length, data: results }, null, 2),
|
|
72
|
+
}],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
return {
|
|
77
|
+
content: [{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: `Error consultant ${dataset.name}: ${err instanceof Error ? err.message : String(err)}`,
|
|
80
|
+
}],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
// Tool 5: list_portals
|
|
85
|
+
server.tool("list_portals", "Llista els portals de dades obertes catalans disponibles amb estadístiques.", {}, async () => {
|
|
86
|
+
const portals = [
|
|
87
|
+
{ id: "generalitat", name: "Generalitat de Catalunya", url: "https://analisi.transparenciacatalunya.cat", api_type: "socrata" },
|
|
88
|
+
{ id: "barcelona", name: "Ajuntament de Barcelona", url: "https://opendata-ajuntament.barcelona.cat", api_type: "ckan" },
|
|
89
|
+
{ id: "diba", name: "Diputació de Barcelona", url: "https://dadesobertes.diba.cat", api_type: "ckan" },
|
|
90
|
+
];
|
|
91
|
+
const counts = await Promise.all(portals.map(async (p) => {
|
|
92
|
+
const result = await searchDatasets("", p.id, undefined, 1);
|
|
93
|
+
return { ...p, dataset_count: result.total };
|
|
94
|
+
}));
|
|
95
|
+
return { content: [{ type: "text", text: JSON.stringify(counts, null, 2) }] };
|
|
96
|
+
});
|
|
97
|
+
// Tool 6: list_categories
|
|
98
|
+
server.tool("list_categories", "Llista totes les categories i temes de datasets disponibles amb comptadors per portal. Útil per saber quins tipus de dades hi ha.", {}, async () => {
|
|
99
|
+
const cats = await getCategories();
|
|
100
|
+
return { content: [{ type: "text", text: JSON.stringify(cats, null, 2) }] };
|
|
101
|
+
});
|
|
102
|
+
async function main() {
|
|
103
|
+
const transport = new StdioServerTransport();
|
|
104
|
+
await server.connect(transport);
|
|
105
|
+
}
|
|
106
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opendata.cat/mcp-server",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"version": "0.0.1",
|
|
7
|
+
"description": "Servidor MCP per consultar les dades obertes públiques de Catalunya",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"bin": {
|
|
11
|
+
"opendata-cat-mcp": "dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"start": "node dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist/"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"mcp",
|
|
22
|
+
"opendata",
|
|
23
|
+
"catalunya",
|
|
24
|
+
"dades-obertes"
|
|
25
|
+
],
|
|
26
|
+
"author": "opendata.cat",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/xaviviro/Opendata.cat-MCP-Server.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/xaviviro/Opendata.cat-MCP-Server/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/xaviviro/Opendata.cat-MCP-Server#readme",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
38
|
+
"zod": "^4.3.6"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^25.6.0",
|
|
42
|
+
"typescript": "^6.0.2"
|
|
43
|
+
}
|
|
44
|
+
}
|