@aborruso/ckan-mcp-server 0.4.67 → 0.4.69
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/LOG.md +11 -0
- package/dist/index.js +59 -17
- package/dist/worker.js +109 -167
- package/package.json +1 -1
package/LOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# LOG
|
|
2
2
|
|
|
3
|
+
## 2026-03-04 (v0.4.69)
|
|
4
|
+
|
|
5
|
+
- feat: `ckan_package_search` and `ckan_package_show` JSON output now include `view_url` field pointing to the dataset page on the source portal
|
|
6
|
+
|
|
7
|
+
## 2026-03-04 (v0.4.68)
|
|
8
|
+
|
|
9
|
+
- feat: SPARQL endpoint config in `portals.json` — `sparql.endpoint_url` + `sparql.method` per portal (Italy: `lod.dati.gov.it/sparql`, GET-only)
|
|
10
|
+
- feat: `ckan_status_show` now shows SPARQL endpoint when configured for the portal
|
|
11
|
+
- fix: `sparql_query` — GET fallback on 403/405 for endpoints that reject POST; User-Agent set to `Mozilla/5.0 (compatible; CKAN-MCP-Server/1.0)` (required by AWS WAF on lod.dati.gov.it)
|
|
12
|
+
- refactor: `portal-config.ts` — added `SparqlConfig` type, `getSparqlConfig(endpointUrl)`, `getPortalSparqlConfig(serverUrl)`; tool count 18→19
|
|
13
|
+
|
|
3
14
|
## 2026-03-04 (v0.4.67)
|
|
4
15
|
|
|
5
16
|
- improve: `sparql_query` — validate SELECT-only, auto-inject LIMIT (default 25, max 1000), truncate output at CHARACTER_LIMIT; +11 tests (310 total)
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,10 @@ var portals_default = {
|
|
|
37
37
|
hvd: {
|
|
38
38
|
category_field: "hvd_category"
|
|
39
39
|
},
|
|
40
|
+
sparql: {
|
|
41
|
+
endpoint_url: "https://lod.dati.gov.it/sparql",
|
|
42
|
+
method: "GET"
|
|
43
|
+
},
|
|
40
44
|
dataset_view_url: "https://www.dati.gov.it/view-dataset/dataset?id={id}",
|
|
41
45
|
organization_view_url: "https://www.dati.gov.it/view-dataset?organization={name}"
|
|
42
46
|
},
|
|
@@ -180,6 +184,17 @@ function getPortalHvdConfig(serverUrl) {
|
|
|
180
184
|
const portal = getPortalConfig(serverUrl);
|
|
181
185
|
return portal?.hvd ?? null;
|
|
182
186
|
}
|
|
187
|
+
function getSparqlConfig(endpointUrl) {
|
|
188
|
+
const cleanUrl = normalizeUrl(endpointUrl);
|
|
189
|
+
const portal = portals_default.portals.find(
|
|
190
|
+
(p) => p.sparql && normalizeUrl(p.sparql.endpoint_url) === cleanUrl
|
|
191
|
+
);
|
|
192
|
+
return portal?.sparql ?? null;
|
|
193
|
+
}
|
|
194
|
+
function getPortalSparqlConfig(serverUrl) {
|
|
195
|
+
const portal = getPortalConfig(serverUrl);
|
|
196
|
+
return portal?.sparql ?? null;
|
|
197
|
+
}
|
|
183
198
|
function getPortalApiPath(serverUrl) {
|
|
184
199
|
const portal = getPortalConfig(serverUrl);
|
|
185
200
|
return portal?.api_path || "/api/3/action";
|
|
@@ -857,7 +872,7 @@ function resolvePageParams(page, pageSize, start, rows) {
|
|
|
857
872
|
}
|
|
858
873
|
return { effectiveStart: start, effectiveRows: rows };
|
|
859
874
|
}
|
|
860
|
-
function compactSearchResult(result) {
|
|
875
|
+
function compactSearchResult(result, serverUrl) {
|
|
861
876
|
return {
|
|
862
877
|
count: result.count,
|
|
863
878
|
results: (result.results || []).map((pkg) => ({
|
|
@@ -868,13 +883,14 @@ function compactSearchResult(result) {
|
|
|
868
883
|
organization: pkg.organization?.title || pkg.organization?.name || null,
|
|
869
884
|
tags: (pkg.tags || []).map((t) => t.name),
|
|
870
885
|
num_resources: pkg.num_resources ?? 0,
|
|
871
|
-
metadata_modified: pkg.metadata_modified
|
|
886
|
+
metadata_modified: pkg.metadata_modified,
|
|
887
|
+
...serverUrl ? { view_url: getDatasetViewUrl(serverUrl, pkg) } : {}
|
|
872
888
|
})),
|
|
873
889
|
...result.facets && Object.keys(result.facets).length > 0 ? { facets: result.facets } : {},
|
|
874
890
|
...result.search_facets && Object.keys(result.search_facets).length > 0 ? { search_facets: result.search_facets } : {}
|
|
875
891
|
};
|
|
876
892
|
}
|
|
877
|
-
function compactPackageShow(result) {
|
|
893
|
+
function compactPackageShow(result, serverUrl) {
|
|
878
894
|
return {
|
|
879
895
|
id: result.id,
|
|
880
896
|
name: result.name,
|
|
@@ -902,7 +918,8 @@ function compactPackageShow(result) {
|
|
|
902
918
|
datastore_active: r.datastore_active ?? null,
|
|
903
919
|
created: r.created || null,
|
|
904
920
|
last_modified: r.last_modified || null
|
|
905
|
-
}))
|
|
921
|
+
})),
|
|
922
|
+
...serverUrl ? { view_url: getDatasetViewUrl(serverUrl, result) } : {}
|
|
906
923
|
};
|
|
907
924
|
}
|
|
908
925
|
function registerPackageTools(server2) {
|
|
@@ -1096,7 +1113,7 @@ Typical workflow: ckan_package_search \u2192 ckan_package_show (get full metadat
|
|
|
1096
1113
|
}
|
|
1097
1114
|
}
|
|
1098
1115
|
if (params.response_format === "json" /* JSON */) {
|
|
1099
|
-
const compact = compactSearchResult(result);
|
|
1116
|
+
const compact = compactSearchResult(result, params.server_url);
|
|
1100
1117
|
return {
|
|
1101
1118
|
content: [{ type: "text", text: truncateJson(compact) }]
|
|
1102
1119
|
};
|
|
@@ -1481,7 +1498,7 @@ Typical workflow: ckan_package_show \u2192 pick a resource with datastore_active
|
|
|
1481
1498
|
}
|
|
1482
1499
|
);
|
|
1483
1500
|
if (params.response_format === "json" /* JSON */) {
|
|
1484
|
-
const compact = compactPackageShow(enrichPackageShowResult(result));
|
|
1501
|
+
const compact = compactPackageShow(enrichPackageShowResult(result), params.server_url);
|
|
1485
1502
|
return {
|
|
1486
1503
|
content: [{ type: "text", text: truncateJson(compact) }],
|
|
1487
1504
|
structuredContent: compact
|
|
@@ -2375,6 +2392,9 @@ Typical workflow: ckan_package_show (get resource_id) \u2192 ckan_datastore_sear
|
|
|
2375
2392
|
// src/tools/status.ts
|
|
2376
2393
|
import { z as z5 } from "zod";
|
|
2377
2394
|
function formatStatusMarkdown(result, serverUrl) {
|
|
2395
|
+
const sparql = getPortalSparqlConfig(serverUrl);
|
|
2396
|
+
const sparqlLine = sparql ? `**SPARQL Endpoint**: ${sparql.endpoint_url}
|
|
2397
|
+
` : "";
|
|
2378
2398
|
return `# CKAN Server Status
|
|
2379
2399
|
|
|
2380
2400
|
**Server**: ${serverUrl}
|
|
@@ -2382,7 +2402,7 @@ function formatStatusMarkdown(result, serverUrl) {
|
|
|
2382
2402
|
**CKAN Version**: ${result.ckan_version || "Unknown"}
|
|
2383
2403
|
**Site Title**: ${result.site_title || "N/A"}
|
|
2384
2404
|
**Site URL**: ${result.site_url || "N/A"}
|
|
2385
|
-
|
|
2405
|
+
` + sparqlLine;
|
|
2386
2406
|
}
|
|
2387
2407
|
function registerStatusTools(server2) {
|
|
2388
2408
|
server2.registerTool(
|
|
@@ -4004,19 +4024,41 @@ async function querySparqlEndpoint(endpointUrl, query) {
|
|
|
4004
4024
|
if (url.protocol !== "https:") {
|
|
4005
4025
|
throw new Error("Only HTTPS endpoints are allowed");
|
|
4006
4026
|
}
|
|
4027
|
+
const sparqlConfig = getSparqlConfig(endpointUrl);
|
|
4028
|
+
const method = sparqlConfig?.method ?? "POST";
|
|
4029
|
+
const commonHeaders = {
|
|
4030
|
+
"Accept": "application/sparql-results+json",
|
|
4031
|
+
"User-Agent": "Mozilla/5.0 (compatible; CKAN-MCP-Server/1.0)"
|
|
4032
|
+
};
|
|
4007
4033
|
const controller = new AbortController();
|
|
4008
4034
|
const timeoutId = setTimeout(() => controller.abort(), 15e3);
|
|
4009
4035
|
let response;
|
|
4010
4036
|
try {
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
}
|
|
4037
|
+
if (method === "GET") {
|
|
4038
|
+
const getUrl = new URL(endpointUrl);
|
|
4039
|
+
getUrl.searchParams.set("query", query);
|
|
4040
|
+
response = await fetch(getUrl.toString(), {
|
|
4041
|
+
method: "GET",
|
|
4042
|
+
signal: controller.signal,
|
|
4043
|
+
headers: commonHeaders
|
|
4044
|
+
});
|
|
4045
|
+
} else {
|
|
4046
|
+
response = await fetch(endpointUrl, {
|
|
4047
|
+
method: "POST",
|
|
4048
|
+
signal: controller.signal,
|
|
4049
|
+
headers: { ...commonHeaders, "Content-Type": "application/sparql-query" },
|
|
4050
|
+
body: query
|
|
4051
|
+
});
|
|
4052
|
+
if (response.status === 403 || response.status === 405) {
|
|
4053
|
+
const getUrl = new URL(endpointUrl);
|
|
4054
|
+
getUrl.searchParams.set("query", query);
|
|
4055
|
+
response = await fetch(getUrl.toString(), {
|
|
4056
|
+
method: "GET",
|
|
4057
|
+
signal: controller.signal,
|
|
4058
|
+
headers: commonHeaders
|
|
4059
|
+
});
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4020
4062
|
} finally {
|
|
4021
4063
|
clearTimeout(timeoutId);
|
|
4022
4064
|
}
|
|
@@ -4681,7 +4723,7 @@ var registerAllPrompts = (server2) => {
|
|
|
4681
4723
|
function createServer() {
|
|
4682
4724
|
return new McpServer({
|
|
4683
4725
|
name: "ckan-mcp-server",
|
|
4684
|
-
version: "0.4.
|
|
4726
|
+
version: "0.4.68"
|
|
4685
4727
|
});
|
|
4686
4728
|
}
|
|
4687
4729
|
function registerAll(server2) {
|