@aborruso/ckan-mcp-server 0.4.73 → 0.4.77
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 +13 -0
- package/dist/index.js +164 -42
- package/dist/worker.js +105 -70
- package/package.json +1 -1
package/LOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# LOG
|
|
2
2
|
|
|
3
|
+
## 2026-03-07 (v0.4.77)
|
|
4
|
+
|
|
5
|
+
- fix(`http.ts`): remove `Referer`, `Sec-Fetch-*`, `Upgrade-Insecure-Requests` from axios headers — these triggered WAF block on BA Data (data.buenosaires.gob.ar) and other portals with strict WAF rules; dati.gov.it unaffected
|
|
6
|
+
|
|
7
|
+
## 2026-03-06 (v0.4.75)
|
|
8
|
+
|
|
9
|
+
- fix(`ckan_find_portals`): deduplicate portals by hostname, preferring https over http
|
|
10
|
+
- feat: new tool `ckan_find_portals` — discovers CKAN portals from datashades.info registry (~950 portals); filters by country, keyword, min_datasets, language, has_datastore; LLM translates country to English
|
|
11
|
+
|
|
12
|
+
## 2026-03-06 (v0.4.74)
|
|
13
|
+
|
|
14
|
+
- fix: use `z.coerce.number()` for all numeric tool parameters — fixes validation errors when MCP clients pass numbers as strings (closes #16)
|
|
15
|
+
|
|
3
16
|
## 2026-03-05 (v0.4.73)
|
|
4
17
|
|
|
5
18
|
- feat: `package_show` now includes `api_json_url` for dataset and each resource (direct CKAN API JSON link)
|
package/dist/index.js
CHANGED
|
@@ -359,11 +359,6 @@ async function makeCkanRequest(serverUrl, action, params = {}) {
|
|
|
359
359
|
"Accept-Language": "en-US,en;q=0.9,it;q=0.8",
|
|
360
360
|
"Accept-Encoding": "gzip, deflate, br",
|
|
361
361
|
Connection: "keep-alive",
|
|
362
|
-
Referer: `${baseUrl}/`,
|
|
363
|
-
"Sec-Fetch-Site": "same-origin",
|
|
364
|
-
"Sec-Fetch-Mode": "navigate",
|
|
365
|
-
"Sec-Fetch-Dest": "document",
|
|
366
|
-
"Upgrade-Insecure-Requests": "1",
|
|
367
362
|
"Sec-CH-UA": '"Chromium";v="120", "Not?A_Brand";v="24", "Google Chrome";v="120"',
|
|
368
363
|
"Sec-CH-UA-Mobile": "?0",
|
|
369
364
|
"Sec-CH-UA-Platform": '"Linux"',
|
|
@@ -1062,16 +1057,16 @@ Typical workflow: ckan_package_search \u2192 ckan_package_show (get full metadat
|
|
|
1062
1057
|
server_url: z2.string().url("Must be a valid URL").describe("Base URL of the CKAN server"),
|
|
1063
1058
|
q: z2.string().optional().default("*:*").describe("Search query in Solr syntax"),
|
|
1064
1059
|
fq: z2.string().optional().describe(`Filter query in Solr syntax; applied after scoring, does not affect relevance. CKAN extras fields use prefix 'extras_' (e.g. extras_hvd_category). For OR on same field use field:(val1 OR val2), never field:val1 OR field:val2 (silently breaks). Examples: 'organization:comune-palermo', 'res_format:CSV', 'extras_hvd_category:("uri1" OR "uri2")'.`),
|
|
1065
|
-
rows: z2.number().int().min(0).max(1e3).optional().default(10).describe("Number of results to return"),
|
|
1066
|
-
start: z2.number().int().min(0).optional().default(0).describe("Offset for pagination"),
|
|
1060
|
+
rows: z2.coerce.number().int().min(0).max(1e3).optional().default(10).describe("Number of results to return"),
|
|
1061
|
+
start: z2.coerce.number().int().min(0).optional().default(0).describe("Offset for pagination"),
|
|
1067
1062
|
sort: z2.string().optional().describe("Sort field and direction (e.g., 'metadata_modified desc')"),
|
|
1068
1063
|
facet_field: z2.array(z2.string()).optional().describe("Fields to facet on"),
|
|
1069
|
-
facet_limit: z2.number().int().min(1).optional().default(50).describe("Maximum facet values per field"),
|
|
1070
|
-
page: z2.number().int().min(1).optional().describe("Page number (1-based); alias for start. Overrides start if provided."),
|
|
1071
|
-
page_size: z2.number().int().min(1).max(1e3).optional().default(10).describe("Results per page when using page (default: 10)"),
|
|
1064
|
+
facet_limit: z2.coerce.number().int().min(1).optional().default(50).describe("Maximum facet values per field"),
|
|
1065
|
+
page: z2.coerce.number().int().min(1).optional().describe("Page number (1-based); alias for start. Overrides start if provided."),
|
|
1066
|
+
page_size: z2.coerce.number().int().min(1).max(1e3).optional().default(10).describe("Results per page when using page (default: 10)"),
|
|
1072
1067
|
include_drafts: z2.boolean().optional().default(false).describe("Include draft datasets"),
|
|
1073
1068
|
content_recent: z2.boolean().optional().default(false).describe("Use issued date with fallback to metadata_created for recent content"),
|
|
1074
|
-
content_recent_days: z2.number().int().min(1).optional().default(30).describe("Day window for content_recent (default 30)"),
|
|
1069
|
+
content_recent_days: z2.coerce.number().int().min(1).optional().default(30).describe("Day window for content_recent (default 30)"),
|
|
1075
1070
|
query_parser: z2.enum(["default", "text"]).optional().describe("Override search parser ('text' forces text:(...) on non-fielded queries)"),
|
|
1076
1071
|
response_format: ResponseFormatSchema
|
|
1077
1072
|
}).strict(),
|
|
@@ -1312,12 +1307,12 @@ Typical workflow: ckan_find_relevant_datasets \u2192 ckan_package_show (inspect
|
|
|
1312
1307
|
inputSchema: z2.object({
|
|
1313
1308
|
server_url: z2.string().url().describe("Base URL of the CKAN server (e.g., https://dati.gov.it/opendata)"),
|
|
1314
1309
|
query: z2.string().min(2).describe("Natural language or keyword query to match against dataset title, notes, tags, and organization"),
|
|
1315
|
-
limit: z2.number().int().min(1).max(50).optional().default(10).describe("Number of datasets to return"),
|
|
1310
|
+
limit: z2.coerce.number().int().min(1).max(50).optional().default(10).describe("Number of datasets to return"),
|
|
1316
1311
|
weights: z2.object({
|
|
1317
|
-
title: z2.number().min(0).optional().describe("Weight for title match (default 4)"),
|
|
1318
|
-
notes: z2.number().min(0).optional().describe("Weight for description match (default 2)"),
|
|
1319
|
-
tags: z2.number().min(0).optional().describe("Weight for tag match (default 3)"),
|
|
1320
|
-
organization: z2.number().min(0).optional().describe("Weight for organization match (default 1)")
|
|
1312
|
+
title: z2.coerce.number().min(0).optional().describe("Weight for title match (default 4)"),
|
|
1313
|
+
notes: z2.coerce.number().min(0).optional().describe("Weight for description match (default 2)"),
|
|
1314
|
+
tags: z2.coerce.number().min(0).optional().describe("Weight for tag match (default 3)"),
|
|
1315
|
+
organization: z2.coerce.number().min(0).optional().describe("Weight for organization match (default 1)")
|
|
1321
1316
|
}).optional().describe("Per-field scoring weights; unspecified fields use defaults"),
|
|
1322
1317
|
query_parser: z2.enum(["default", "text"]).optional().describe("Override search parser ('text' forces text:(...) on non-fielded queries)"),
|
|
1323
1318
|
response_format: ResponseFormatSchema
|
|
@@ -1795,8 +1790,8 @@ Typical workflow: ckan_organization_list \u2192 ckan_organization_show (inspect
|
|
|
1795
1790
|
server_url: z3.string().url().describe("Base URL of the CKAN server (e.g., https://dati.gov.it/opendata)"),
|
|
1796
1791
|
all_fields: z3.boolean().optional().default(false).describe("Return full organization objects (true) or just name slugs (false)"),
|
|
1797
1792
|
sort: z3.string().optional().default("name asc").describe("Sort field and direction (e.g., 'name asc', 'package_count desc')"),
|
|
1798
|
-
limit: z3.number().int().min(0).optional().default(100).describe("Max organizations to return. Use 0 to get only the count via faceting"),
|
|
1799
|
-
offset: z3.number().int().min(0).optional().default(0).describe("Pagination offset"),
|
|
1793
|
+
limit: z3.coerce.number().int().min(0).optional().default(100).describe("Max organizations to return. Use 0 to get only the count via faceting"),
|
|
1794
|
+
offset: z3.coerce.number().int().min(0).optional().default(0).describe("Pagination offset"),
|
|
1800
1795
|
response_format: ResponseFormatSchema
|
|
1801
1796
|
}).strict(),
|
|
1802
1797
|
annotations: {
|
|
@@ -2301,8 +2296,8 @@ Typical workflow: ckan_package_search \u2192 ckan_package_show (find resource_id
|
|
|
2301
2296
|
resource_id: z4.string().min(1).describe("UUID of the DataStore resource (from ckan_package_show resource.id where datastore_active is true)"),
|
|
2302
2297
|
q: z4.string().optional().describe("Full-text search across all fields"),
|
|
2303
2298
|
filters: z4.record(z4.any()).optional().describe('Key-value filters for exact matches (e.g., { "regione": "Sicilia", "anno": 2023 })'),
|
|
2304
|
-
limit: z4.number().int().min(0).max(32e3).optional().default(100).describe("Max rows to return (default 100, max 32000); use 0 to get only column names without data"),
|
|
2305
|
-
offset: z4.number().int().min(0).optional().default(0).describe("Pagination offset"),
|
|
2299
|
+
limit: z4.coerce.number().int().min(0).max(32e3).optional().default(100).describe("Max rows to return (default 100, max 32000); use 0 to get only column names without data"),
|
|
2300
|
+
offset: z4.coerce.number().int().min(0).optional().default(0).describe("Pagination offset"),
|
|
2306
2301
|
fields: z4.array(z4.string()).optional().describe("Specific field names to return; omit to return all fields"),
|
|
2307
2302
|
sort: z4.string().optional().describe("Sort expression (e.g., 'anno desc', 'nome asc')"),
|
|
2308
2303
|
distinct: z4.boolean().optional().default(false).describe("Return only distinct rows"),
|
|
@@ -4215,6 +4210,132 @@ ${error instanceof Error ? error.message : String(error)}`
|
|
|
4215
4210
|
);
|
|
4216
4211
|
}
|
|
4217
4212
|
|
|
4213
|
+
// src/tools/portal-discovery.ts
|
|
4214
|
+
import { z as z11 } from "zod";
|
|
4215
|
+
import axios3 from "axios";
|
|
4216
|
+
var DATASHADES_URL = "https://datashades.info/api/portal/list";
|
|
4217
|
+
async function fetchPortals() {
|
|
4218
|
+
const resp = await axios3.get(DATASHADES_URL, {
|
|
4219
|
+
timeout: 15e3,
|
|
4220
|
+
headers: { "User-Agent": "CKAN-MCP-Server/1.0" }
|
|
4221
|
+
});
|
|
4222
|
+
return resp.data.portals;
|
|
4223
|
+
}
|
|
4224
|
+
function deduplicateByHostname(portals) {
|
|
4225
|
+
const seen = /* @__PURE__ */ new Map();
|
|
4226
|
+
for (const p of portals) {
|
|
4227
|
+
try {
|
|
4228
|
+
const hostname = new URL(p.Href).hostname;
|
|
4229
|
+
const existing = seen.get(hostname);
|
|
4230
|
+
if (!existing || p.Href.startsWith("https://")) {
|
|
4231
|
+
seen.set(hostname, p);
|
|
4232
|
+
}
|
|
4233
|
+
} catch {
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
return Array.from(seen.values());
|
|
4237
|
+
}
|
|
4238
|
+
function filterPortals(portals, params) {
|
|
4239
|
+
const filtered = portals.filter((p) => p.status === "active" && p.Href).filter((p) => !params.country || p.Coordinates.country_name.toLowerCase().includes(params.country.toLowerCase())).filter((p) => !params.query || p.SiteInfo.site_title.toLowerCase().includes(params.query.toLowerCase())).filter((p) => params.min_datasets === void 0 || p.DatasetsNumber >= params.min_datasets).filter((p) => !params.language || p.SiteInfo.locale_default.toLowerCase().startsWith(params.language.toLowerCase())).filter((p) => !params.has_datastore || (p.Plugins || []).includes("datastore"));
|
|
4240
|
+
return deduplicateByHostname(filtered).sort((a, b) => b.DatasetsNumber - a.DatasetsNumber).slice(0, params.limit);
|
|
4241
|
+
}
|
|
4242
|
+
function formatMarkdown(portals, total, limit) {
|
|
4243
|
+
if (portals.length === 0) return "No CKAN portals found matching the given filters.";
|
|
4244
|
+
const rows = portals.map(
|
|
4245
|
+
(p) => `| [${p.SiteInfo.site_title || p.Href}](${p.Href}) | ${p.Coordinates.country_name} | ${p.Version} | ${p.DatasetsNumber.toLocaleString()} | ${p.SiteInfo.locale_default} | ${(p.Plugins || []).includes("datastore") ? "\u2705" : "\u274C"} |`
|
|
4246
|
+
).join("\n");
|
|
4247
|
+
return `# CKAN Portals
|
|
4248
|
+
|
|
4249
|
+
**Source**: [datashades.info](https://datashades.info/portals) \u2014 live registry of ${total} active portals
|
|
4250
|
+
**Showing**: ${portals.length} of ${total} (filtered, sorted by dataset count)
|
|
4251
|
+
|
|
4252
|
+
| Portal | Country | CKAN | Datasets | Locale | DataStore |
|
|
4253
|
+
|--------|---------|------|----------|--------|-----------|
|
|
4254
|
+
${rows}
|
|
4255
|
+
|
|
4256
|
+
---
|
|
4257
|
+
\u{1F4A1} Use the portal URL as \`server_url\` in any CKAN tool.`;
|
|
4258
|
+
}
|
|
4259
|
+
function registerPortalDiscoveryTools(server2) {
|
|
4260
|
+
server2.registerTool(
|
|
4261
|
+
"ckan_find_portals",
|
|
4262
|
+
{
|
|
4263
|
+
title: "Find CKAN Portals",
|
|
4264
|
+
description: `Search the live datashades.info registry of ~950 CKAN portals worldwide.
|
|
4265
|
+
|
|
4266
|
+
Use this tool to discover which CKAN portals exist for a country, language, or topic
|
|
4267
|
+
before querying them with other CKAN tools.
|
|
4268
|
+
|
|
4269
|
+
**IMPORTANT \u2014 country parameter**: always pass country name in English.
|
|
4270
|
+
If the user writes in another language (e.g. "Italia", "Espa\xF1a", "Brasil"),
|
|
4271
|
+
translate to English ("Italy", "Spain", "Brazil") before calling this tool.
|
|
4272
|
+
|
|
4273
|
+
Args:
|
|
4274
|
+
- country (string): Country name in English (e.g. "Italy", "Brazil", "France")
|
|
4275
|
+
- query (string): Keyword to match against portal title (e.g. "transport", "health")
|
|
4276
|
+
- min_datasets (number): Minimum number of datasets (e.g. 100)
|
|
4277
|
+
- language (string): Portal default locale code (e.g. "it", "en", "pt_BR", "fr")
|
|
4278
|
+
- has_datastore (boolean): If true, return only portals with DataStore enabled (supports SQL queries)
|
|
4279
|
+
- limit (number): Max results to return (default 10, max 50)
|
|
4280
|
+
|
|
4281
|
+
Returns:
|
|
4282
|
+
Ranked list of matching portals with URL, country, CKAN version, dataset count, and DataStore status.
|
|
4283
|
+
|
|
4284
|
+
Typical workflow: ckan_find_portals (discover portal URL) \u2192 ckan_status_show (verify) \u2192 ckan_package_search (search datasets)`,
|
|
4285
|
+
inputSchema: z11.object({
|
|
4286
|
+
country: z11.string().optional().describe("Country name in English (e.g. 'Italy', 'Brazil'). Translate from any language before passing."),
|
|
4287
|
+
query: z11.string().optional().describe("Keyword matched against portal title (case-insensitive)"),
|
|
4288
|
+
min_datasets: z11.coerce.number().int().min(0).optional().describe("Minimum number of datasets"),
|
|
4289
|
+
language: z11.string().optional().describe("Portal default locale code (e.g. 'it', 'en', 'pt_BR')"),
|
|
4290
|
+
has_datastore: z11.boolean().optional().describe("If true, return only portals with DataStore plugin (required for SQL queries)"),
|
|
4291
|
+
limit: z11.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results (default 10, max 50)")
|
|
4292
|
+
}).strict(),
|
|
4293
|
+
annotations: {
|
|
4294
|
+
readOnlyHint: true,
|
|
4295
|
+
destructiveHint: false,
|
|
4296
|
+
idempotentHint: true,
|
|
4297
|
+
openWorldHint: true
|
|
4298
|
+
}
|
|
4299
|
+
},
|
|
4300
|
+
async (params) => {
|
|
4301
|
+
try {
|
|
4302
|
+
const all = await fetchPortals();
|
|
4303
|
+
const active = all.filter((p) => p.status === "active");
|
|
4304
|
+
const results = filterPortals(all, {
|
|
4305
|
+
country: params.country,
|
|
4306
|
+
query: params.query,
|
|
4307
|
+
min_datasets: params.min_datasets,
|
|
4308
|
+
language: params.language,
|
|
4309
|
+
has_datastore: params.has_datastore,
|
|
4310
|
+
limit: params.limit
|
|
4311
|
+
});
|
|
4312
|
+
const markdown = formatMarkdown(results, active.length, params.limit);
|
|
4313
|
+
return {
|
|
4314
|
+
content: [{ type: "text", text: addDemoFooter(markdown) }],
|
|
4315
|
+
structuredContent: { portals: results.map((p) => ({
|
|
4316
|
+
url: p.Href,
|
|
4317
|
+
title: p.SiteInfo.site_title,
|
|
4318
|
+
country: p.Coordinates.country_name,
|
|
4319
|
+
version: p.Version,
|
|
4320
|
+
datasets: p.DatasetsNumber,
|
|
4321
|
+
locale: p.SiteInfo.locale_default,
|
|
4322
|
+
has_datastore: (p.Plugins || []).includes("datastore")
|
|
4323
|
+
})) }
|
|
4324
|
+
};
|
|
4325
|
+
} catch (error) {
|
|
4326
|
+
return {
|
|
4327
|
+
content: [{
|
|
4328
|
+
type: "text",
|
|
4329
|
+
text: `Could not fetch portal list from datashades.info:
|
|
4330
|
+
${error instanceof Error ? error.message : String(error)}`
|
|
4331
|
+
}],
|
|
4332
|
+
isError: true
|
|
4333
|
+
};
|
|
4334
|
+
}
|
|
4335
|
+
}
|
|
4336
|
+
);
|
|
4337
|
+
}
|
|
4338
|
+
|
|
4218
4339
|
// src/resources/dataset.ts
|
|
4219
4340
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4220
4341
|
|
|
@@ -4484,7 +4605,7 @@ function registerAllResources(server2) {
|
|
|
4484
4605
|
}
|
|
4485
4606
|
|
|
4486
4607
|
// src/prompts/theme.ts
|
|
4487
|
-
import { z as
|
|
4608
|
+
import { z as z12 } from "zod";
|
|
4488
4609
|
|
|
4489
4610
|
// src/prompts/types.ts
|
|
4490
4611
|
var createTextPrompt = (text) => ({
|
|
@@ -4545,9 +4666,9 @@ var registerThemePrompt = (server2) => {
|
|
|
4545
4666
|
title: "Search datasets by theme",
|
|
4546
4667
|
description: "Guided prompt to discover a theme and search datasets under it.",
|
|
4547
4668
|
argsSchema: {
|
|
4548
|
-
server_url:
|
|
4549
|
-
theme:
|
|
4550
|
-
rows:
|
|
4669
|
+
server_url: z12.string().url().describe("Base URL of the CKAN server"),
|
|
4670
|
+
theme: z12.string().min(1).describe("Theme or group name to search"),
|
|
4671
|
+
rows: z12.coerce.number().int().positive().default(10).describe("Max results to return")
|
|
4551
4672
|
}
|
|
4552
4673
|
},
|
|
4553
4674
|
async ({ server_url, theme, rows }) => createTextPrompt(buildThemePromptText(server_url, theme, rows))
|
|
@@ -4555,7 +4676,7 @@ var registerThemePrompt = (server2) => {
|
|
|
4555
4676
|
};
|
|
4556
4677
|
|
|
4557
4678
|
// src/prompts/organization.ts
|
|
4558
|
-
import { z as
|
|
4679
|
+
import { z as z13 } from "zod";
|
|
4559
4680
|
var ORGANIZATION_PROMPT_NAME = "ckan-search-by-organization";
|
|
4560
4681
|
var buildOrganizationPromptText = (serverUrl, organization, rows) => `# Guided search: datasets by organization
|
|
4561
4682
|
|
|
@@ -4590,9 +4711,9 @@ var registerOrganizationPrompt = (server2) => {
|
|
|
4590
4711
|
title: "Search datasets by organization",
|
|
4591
4712
|
description: "Guided prompt to find a publisher and list its datasets.",
|
|
4592
4713
|
argsSchema: {
|
|
4593
|
-
server_url:
|
|
4594
|
-
organization:
|
|
4595
|
-
rows:
|
|
4714
|
+
server_url: z13.string().url().describe("Base URL of the CKAN server"),
|
|
4715
|
+
organization: z13.string().min(1).describe("Organization name or keyword"),
|
|
4716
|
+
rows: z13.coerce.number().int().positive().default(10).describe("Max results to return")
|
|
4596
4717
|
}
|
|
4597
4718
|
},
|
|
4598
4719
|
async ({ server_url, organization, rows }) => createTextPrompt(buildOrganizationPromptText(server_url, organization, rows))
|
|
@@ -4600,7 +4721,7 @@ var registerOrganizationPrompt = (server2) => {
|
|
|
4600
4721
|
};
|
|
4601
4722
|
|
|
4602
4723
|
// src/prompts/format.ts
|
|
4603
|
-
import { z as
|
|
4724
|
+
import { z as z14 } from "zod";
|
|
4604
4725
|
var FORMAT_PROMPT_NAME = "ckan-search-by-format";
|
|
4605
4726
|
var buildFormatPromptText = (serverUrl, format, rows) => `# Guided search: datasets by resource format
|
|
4606
4727
|
|
|
@@ -4624,9 +4745,9 @@ var registerFormatPrompt = (server2) => {
|
|
|
4624
4745
|
title: "Search datasets by resource format",
|
|
4625
4746
|
description: "Guided prompt to find datasets with a given resource format.",
|
|
4626
4747
|
argsSchema: {
|
|
4627
|
-
server_url:
|
|
4628
|
-
format:
|
|
4629
|
-
rows:
|
|
4748
|
+
server_url: z14.string().url().describe("Base URL of the CKAN server"),
|
|
4749
|
+
format: z14.string().min(1).describe("Resource format (e.g., CSV, JSON)"),
|
|
4750
|
+
rows: z14.coerce.number().int().positive().default(10).describe("Max results to return")
|
|
4630
4751
|
}
|
|
4631
4752
|
},
|
|
4632
4753
|
async ({ server_url, format, rows }) => createTextPrompt(buildFormatPromptText(server_url, format, rows))
|
|
@@ -4634,7 +4755,7 @@ var registerFormatPrompt = (server2) => {
|
|
|
4634
4755
|
};
|
|
4635
4756
|
|
|
4636
4757
|
// src/prompts/recent.ts
|
|
4637
|
-
import { z as
|
|
4758
|
+
import { z as z15 } from "zod";
|
|
4638
4759
|
var RECENT_PROMPT_NAME = "ckan-recent-datasets";
|
|
4639
4760
|
var buildRecentPromptText = (serverUrl, rows) => `# Guided search: recent datasets
|
|
4640
4761
|
|
|
@@ -4682,8 +4803,8 @@ var registerRecentPrompt = (server2) => {
|
|
|
4682
4803
|
title: "Find recently updated datasets",
|
|
4683
4804
|
description: "Guided prompt to list recently updated datasets on a CKAN portal.",
|
|
4684
4805
|
argsSchema: {
|
|
4685
|
-
server_url:
|
|
4686
|
-
rows:
|
|
4806
|
+
server_url: z15.string().url().describe("Base URL of the CKAN server"),
|
|
4807
|
+
rows: z15.coerce.number().int().positive().default(10).describe("Max results to return")
|
|
4687
4808
|
}
|
|
4688
4809
|
},
|
|
4689
4810
|
async ({ server_url, rows }) => createTextPrompt(buildRecentPromptText(server_url, rows))
|
|
@@ -4691,7 +4812,7 @@ var registerRecentPrompt = (server2) => {
|
|
|
4691
4812
|
};
|
|
4692
4813
|
|
|
4693
4814
|
// src/prompts/dataset-analysis.ts
|
|
4694
|
-
import { z as
|
|
4815
|
+
import { z as z16 } from "zod";
|
|
4695
4816
|
var DATASET_ANALYSIS_PROMPT_NAME = "ckan-analyze-dataset";
|
|
4696
4817
|
var buildDatasetAnalysisPromptText = (serverUrl, id) => `# Guided analysis: dataset
|
|
4697
4818
|
|
|
@@ -4733,8 +4854,8 @@ var registerDatasetAnalysisPrompt = (server2) => {
|
|
|
4733
4854
|
title: "Analyze a dataset",
|
|
4734
4855
|
description: "Guided prompt to inspect dataset metadata and explore DataStore tables.",
|
|
4735
4856
|
argsSchema: {
|
|
4736
|
-
server_url:
|
|
4737
|
-
id:
|
|
4857
|
+
server_url: z16.string().url().describe("Base URL of the CKAN server"),
|
|
4858
|
+
id: z16.string().min(1).describe("Dataset id or name (CKAN package id)")
|
|
4738
4859
|
}
|
|
4739
4860
|
},
|
|
4740
4861
|
async ({ server_url, id }) => createTextPrompt(buildDatasetAnalysisPromptText(server_url, id))
|
|
@@ -4742,7 +4863,7 @@ var registerDatasetAnalysisPrompt = (server2) => {
|
|
|
4742
4863
|
};
|
|
4743
4864
|
|
|
4744
4865
|
// src/prompts/hvd.ts
|
|
4745
|
-
import { z as
|
|
4866
|
+
import { z as z17 } from "zod";
|
|
4746
4867
|
var HVD_PROMPT_NAME = "ckan-search-hvd";
|
|
4747
4868
|
var buildHvdPromptText = (serverUrl, rows, categoryField) => {
|
|
4748
4869
|
if (!categoryField) {
|
|
@@ -4804,8 +4925,8 @@ var registerHvdPrompt = (server2) => {
|
|
|
4804
4925
|
title: "Search High-Value Datasets (HVD)",
|
|
4805
4926
|
description: "Guided prompt to find High-Value Datasets (HVD) on a CKAN portal. Automatically uses the correct filter field from portal configuration.",
|
|
4806
4927
|
argsSchema: {
|
|
4807
|
-
server_url:
|
|
4808
|
-
rows:
|
|
4928
|
+
server_url: z17.string().url().describe("Base URL of the CKAN server"),
|
|
4929
|
+
rows: z17.coerce.number().int().positive().default(10).describe("Max results to return")
|
|
4809
4930
|
}
|
|
4810
4931
|
},
|
|
4811
4932
|
async ({ server_url, rows }) => {
|
|
@@ -4829,7 +4950,7 @@ var registerAllPrompts = (server2) => {
|
|
|
4829
4950
|
function createServer() {
|
|
4830
4951
|
return new McpServer({
|
|
4831
4952
|
name: "ckan-mcp-server",
|
|
4832
|
-
version: "0.4.
|
|
4953
|
+
version: "0.4.77"
|
|
4833
4954
|
});
|
|
4834
4955
|
}
|
|
4835
4956
|
function registerAll(server2) {
|
|
@@ -4843,6 +4964,7 @@ function registerAll(server2) {
|
|
|
4843
4964
|
registerAnalyzeTools(server2);
|
|
4844
4965
|
registerCatalogStatsTools(server2);
|
|
4845
4966
|
registerSparqlTools(server2);
|
|
4967
|
+
registerPortalDiscoveryTools(server2);
|
|
4846
4968
|
registerAllResources(server2);
|
|
4847
4969
|
registerAllPrompts(server2);
|
|
4848
4970
|
}
|