@aborruso/ckan-mcp-server 0.4.14 โ 0.4.15
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/EXAMPLES.md +18 -0
- package/LOG.md +31 -0
- package/README.md +7 -3
- package/dist/index.js +160 -19
- package/openspec/changes/add-mqa-quality-tool/proposal.md +21 -0
- package/openspec/changes/add-mqa-quality-tool/specs/ckan-quality/spec.md +71 -0
- package/openspec/changes/add-mqa-quality-tool/tasks.md +29 -0
- package/package.json +1 -1
package/EXAMPLES.md
CHANGED
|
@@ -151,6 +151,24 @@ ckan_package_search({
|
|
|
151
151
|
})
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
+
### Get MQA quality metrics for a dataset
|
|
155
|
+
```typescript
|
|
156
|
+
ckan_get_mqa_quality({
|
|
157
|
+
server_url: "https://www.dati.gov.it/opendata",
|
|
158
|
+
dataset_id: "332be8b7-89b9-4dfe-a252-7fccd3efda76",
|
|
159
|
+
response_format: "markdown"
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Returns quality score and detailed metrics from data.europa.eu MQA (Metadata Quality Assurance) system:
|
|
164
|
+
- Overall score (max 405 points)
|
|
165
|
+
- Accessibility (URL status, download availability)
|
|
166
|
+
- Reusability (license, contact point, publisher)
|
|
167
|
+
- Interoperability (format, media type)
|
|
168
|
+
- Findability (keywords, category, spatial/temporal coverage)
|
|
169
|
+
|
|
170
|
+
**Note**: Only works with dati.gov.it datasets. Uses the `identifier` field (or falls back to `name`) to query the European MQA API.
|
|
171
|
+
|
|
154
172
|
## USA Examples - data.gov
|
|
155
173
|
|
|
156
174
|
### Search government datasets
|
package/LOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# LOG
|
|
2
2
|
|
|
3
|
+
## 2026-01-23
|
|
4
|
+
|
|
5
|
+
### MQA Quality Metrics Tool
|
|
6
|
+
|
|
7
|
+
- **Feature**: Added `ckan_get_mqa_quality` tool for retrieving quality metrics from data.europa.eu MQA API
|
|
8
|
+
- **Scope**: Only works with dati.gov.it datasets (server validation enforced)
|
|
9
|
+
- **Data source**: Queries https://data.europa.eu/api/mqa/cache/datasets/{identifier}
|
|
10
|
+
- **Identifier logic**: Uses `identifier` field from CKAN metadata, falls back to `name` if identifier is empty
|
|
11
|
+
- **Metrics returned**:
|
|
12
|
+
- Overall score (max 405 points)
|
|
13
|
+
- Accessibility (URL status, download availability)
|
|
14
|
+
- Reusability (license, contact point, publisher)
|
|
15
|
+
- Interoperability (format, media type)
|
|
16
|
+
- Findability (keywords, category, spatial/temporal coverage)
|
|
17
|
+
- **Output formats**: Markdown (default, human-readable) or JSON (structured data)
|
|
18
|
+
- **Error handling**: Dataset not found, MQA API unavailable, invalid server URL
|
|
19
|
+
- **Tests**: +11 integration tests (212 total, all passing)
|
|
20
|
+
- Server validation (www/non-www dati.gov.it URLs)
|
|
21
|
+
- Quality retrieval with identifier
|
|
22
|
+
- Fallback to name field
|
|
23
|
+
- Error scenarios (404, network errors)
|
|
24
|
+
- Markdown formatting (complete/partial data, availability indicators)
|
|
25
|
+
- **Documentation**: README.md (new Quality Metrics section), EXAMPLES.md (usage example with expected metrics)
|
|
26
|
+
- **Files**:
|
|
27
|
+
- `src/tools/quality.ts` (new, 194 lines)
|
|
28
|
+
- `src/server.ts` (register quality tools)
|
|
29
|
+
- `tests/integration/quality.test.ts` (new, 11 tests)
|
|
30
|
+
- `tests/fixtures/responses/mqa-quality-success.json` (new)
|
|
31
|
+
- `tests/fixtures/responses/package-show-{with,without}-identifier.json` (new)
|
|
32
|
+
- **OpenSpec**: Proposal in `openspec/changes/add-mqa-quality-tool/` (4 requirements, 11 scenarios)
|
|
33
|
+
|
|
3
34
|
## 2026-01-22
|
|
4
35
|
|
|
5
36
|
### Date Query Auto-Conversion (v0.4.14)
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ MCP (Model Context Protocol) server for interacting with CKAN-based open data po
|
|
|
17
17
|
- โก Pagination and faceting support
|
|
18
18
|
- ๐ MCP Resource Templates for direct data access
|
|
19
19
|
- ๐งญ Guided MCP prompts for common workflows
|
|
20
|
-
- ๐งช Test suite with
|
|
20
|
+
- ๐งช Test suite with 212 tests (100% passing)
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -45,7 +45,7 @@ npm install
|
|
|
45
45
|
# Build with esbuild (fast, ~4ms)
|
|
46
46
|
npm run build
|
|
47
47
|
|
|
48
|
-
# Run tests (
|
|
48
|
+
# Run tests (212 tests)
|
|
49
49
|
npm test
|
|
50
50
|
```
|
|
51
51
|
|
|
@@ -209,6 +209,10 @@ These guides are based on a public demo server, which has a limit of 100,000 cal
|
|
|
209
209
|
- **ckan_group_show**: Show group details
|
|
210
210
|
- **ckan_group_search**: Search groups by name
|
|
211
211
|
|
|
212
|
+
### Quality Metrics
|
|
213
|
+
|
|
214
|
+
- **ckan_get_mqa_quality**: Get MQA quality score and metrics for dati.gov.it datasets (accessibility, reusability, interoperability, findability)
|
|
215
|
+
|
|
212
216
|
### Utilities
|
|
213
217
|
|
|
214
218
|
- **ckan_status_show**: Verify server status
|
|
@@ -566,7 +570,7 @@ ckan-mcp-server/
|
|
|
566
570
|
โ โโโ transport/
|
|
567
571
|
โ โโโ stdio.ts # Stdio transport
|
|
568
572
|
โ โโโ http.ts # HTTP transport
|
|
569
|
-
โโโ tests/ # Test suite (
|
|
573
|
+
โโโ tests/ # Test suite (212 tests)
|
|
570
574
|
โโโ dist/ # Compiled files (generated)
|
|
571
575
|
โโโ package.json
|
|
572
576
|
โโโ README.md
|
package/dist/index.js
CHANGED
|
@@ -2101,6 +2101,146 @@ Returns:
|
|
|
2101
2101
|
);
|
|
2102
2102
|
}
|
|
2103
2103
|
|
|
2104
|
+
// src/tools/quality.ts
|
|
2105
|
+
import { z as z8 } from "zod";
|
|
2106
|
+
import axios2 from "axios";
|
|
2107
|
+
var MQA_API_BASE = "https://data.europa.eu/api/mqa/cache/datasets";
|
|
2108
|
+
var ALLOWED_SERVER_PATTERNS = [
|
|
2109
|
+
/^https?:\/\/(www\.)?dati\.gov\.it/i
|
|
2110
|
+
];
|
|
2111
|
+
function isValidMqaServer(serverUrl) {
|
|
2112
|
+
return ALLOWED_SERVER_PATTERNS.some((pattern) => pattern.test(serverUrl));
|
|
2113
|
+
}
|
|
2114
|
+
async function getMqaQuality(serverUrl, datasetId) {
|
|
2115
|
+
const dataset = await makeCkanRequest(
|
|
2116
|
+
serverUrl,
|
|
2117
|
+
"package_show",
|
|
2118
|
+
{ id: datasetId }
|
|
2119
|
+
);
|
|
2120
|
+
const europeanId = dataset.identifier || dataset.name;
|
|
2121
|
+
const mqaUrl = `${MQA_API_BASE}/${europeanId}`;
|
|
2122
|
+
try {
|
|
2123
|
+
const response = await axios2.get(mqaUrl, {
|
|
2124
|
+
timeout: 3e4,
|
|
2125
|
+
headers: {
|
|
2126
|
+
"User-Agent": "CKAN-MCP-Server/1.0"
|
|
2127
|
+
}
|
|
2128
|
+
});
|
|
2129
|
+
return response.data;
|
|
2130
|
+
} catch (error) {
|
|
2131
|
+
if (axios2.isAxiosError(error)) {
|
|
2132
|
+
if (error.response?.status === 404) {
|
|
2133
|
+
throw new Error(`Quality metrics not found for dataset '${europeanId}' on data.europa.eu`);
|
|
2134
|
+
}
|
|
2135
|
+
throw new Error(`MQA API error: ${error.message}`);
|
|
2136
|
+
}
|
|
2137
|
+
throw error;
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
function formatQualityMarkdown(data, datasetId) {
|
|
2141
|
+
const lines = [];
|
|
2142
|
+
lines.push(`# Quality Metrics for Dataset: ${datasetId}`);
|
|
2143
|
+
lines.push("");
|
|
2144
|
+
if (data.info?.score !== void 0) {
|
|
2145
|
+
lines.push(`**Overall Score**: ${data.info.score}/405`);
|
|
2146
|
+
lines.push("");
|
|
2147
|
+
}
|
|
2148
|
+
if (data.accessibility) {
|
|
2149
|
+
lines.push("## Accessibility");
|
|
2150
|
+
if (data.accessibility.accessUrl !== void 0) {
|
|
2151
|
+
lines.push(`- Access URL: ${data.accessibility.accessUrl.available ? "\u2713" : "\u2717"} Available`);
|
|
2152
|
+
}
|
|
2153
|
+
if (data.accessibility.downloadUrl !== void 0) {
|
|
2154
|
+
lines.push(`- Download URL: ${data.accessibility.downloadUrl.available ? "\u2713" : "\u2717"} Available`);
|
|
2155
|
+
}
|
|
2156
|
+
lines.push("");
|
|
2157
|
+
}
|
|
2158
|
+
if (data.reusability) {
|
|
2159
|
+
lines.push("## Reusability");
|
|
2160
|
+
if (data.reusability.licence !== void 0) {
|
|
2161
|
+
lines.push(`- License: ${data.reusability.licence.available ? "\u2713" : "\u2717"} Available`);
|
|
2162
|
+
}
|
|
2163
|
+
if (data.reusability.contactPoint !== void 0) {
|
|
2164
|
+
lines.push(`- Contact Point: ${data.reusability.contactPoint.available ? "\u2713" : "\u2717"} Available`);
|
|
2165
|
+
}
|
|
2166
|
+
if (data.reusability.publisher !== void 0) {
|
|
2167
|
+
lines.push(`- Publisher: ${data.reusability.publisher.available ? "\u2713" : "\u2717"} Available`);
|
|
2168
|
+
}
|
|
2169
|
+
lines.push("");
|
|
2170
|
+
}
|
|
2171
|
+
if (data.interoperability) {
|
|
2172
|
+
lines.push("## Interoperability");
|
|
2173
|
+
if (data.interoperability.format !== void 0) {
|
|
2174
|
+
lines.push(`- Format: ${data.interoperability.format.available ? "\u2713" : "\u2717"} Available`);
|
|
2175
|
+
}
|
|
2176
|
+
if (data.interoperability.mediaType !== void 0) {
|
|
2177
|
+
lines.push(`- Media Type: ${data.interoperability.mediaType.available ? "\u2713" : "\u2717"} Available`);
|
|
2178
|
+
}
|
|
2179
|
+
lines.push("");
|
|
2180
|
+
}
|
|
2181
|
+
if (data.findability) {
|
|
2182
|
+
lines.push("## Findability");
|
|
2183
|
+
if (data.findability.keyword !== void 0) {
|
|
2184
|
+
lines.push(`- Keywords: ${data.findability.keyword.available ? "\u2713" : "\u2717"} Available`);
|
|
2185
|
+
}
|
|
2186
|
+
if (data.findability.category !== void 0) {
|
|
2187
|
+
lines.push(`- Category: ${data.findability.category.available ? "\u2713" : "\u2717"} Available`);
|
|
2188
|
+
}
|
|
2189
|
+
if (data.findability.spatial !== void 0) {
|
|
2190
|
+
lines.push(`- Spatial: ${data.findability.spatial.available ? "\u2713" : "\u2717"} Available`);
|
|
2191
|
+
}
|
|
2192
|
+
if (data.findability.temporal !== void 0) {
|
|
2193
|
+
lines.push(`- Temporal: ${data.findability.temporal.available ? "\u2713" : "\u2717"} Available`);
|
|
2194
|
+
}
|
|
2195
|
+
lines.push("");
|
|
2196
|
+
}
|
|
2197
|
+
lines.push("---");
|
|
2198
|
+
lines.push(`Source: ${MQA_API_BASE}/${data.id || datasetId}`);
|
|
2199
|
+
return lines.join("\n");
|
|
2200
|
+
}
|
|
2201
|
+
function registerQualityTools(server2) {
|
|
2202
|
+
server2.tool(
|
|
2203
|
+
"ckan_get_mqa_quality",
|
|
2204
|
+
"Get MQA (Metadata Quality Assurance) quality metrics for a dataset on dati.gov.it. Returns quality score and detailed metrics (accessibility, reusability, interoperability, findability) from data.europa.eu. Only works with dati.gov.it server.",
|
|
2205
|
+
{
|
|
2206
|
+
server_url: z8.string().url().describe("Base URL of dati.gov.it (e.g., https://www.dati.gov.it/opendata)"),
|
|
2207
|
+
dataset_id: z8.string().describe("Dataset ID or name"),
|
|
2208
|
+
response_format: ResponseFormatSchema.optional()
|
|
2209
|
+
},
|
|
2210
|
+
async ({ server_url, dataset_id, response_format }) => {
|
|
2211
|
+
if (!isValidMqaServer(server_url)) {
|
|
2212
|
+
return {
|
|
2213
|
+
content: [{
|
|
2214
|
+
type: "text",
|
|
2215
|
+
text: `Error: MQA quality metrics are only available for dati.gov.it datasets. Provided server: ${server_url}
|
|
2216
|
+
|
|
2217
|
+
The MQA (Metadata Quality Assurance) system is operated by data.europa.eu and only evaluates datasets from Italian open data portal.`
|
|
2218
|
+
}]
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
try {
|
|
2222
|
+
const qualityData = await getMqaQuality(server_url, dataset_id);
|
|
2223
|
+
const format = response_format || "markdown" /* MARKDOWN */;
|
|
2224
|
+
const output = format === "json" /* JSON */ ? JSON.stringify(qualityData, null, 2) : formatQualityMarkdown(qualityData, dataset_id);
|
|
2225
|
+
return {
|
|
2226
|
+
content: [{
|
|
2227
|
+
type: "text",
|
|
2228
|
+
text: output
|
|
2229
|
+
}]
|
|
2230
|
+
};
|
|
2231
|
+
} catch (error) {
|
|
2232
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2233
|
+
return {
|
|
2234
|
+
content: [{
|
|
2235
|
+
type: "text",
|
|
2236
|
+
text: `Error retrieving quality metrics: ${errorMessage}`
|
|
2237
|
+
}]
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
);
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2104
2244
|
// src/resources/dataset.ts
|
|
2105
2245
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2106
2246
|
|
|
@@ -2370,7 +2510,7 @@ function registerAllResources(server2) {
|
|
|
2370
2510
|
}
|
|
2371
2511
|
|
|
2372
2512
|
// src/prompts/theme.ts
|
|
2373
|
-
import { z as
|
|
2513
|
+
import { z as z9 } from "zod";
|
|
2374
2514
|
|
|
2375
2515
|
// src/prompts/types.ts
|
|
2376
2516
|
var createTextPrompt = (text) => ({
|
|
@@ -2427,9 +2567,9 @@ var registerThemePrompt = (server2) => {
|
|
|
2427
2567
|
title: "Search datasets by theme",
|
|
2428
2568
|
description: "Guided prompt to discover a theme and search datasets under it.",
|
|
2429
2569
|
argsSchema: {
|
|
2430
|
-
server_url:
|
|
2431
|
-
theme:
|
|
2432
|
-
rows:
|
|
2570
|
+
server_url: z9.string().url().describe("Base URL of the CKAN server"),
|
|
2571
|
+
theme: z9.string().min(1).describe("Theme or group name to search"),
|
|
2572
|
+
rows: z9.coerce.number().int().positive().default(10).describe("Max results to return")
|
|
2433
2573
|
}
|
|
2434
2574
|
},
|
|
2435
2575
|
async ({ server_url, theme, rows }) => createTextPrompt(buildThemePromptText(server_url, theme, rows))
|
|
@@ -2437,7 +2577,7 @@ var registerThemePrompt = (server2) => {
|
|
|
2437
2577
|
};
|
|
2438
2578
|
|
|
2439
2579
|
// src/prompts/organization.ts
|
|
2440
|
-
import { z as
|
|
2580
|
+
import { z as z10 } from "zod";
|
|
2441
2581
|
var ORGANIZATION_PROMPT_NAME = "ckan-search-by-organization";
|
|
2442
2582
|
var buildOrganizationPromptText = (serverUrl, organization, rows) => `# Guided search: datasets by organization
|
|
2443
2583
|
|
|
@@ -2468,9 +2608,9 @@ var registerOrganizationPrompt = (server2) => {
|
|
|
2468
2608
|
title: "Search datasets by organization",
|
|
2469
2609
|
description: "Guided prompt to find a publisher and list its datasets.",
|
|
2470
2610
|
argsSchema: {
|
|
2471
|
-
server_url:
|
|
2472
|
-
organization:
|
|
2473
|
-
rows:
|
|
2611
|
+
server_url: z10.string().url().describe("Base URL of the CKAN server"),
|
|
2612
|
+
organization: z10.string().min(1).describe("Organization name or keyword"),
|
|
2613
|
+
rows: z10.coerce.number().int().positive().default(10).describe("Max results to return")
|
|
2474
2614
|
}
|
|
2475
2615
|
},
|
|
2476
2616
|
async ({ server_url, organization, rows }) => createTextPrompt(buildOrganizationPromptText(server_url, organization, rows))
|
|
@@ -2478,7 +2618,7 @@ var registerOrganizationPrompt = (server2) => {
|
|
|
2478
2618
|
};
|
|
2479
2619
|
|
|
2480
2620
|
// src/prompts/format.ts
|
|
2481
|
-
import { z as
|
|
2621
|
+
import { z as z11 } from "zod";
|
|
2482
2622
|
var FORMAT_PROMPT_NAME = "ckan-search-by-format";
|
|
2483
2623
|
var buildFormatPromptText = (serverUrl, format, rows) => `# Guided search: datasets by resource format
|
|
2484
2624
|
|
|
@@ -2499,9 +2639,9 @@ var registerFormatPrompt = (server2) => {
|
|
|
2499
2639
|
title: "Search datasets by resource format",
|
|
2500
2640
|
description: "Guided prompt to find datasets with a given resource format.",
|
|
2501
2641
|
argsSchema: {
|
|
2502
|
-
server_url:
|
|
2503
|
-
format:
|
|
2504
|
-
rows:
|
|
2642
|
+
server_url: z11.string().url().describe("Base URL of the CKAN server"),
|
|
2643
|
+
format: z11.string().min(1).describe("Resource format (e.g., CSV, JSON)"),
|
|
2644
|
+
rows: z11.coerce.number().int().positive().default(10).describe("Max results to return")
|
|
2505
2645
|
}
|
|
2506
2646
|
},
|
|
2507
2647
|
async ({ server_url, format, rows }) => createTextPrompt(buildFormatPromptText(server_url, format, rows))
|
|
@@ -2509,7 +2649,7 @@ var registerFormatPrompt = (server2) => {
|
|
|
2509
2649
|
};
|
|
2510
2650
|
|
|
2511
2651
|
// src/prompts/recent.ts
|
|
2512
|
-
import { z as
|
|
2652
|
+
import { z as z12 } from "zod";
|
|
2513
2653
|
var RECENT_PROMPT_NAME = "ckan-recent-datasets";
|
|
2514
2654
|
var buildRecentPromptText = (serverUrl, rows) => `# Guided search: recent datasets
|
|
2515
2655
|
|
|
@@ -2537,8 +2677,8 @@ var registerRecentPrompt = (server2) => {
|
|
|
2537
2677
|
title: "Find recently updated datasets",
|
|
2538
2678
|
description: "Guided prompt to list recently updated datasets on a CKAN portal.",
|
|
2539
2679
|
argsSchema: {
|
|
2540
|
-
server_url:
|
|
2541
|
-
rows:
|
|
2680
|
+
server_url: z12.string().url().describe("Base URL of the CKAN server"),
|
|
2681
|
+
rows: z12.coerce.number().int().positive().default(10).describe("Max results to return")
|
|
2542
2682
|
}
|
|
2543
2683
|
},
|
|
2544
2684
|
async ({ server_url, rows }) => createTextPrompt(buildRecentPromptText(server_url, rows))
|
|
@@ -2546,7 +2686,7 @@ var registerRecentPrompt = (server2) => {
|
|
|
2546
2686
|
};
|
|
2547
2687
|
|
|
2548
2688
|
// src/prompts/dataset-analysis.ts
|
|
2549
|
-
import { z as
|
|
2689
|
+
import { z as z13 } from "zod";
|
|
2550
2690
|
var DATASET_ANALYSIS_PROMPT_NAME = "ckan-analyze-dataset";
|
|
2551
2691
|
var buildDatasetAnalysisPromptText = (serverUrl, id) => `# Guided analysis: dataset
|
|
2552
2692
|
|
|
@@ -2588,8 +2728,8 @@ var registerDatasetAnalysisPrompt = (server2) => {
|
|
|
2588
2728
|
title: "Analyze a dataset",
|
|
2589
2729
|
description: "Guided prompt to inspect dataset metadata and explore DataStore tables.",
|
|
2590
2730
|
argsSchema: {
|
|
2591
|
-
server_url:
|
|
2592
|
-
id:
|
|
2731
|
+
server_url: z13.string().url().describe("Base URL of the CKAN server"),
|
|
2732
|
+
id: z13.string().min(1).describe("Dataset id or name (CKAN package id)")
|
|
2593
2733
|
}
|
|
2594
2734
|
},
|
|
2595
2735
|
async ({ server_url, id }) => createTextPrompt(buildDatasetAnalysisPromptText(server_url, id))
|
|
@@ -2609,7 +2749,7 @@ var registerAllPrompts = (server2) => {
|
|
|
2609
2749
|
function createServer() {
|
|
2610
2750
|
return new McpServer({
|
|
2611
2751
|
name: "ckan-mcp-server",
|
|
2612
|
-
version: "0.4.
|
|
2752
|
+
version: "0.4.15"
|
|
2613
2753
|
});
|
|
2614
2754
|
}
|
|
2615
2755
|
function registerAll(server2) {
|
|
@@ -2619,6 +2759,7 @@ function registerAll(server2) {
|
|
|
2619
2759
|
registerStatusTools(server2);
|
|
2620
2760
|
registerTagTools(server2);
|
|
2621
2761
|
registerGroupTools(server2);
|
|
2762
|
+
registerQualityTools(server2);
|
|
2622
2763
|
registerAllResources(server2);
|
|
2623
2764
|
registerAllPrompts(server2);
|
|
2624
2765
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Change: Add MQA Quality Score Tool for dati.gov.it
|
|
2
|
+
|
|
3
|
+
## Why
|
|
4
|
+
Datasets on dati.gov.it display quality scores (Eccellente, Buono, etc.) calculated by data.europa.eu's MQA (Metadata Quality Assurance) system. Currently there's no way to access these quality metrics through the MCP server, limiting users' ability to evaluate dataset quality programmatically.
|
|
5
|
+
|
|
6
|
+
## What Changes
|
|
7
|
+
- Add `ckan_get_mqa_quality` tool for retrieving quality metrics from data.europa.eu
|
|
8
|
+
- Tool works only with dati.gov.it server (validated at runtime)
|
|
9
|
+
- Fetches dataset identifier from CKAN, then queries MQA API
|
|
10
|
+
- Returns quality score and detailed metrics (accessibility, reusability, interoperability, findability)
|
|
11
|
+
- Supports both markdown and JSON output formats
|
|
12
|
+
|
|
13
|
+
## Impact
|
|
14
|
+
- Affected specs: New capability `ckan-quality`
|
|
15
|
+
- Affected code:
|
|
16
|
+
- New file: `src/tools/quality.ts` (tool handler)
|
|
17
|
+
- New file: `tests/integration/quality.test.ts` (tests with mocked responses)
|
|
18
|
+
- New file: `tests/fixtures/responses/mqa-quality.json` (mock data)
|
|
19
|
+
- Modified: `src/server.ts` (register new tool)
|
|
20
|
+
- Modified: `README.md` (document new tool)
|
|
21
|
+
- Modified: `EXAMPLES.md` (add usage examples)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: MQA Quality Score Retrieval
|
|
4
|
+
The system SHALL provide a tool to retrieve MQA (Metadata Quality Assurance) quality metrics from data.europa.eu for datasets published on dati.gov.it.
|
|
5
|
+
|
|
6
|
+
#### Scenario: Successful quality score retrieval
|
|
7
|
+
- **GIVEN** a valid dataset ID from dati.gov.it
|
|
8
|
+
- **WHEN** user requests quality metrics
|
|
9
|
+
- **THEN** system SHALL fetch identifier field from CKAN package_show
|
|
10
|
+
- **AND** system SHALL query data.europa.eu MQA API
|
|
11
|
+
- **AND** system SHALL return quality score and detailed metrics (accessibility, reusability, interoperability, findability)
|
|
12
|
+
|
|
13
|
+
#### Scenario: Identifier fallback to name
|
|
14
|
+
- **GIVEN** a dataset with empty identifier field
|
|
15
|
+
- **WHEN** user requests quality metrics
|
|
16
|
+
- **THEN** system SHALL use the name field as fallback identifier for MQA API query
|
|
17
|
+
|
|
18
|
+
#### Scenario: Dataset not found
|
|
19
|
+
- **GIVEN** an invalid or non-existent dataset ID
|
|
20
|
+
- **WHEN** user requests quality metrics
|
|
21
|
+
- **THEN** system SHALL return clear error message indicating dataset not found
|
|
22
|
+
|
|
23
|
+
#### Scenario: MQA API unavailable
|
|
24
|
+
- **GIVEN** data.europa.eu MQA API is unavailable or returns error
|
|
25
|
+
- **WHEN** user requests quality metrics
|
|
26
|
+
- **THEN** system SHALL return clear error message indicating MQA service unavailability
|
|
27
|
+
|
|
28
|
+
### Requirement: Server Validation
|
|
29
|
+
The system SHALL restrict MQA quality queries to dati.gov.it server only.
|
|
30
|
+
|
|
31
|
+
#### Scenario: Valid dati.gov.it server
|
|
32
|
+
- **GIVEN** server_url is "https://www.dati.gov.it/opendata" or "https://dati.gov.it/opendata"
|
|
33
|
+
- **WHEN** user requests quality metrics
|
|
34
|
+
- **THEN** system SHALL proceed with MQA query
|
|
35
|
+
|
|
36
|
+
#### Scenario: Invalid server URL
|
|
37
|
+
- **GIVEN** server_url is not dati.gov.it (e.g., "https://catalog.data.gov")
|
|
38
|
+
- **WHEN** user requests quality metrics
|
|
39
|
+
- **THEN** system SHALL reject request with error message explaining MQA is only available for dati.gov.it
|
|
40
|
+
|
|
41
|
+
### Requirement: Output Formats
|
|
42
|
+
The system SHALL support both markdown and JSON output formats for quality metrics.
|
|
43
|
+
|
|
44
|
+
#### Scenario: Markdown format (default)
|
|
45
|
+
- **GIVEN** user does not specify response_format or specifies "markdown"
|
|
46
|
+
- **WHEN** quality metrics are retrieved
|
|
47
|
+
- **THEN** system SHALL return human-readable markdown with:
|
|
48
|
+
- Overall quality score
|
|
49
|
+
- Breakdown by dimension (accessibility, reusability, interoperability, findability)
|
|
50
|
+
- Key findings and recommendations
|
|
51
|
+
|
|
52
|
+
#### Scenario: JSON format
|
|
53
|
+
- **GIVEN** user specifies response_format as "json"
|
|
54
|
+
- **WHEN** quality metrics are retrieved
|
|
55
|
+
- **THEN** system SHALL return complete MQA API response as structured JSON
|
|
56
|
+
|
|
57
|
+
### Requirement: Tool Parameters
|
|
58
|
+
The system SHALL accept the following parameters for the MQA quality tool:
|
|
59
|
+
- server_url (required): Base URL of dati.gov.it portal
|
|
60
|
+
- dataset_id (required): Dataset ID or name
|
|
61
|
+
- response_format (optional): "markdown" (default) or "json"
|
|
62
|
+
|
|
63
|
+
#### Scenario: Minimal parameters
|
|
64
|
+
- **GIVEN** user provides only server_url and dataset_id
|
|
65
|
+
- **WHEN** tool is invoked
|
|
66
|
+
- **THEN** system SHALL use default markdown format
|
|
67
|
+
|
|
68
|
+
#### Scenario: All parameters specified
|
|
69
|
+
- **GIVEN** user provides server_url, dataset_id, and response_format
|
|
70
|
+
- **WHEN** tool is invoked
|
|
71
|
+
- **THEN** system SHALL use specified format for output
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Implementation Tasks
|
|
2
|
+
|
|
3
|
+
## 1. Core Implementation
|
|
4
|
+
- [x] 1.1 Create `src/tools/quality.ts` with `ckan_get_mqa_quality` tool handler
|
|
5
|
+
- [x] 1.2 Implement server URL validation (dati.gov.it only)
|
|
6
|
+
- [x] 1.3 Add CKAN package_show call to extract identifier field
|
|
7
|
+
- [x] 1.4 Add MQA API client (https://data.europa.eu/api/mqa/cache/datasets/{id})
|
|
8
|
+
- [x] 1.5 Implement markdown and JSON formatters for quality metrics
|
|
9
|
+
- [x] 1.6 Register tool in `src/server.ts`
|
|
10
|
+
|
|
11
|
+
## 2. Testing
|
|
12
|
+
- [x] 2.1 Create mock fixtures for CKAN package_show response
|
|
13
|
+
- [x] 2.2 Create mock fixtures for MQA API response
|
|
14
|
+
- [x] 2.3 Write integration tests for successful quality retrieval
|
|
15
|
+
- [x] 2.4 Write tests for error scenarios (invalid server, dataset not found, MQA API unavailable)
|
|
16
|
+
- [x] 2.5 Write tests for fallback from identifier to name field
|
|
17
|
+
- [x] 2.6 Verify test coverage matches project standards
|
|
18
|
+
|
|
19
|
+
## 3. Documentation
|
|
20
|
+
- [x] 3.1 Add tool description to README.md
|
|
21
|
+
- [x] 3.2 Add usage examples to EXAMPLES.md
|
|
22
|
+
- [x] 3.3 Document server restriction (dati.gov.it only)
|
|
23
|
+
- [x] 3.4 Document quality metrics structure (score, accessibility, reusability, interoperability, findability)
|
|
24
|
+
|
|
25
|
+
## 4. Validation
|
|
26
|
+
- [x] 4.1 Run full test suite (npm test) - 212 tests passing
|
|
27
|
+
- [x] 4.2 Test manually with real dati.gov.it dataset
|
|
28
|
+
- [x] 4.3 Verify error handling for non-dati.gov.it servers
|
|
29
|
+
- [x] 4.4 Build project successfully (npm run build)
|