@aborruso/ckan-mcp-server 0.4.112 → 0.4.114

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.
Files changed (4) hide show
  1. package/LOG.md +54 -25
  2. package/dist/index.js +107 -107
  3. package/dist/worker.js +112 -115
  4. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1009,26 +1009,58 @@ function truncateText(text, limit = CHARACTER_LIMIT) {
1009
1009
 
1010
1010
  ... [Response truncated at ${limit} characters]`;
1011
1011
  }
1012
+ var SHRINKABLE_KEYS = [
1013
+ "results",
1014
+ "records",
1015
+ "rows",
1016
+ "datasets",
1017
+ "resources",
1018
+ "packages",
1019
+ "organizations",
1020
+ "groups",
1021
+ "portals",
1022
+ "tags",
1023
+ "facets",
1024
+ "fields"
1025
+ ];
1012
1026
  function truncateJson(obj, limit = CHARACTER_LIMIT) {
1013
1027
  let json = JSON.stringify(obj, null, 2);
1014
1028
  if (json.length <= limit) return json;
1015
1029
  json = JSON.stringify(obj);
1016
1030
  if (json.length <= limit) return json;
1017
1031
  const data = structuredClone(obj);
1018
- const arrayKeys = ["results", "records", "resources", "packages", "organizations", "groups", "tags"];
1019
- for (const key of arrayKeys) {
1032
+ for (const key of SHRINKABLE_KEYS) {
1020
1033
  if (Array.isArray(data[key]) && data[key].length > 0) {
1021
1034
  const originalCount = data[key].length;
1022
- while (data[key].length > 1) {
1035
+ data["_truncated"] = true;
1036
+ data["_original_count"] = originalCount;
1037
+ while (data[key].length > 0) {
1023
1038
  data[key].pop();
1024
- data["_truncated"] = true;
1025
- data["_original_count"] = originalCount;
1026
1039
  json = JSON.stringify(data);
1027
1040
  if (json.length <= limit) return json;
1028
1041
  }
1029
1042
  }
1030
1043
  }
1031
- return truncateText(JSON.stringify(data), limit);
1044
+ const explained = JSON.stringify({
1045
+ _truncated: true,
1046
+ _error: `Response exceeded the ${limit} character limit and could not be reduced to fit. Narrow the query or use pagination.`
1047
+ });
1048
+ if (explained.length <= limit) return explained;
1049
+ const bare = JSON.stringify({ _truncated: true });
1050
+ return bare.length <= limit ? bare : "{}";
1051
+ }
1052
+ function jsonToolResult(obj, limit = CHARACTER_LIMIT) {
1053
+ const text = truncateJson(obj, limit);
1054
+ return {
1055
+ content: [{ type: "text", text }],
1056
+ structuredContent: JSON.parse(text)
1057
+ };
1058
+ }
1059
+ function cappedStructured(obj, limit = CHARACTER_LIMIT) {
1060
+ return JSON.parse(truncateJson(obj, limit));
1061
+ }
1062
+ function formatError(message, asJson) {
1063
+ return asJson ? JSON.stringify({ error: message, _error: true }, null, 2) : message;
1032
1064
  }
1033
1065
  function wrapUntrusted(text) {
1034
1066
  const safe = String(text).replace(/```/g, "\u02BC\u02BC\u02BC");
@@ -2016,7 +2048,7 @@ Note: showing top ${sorted.length} only. Use \`response_format: json\` for full
2016
2048
  };
2017
2049
  } catch (error) {
2018
2050
  return {
2019
- content: [{ type: "text", text: formatCkanError(error, "ckan_package_search") }],
2051
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_package_search"), params.response_format === "json" /* JSON */) }],
2020
2052
  isError: true
2021
2053
  };
2022
2054
  }
@@ -2136,10 +2168,7 @@ Typical workflow: ckan_find_relevant_datasets \u2192 ckan_package_show (inspect
2136
2168
  results: top
2137
2169
  };
2138
2170
  if (params.response_format === "json" /* JSON */) {
2139
- return {
2140
- content: [{ type: "text", text: truncateText(JSON.stringify(payload, null, 2)) }],
2141
- structuredContent: payload
2142
- };
2171
+ return jsonToolResult(payload);
2143
2172
  }
2144
2173
  let markdown = `# Relevant CKAN Datasets
2145
2174
 
@@ -2218,7 +2247,7 @@ Typical workflow: ckan_find_relevant_datasets \u2192 ckan_package_show (inspect
2218
2247
  };
2219
2248
  } catch (error) {
2220
2249
  return {
2221
- content: [{ type: "text", text: formatCkanError(error, "ckan_find_relevant_datasets") }],
2250
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_find_relevant_datasets"), params.response_format === "json" /* JSON */) }],
2222
2251
  isError: true
2223
2252
  };
2224
2253
  }
@@ -2283,10 +2312,7 @@ Typical workflow: ckan_package_show \u2192 pick a resource with datastore_active
2283
2312
  );
2284
2313
  if (params.response_format === "json" /* JSON */) {
2285
2314
  const compact = compactPackageShow(enrichPackageShowResult(result), params.server_url);
2286
- return {
2287
- content: [{ type: "text", text: truncateJson(compact) }],
2288
- structuredContent: compact
2289
- };
2315
+ return jsonToolResult(compact);
2290
2316
  }
2291
2317
  const markdown = formatPackageShowMarkdown(result, params.server_url);
2292
2318
  return {
@@ -2294,7 +2320,7 @@ Typical workflow: ckan_package_show \u2192 pick a resource with datastore_active
2294
2320
  };
2295
2321
  } catch (error) {
2296
2322
  return {
2297
- content: [{ type: "text", text: formatCkanError(error, "ckan_package_show") }],
2323
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_package_show"), params.response_format === "json" /* JSON */) }],
2298
2324
  isError: true
2299
2325
  };
2300
2326
  }
@@ -2395,10 +2421,7 @@ dataset's own resource URLs); set check_source_portal=true to enable it.`,
2395
2421
  format_filter: formatFilter ?? null,
2396
2422
  resources: summary
2397
2423
  };
2398
- return {
2399
- content: [{ type: "text", text: truncateText(JSON.stringify(payload, null, 2)) }],
2400
- structuredContent: payload
2401
- };
2424
+ return jsonToolResult(payload);
2402
2425
  }
2403
2426
  let markdown = `# Resources: ${result.title || result.name}
2404
2427
 
@@ -2459,7 +2482,7 @@ dataset's own resource URLs); set check_source_portal=true to enable it.`,
2459
2482
  };
2460
2483
  } catch (error) {
2461
2484
  return {
2462
- content: [{ type: "text", text: formatCkanError(error, "ckan_list_resources") }],
2485
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_list_resources"), params.response_format === "json" /* JSON */) }],
2463
2486
  isError: true
2464
2487
  };
2465
2488
  }
@@ -2668,10 +2691,7 @@ Typical workflow: ckan_organization_list \u2192 ckan_organization_show (inspect
2668
2691
  }));
2669
2692
  if (params.response_format === "json" /* JSON */) {
2670
2693
  const output = { count: items.length, organizations };
2671
- return {
2672
- content: [{ type: "text", text: truncateJson(output) }],
2673
- structuredContent: output
2674
- };
2694
+ return jsonToolResult(output);
2675
2695
  }
2676
2696
  let markdown2 = `# CKAN Organizations
2677
2697
 
@@ -2704,10 +2724,7 @@ Note: organization_list returned 500; using package_search facets.
2704
2724
  }
2705
2725
  if (params.response_format === "json" /* JSON */) {
2706
2726
  const compact = compactOrganizationList(result);
2707
- return {
2708
- content: [{ type: "text", text: truncateJson(compact) }],
2709
- structuredContent: compact
2710
- };
2727
+ return jsonToolResult(compact);
2711
2728
  }
2712
2729
  let markdown = `# CKAN Organizations
2713
2730
 
@@ -2746,7 +2763,7 @@ Note: organization_list returned 500; using package_search facets.
2746
2763
  };
2747
2764
  } catch (error) {
2748
2765
  return {
2749
- content: [{ type: "text", text: formatCkanError(error, "ckan_organization_list") }],
2766
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_organization_list"), params.response_format === "json" /* JSON */) }],
2750
2767
  isError: true
2751
2768
  };
2752
2769
  }
@@ -2796,10 +2813,7 @@ Typical workflow: ckan_organization_show \u2192 ckan_package_show (inspect a dat
2796
2813
  );
2797
2814
  if (params.response_format === "json" /* JSON */) {
2798
2815
  const compact = compactOrganizationShow(result, params.server_url);
2799
- return {
2800
- content: [{ type: "text", text: truncateJson(compact) }],
2801
- structuredContent: compact
2802
- };
2816
+ return jsonToolResult(compact);
2803
2817
  }
2804
2818
  const markdown = formatOrganizationShowMarkdown(result, params.server_url);
2805
2819
  return {
@@ -2807,7 +2821,7 @@ Typical workflow: ckan_organization_show \u2192 ckan_package_show (inspect a dat
2807
2821
  };
2808
2822
  } catch (error) {
2809
2823
  return {
2810
- content: [{ type: "text", text: formatCkanError(error, "ckan_organization_show") }],
2824
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_organization_show"), params.response_format === "json" /* JSON */) }],
2811
2825
  isError: true
2812
2826
  };
2813
2827
  }
@@ -2873,10 +2887,7 @@ Typical workflow: ckan_organization_search \u2192 ckan_organization_show (get de
2873
2887
  view_url: getOrganizationViewUrl(params.server_url, { name: item.name })
2874
2888
  }))
2875
2889
  };
2876
- return {
2877
- content: [{ type: "text", text: truncateText(JSON.stringify(jsonResult, null, 2)) }],
2878
- structuredContent: jsonResult
2879
- };
2890
+ return jsonToolResult(jsonResult);
2880
2891
  }
2881
2892
  let markdown = `# CKAN Organization Search Results
2882
2893
 
@@ -2915,7 +2926,7 @@ Typical workflow: ckan_organization_search \u2192 ckan_organization_show (get de
2915
2926
  };
2916
2927
  } catch (error) {
2917
2928
  return {
2918
- content: [{ type: "text", text: formatCkanError(error, "ckan_organization_search") }],
2929
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_organization_search"), params.response_format === "json" /* JSON */) }],
2919
2930
  isError: true
2920
2931
  };
2921
2932
  }
@@ -3127,7 +3138,7 @@ Typical workflow: ckan_package_search \u2192 ckan_package_show (find resource_id
3127
3138
  };
3128
3139
  } catch (error) {
3129
3140
  return {
3130
- content: [{ type: "text", text: formatCkanError(error, "ckan_datastore_search") }],
3141
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_datastore_search"), params.response_format === "json" /* JSON */) }],
3131
3142
  isError: true
3132
3143
  };
3133
3144
  }
@@ -3177,10 +3188,7 @@ Security note: SQL queries are forwarded directly to the CKAN DataStore API. The
3177
3188
  );
3178
3189
  if (params.response_format === "json" /* JSON */) {
3179
3190
  const compact = compactDatastoreResult(result);
3180
- return {
3181
- content: [{ type: "text", text: truncateJson(compact) }],
3182
- structuredContent: compact
3183
- };
3191
+ return jsonToolResult(compact);
3184
3192
  }
3185
3193
  const markdown = formatDatastoreSqlMarkdown(result, params.server_url, params.sql);
3186
3194
  return {
@@ -3188,7 +3196,7 @@ Security note: SQL queries are forwarded directly to the CKAN DataStore API. The
3188
3196
  };
3189
3197
  } catch (error) {
3190
3198
  return {
3191
- content: [{ type: "text", text: formatCkanError(error, "ckan_datastore_search_sql") }],
3199
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_datastore_search_sql"), params.response_format === "json" /* JSON */) }],
3192
3200
  isError: true
3193
3201
  };
3194
3202
  }
@@ -3251,8 +3259,8 @@ Typical workflow: ckan_status_show (verify server is up) \u2192 ckan_package_sea
3251
3259
  ]);
3252
3260
  const markdown = formatStatusMarkdown(result, params.server_url, hvdCount);
3253
3261
  return {
3254
- content: [{ type: "text", text: addDemoFooter(markdown) }],
3255
- structuredContent: result
3262
+ content: [{ type: "text", text: truncateText(addDemoFooter(markdown)) }],
3263
+ structuredContent: cappedStructured(result)
3256
3264
  };
3257
3265
  } catch (error) {
3258
3266
  return {
@@ -3362,10 +3370,7 @@ Typical workflow: ckan_tag_list \u2192 ckan_package_search with fq="tags:tag_nam
3362
3370
  count: tags.length,
3363
3371
  tags
3364
3372
  };
3365
- return {
3366
- content: [{ type: "text", text: truncateText(JSON.stringify(output, null, 2)) }],
3367
- structuredContent: output
3368
- };
3373
+ return jsonToolResult(output);
3369
3374
  }
3370
3375
  let markdown = `# CKAN Tags
3371
3376
 
@@ -3397,7 +3402,7 @@ Typical workflow: ckan_tag_list \u2192 ckan_package_search with fq="tags:tag_nam
3397
3402
  return {
3398
3403
  content: [{
3399
3404
  type: "text",
3400
- text: `Error listing tags: ${error instanceof Error ? error.message : String(error)}`
3405
+ text: formatError(`Error listing tags: ${error instanceof Error ? error.message : String(error)}`, params.response_format === "json" /* JSON */)
3401
3406
  }],
3402
3407
  isError: true
3403
3408
  };
@@ -3598,10 +3603,7 @@ Typical workflow: ckan_group_list \u2192 ckan_group_show (inspect one) \u2192 ck
3598
3603
  );
3599
3604
  if (params.response_format === "json" /* JSON */) {
3600
3605
  const compact = compactGroupList(result);
3601
- return {
3602
- content: [{ type: "text", text: truncateJson(compact) }],
3603
- structuredContent: compact
3604
- };
3606
+ return jsonToolResult(compact);
3605
3607
  }
3606
3608
  let markdown = `# CKAN Groups
3607
3609
 
@@ -3640,7 +3642,7 @@ Typical workflow: ckan_group_list \u2192 ckan_group_show (inspect one) \u2192 ck
3640
3642
  };
3641
3643
  } catch (error) {
3642
3644
  return {
3643
- content: [{ type: "text", text: formatCkanError(error, "ckan_group_list") }],
3645
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_group_list"), params.response_format === "json" /* JSON */) }],
3644
3646
  isError: true
3645
3647
  };
3646
3648
  }
@@ -3687,10 +3689,7 @@ Typical workflow: ckan_group_show \u2192 ckan_package_show (inspect a dataset) \
3687
3689
  );
3688
3690
  if (params.response_format === "json" /* JSON */) {
3689
3691
  const compact = compactGroupShow(result);
3690
- return {
3691
- content: [{ type: "text", text: truncateJson(compact) }],
3692
- structuredContent: compact
3693
- };
3692
+ return jsonToolResult(compact);
3694
3693
  }
3695
3694
  const markdown = formatGroupShowMarkdown(result, params.server_url);
3696
3695
  return {
@@ -3698,7 +3697,7 @@ Typical workflow: ckan_group_show \u2192 ckan_package_show (inspect a dataset) \
3698
3697
  };
3699
3698
  } catch (error) {
3700
3699
  return {
3701
- content: [{ type: "text", text: formatCkanError(error, "ckan_group_show") }],
3700
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_group_show"), params.response_format === "json" /* JSON */) }],
3702
3701
  isError: true
3703
3702
  };
3704
3703
  }
@@ -3759,10 +3758,7 @@ Typical workflow: ckan_group_search \u2192 ckan_group_show (get details) \u2192
3759
3758
  dataset_count: group.count
3760
3759
  }))
3761
3760
  };
3762
- return {
3763
- content: [{ type: "text", text: truncateText(JSON.stringify(jsonResult, null, 2)) }],
3764
- structuredContent: jsonResult
3765
- };
3761
+ return jsonToolResult(jsonResult);
3766
3762
  }
3767
3763
  let markdown = `# CKAN Group Search Results
3768
3764
 
@@ -3800,7 +3796,7 @@ Typical workflow: ckan_group_search \u2192 ckan_group_show (get details) \u2192
3800
3796
  };
3801
3797
  } catch (error) {
3802
3798
  return {
3803
- content: [{ type: "text", text: formatCkanError(error, "ckan_group_search") }],
3799
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_group_search"), params.response_format === "json" /* JSON */) }],
3804
3800
  isError: true
3805
3801
  };
3806
3802
  }
@@ -4457,25 +4453,31 @@ function registerQualityTools(server) {
4457
4453
  return {
4458
4454
  content: [{
4459
4455
  type: "text",
4460
- text: `Error: MQA quality metrics are only available for dati.gov.it datasets. Provided server: ${server_url}
4456
+ text: formatError(
4457
+ `Error: MQA quality metrics are only available for dati.gov.it datasets. Provided server: ${server_url}
4461
4458
 
4462
- The MQA (Metadata Quality Assurance) system is operated by data.europa.eu and only evaluates datasets from Italian open data portal.`
4463
- }]
4459
+ The MQA (Metadata Quality Assurance) system is operated by data.europa.eu and only evaluates datasets from Italian open data portal.`,
4460
+ response_format === "json" /* JSON */
4461
+ )
4462
+ }],
4463
+ isError: true
4464
4464
  };
4465
4465
  }
4466
4466
  try {
4467
4467
  const qualityData = await getMqaQuality(server_url, dataset_id);
4468
4468
  const format = response_format || "markdown" /* MARKDOWN */;
4469
- const output = format === "json" /* JSON */ ? JSON.stringify(qualityData, null, 2) : formatQualityMarkdown(qualityData, dataset_id);
4469
+ const output = format === "json" /* JSON */ ? truncateJson(qualityData) : truncateText(addDemoFooter(formatQualityMarkdown(qualityData, dataset_id)));
4470
4470
  return {
4471
4471
  content: [{
4472
4472
  type: "text",
4473
- text: addDemoFooter(output)
4473
+ text: output
4474
4474
  }]
4475
4475
  };
4476
4476
  } catch (error) {
4477
+ const message = `Error retrieving quality metrics: ${formatCkanError(error, "ckan_get_mqa_quality")}`;
4477
4478
  return {
4478
- content: [{ type: "text", text: `Error retrieving quality metrics: ${formatCkanError(error, "ckan_get_mqa_quality")}` }]
4479
+ content: [{ type: "text", text: formatError(message, response_format === "json" /* JSON */) }],
4480
+ isError: true
4479
4481
  };
4480
4482
  }
4481
4483
  }
@@ -4502,25 +4504,31 @@ The MQA (Metadata Quality Assurance) system is operated by data.europa.eu and on
4502
4504
  return {
4503
4505
  content: [{
4504
4506
  type: "text",
4505
- text: `Error: MQA quality details are only available for dati.gov.it datasets. Provided server: ${server_url}
4507
+ text: formatError(
4508
+ `Error: MQA quality details are only available for dati.gov.it datasets. Provided server: ${server_url}
4506
4509
 
4507
- The MQA (Metadata Quality Assurance) system is operated by data.europa.eu and only evaluates datasets from Italian open data portal.`
4508
- }]
4510
+ The MQA (Metadata Quality Assurance) system is operated by data.europa.eu and only evaluates datasets from Italian open data portal.`,
4511
+ response_format === "json" /* JSON */
4512
+ )
4513
+ }],
4514
+ isError: true
4509
4515
  };
4510
4516
  }
4511
4517
  try {
4512
4518
  const details = await getMqaQualityDetails(server_url, dataset_id);
4513
4519
  const format = response_format || "markdown" /* MARKDOWN */;
4514
- const output = format === "json" /* JSON */ ? JSON.stringify(details, null, 2) : formatQualityDetailsMarkdown(details, dataset_id);
4520
+ const output = format === "json" /* JSON */ ? truncateJson(details) : truncateText(addDemoFooter(formatQualityDetailsMarkdown(details, dataset_id)));
4515
4521
  return {
4516
4522
  content: [{
4517
4523
  type: "text",
4518
- text: addDemoFooter(output)
4524
+ text: output
4519
4525
  }]
4520
4526
  };
4521
4527
  } catch (error) {
4528
+ const message = `Error retrieving quality details: ${formatCkanError(error, "ckan_get_mqa_quality_details")}`;
4522
4529
  return {
4523
- content: [{ type: "text", text: `Error retrieving quality details: ${formatCkanError(error, "ckan_get_mqa_quality_details")}` }]
4530
+ content: [{ type: "text", text: formatError(message, response_format === "json" /* JSON */) }],
4531
+ isError: true
4524
4532
  };
4525
4533
  }
4526
4534
  }
@@ -4686,7 +4694,7 @@ Typical workflow: ckan_analyze_datasets \u2192 ckan_datastore_search (with known
4686
4694
  }));
4687
4695
  if (params.response_format === "json" /* JSON */) {
4688
4696
  return {
4689
- content: [{ type: "text", text: truncateText(JSON.stringify({ total: searchResult.count, datasets: analyzed }, null, 2)) }]
4697
+ content: [{ type: "text", text: truncateJson({ total: searchResult.count, datasets: analyzed }) }]
4690
4698
  };
4691
4699
  }
4692
4700
  const markdown = formatAnalyzeDatasetsMarkdown(
@@ -4700,7 +4708,7 @@ Typical workflow: ckan_analyze_datasets \u2192 ckan_datastore_search (with known
4700
4708
  };
4701
4709
  } catch (error) {
4702
4710
  return {
4703
- content: [{ type: "text", text: formatCkanError(error, "ckan_analyze_datasets") }],
4711
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_analyze_datasets"), params.response_format === "json" /* JSON */) }],
4704
4712
  isError: true
4705
4713
  };
4706
4714
  }
@@ -4779,7 +4787,7 @@ Typical workflow: ckan_catalog_stats (understand the portal) \u2192 ckan_package
4779
4787
  );
4780
4788
  if (params.response_format === "json" /* JSON */) {
4781
4789
  return {
4782
- content: [{ type: "text", text: truncateText(JSON.stringify({ total: result.count, facets: result.facets }, null, 2)) }]
4790
+ content: [{ type: "text", text: truncateJson({ total: result.count, facets: result.facets }) }]
4783
4791
  };
4784
4792
  }
4785
4793
  const markdown = formatCatalogStatsMarkdown(params.server_url, result.count, result.facets);
@@ -4788,7 +4796,7 @@ Typical workflow: ckan_catalog_stats (understand the portal) \u2192 ckan_package
4788
4796
  };
4789
4797
  } catch (error) {
4790
4798
  return {
4791
- content: [{ type: "text", text: formatCkanError(error, "ckan_catalog_stats") }],
4799
+ content: [{ type: "text", text: formatError(formatCkanError(error, "ckan_catalog_stats"), params.response_format === "json" /* JSON */) }],
4792
4800
  isError: true
4793
4801
  };
4794
4802
  }
@@ -4952,26 +4960,18 @@ Typical workflow: sparql_query (explore schema) \u2192 sparql_query (targeted qu
4952
4960
  const data = await querySparqlEndpoint(params.endpoint_url, limitedQuery);
4953
4961
  if (params.response_format === "json" /* JSON */) {
4954
4962
  const result = formatSparqlJson(data);
4955
- let text2 = JSON.stringify(result, null, 2);
4956
- if (text2.length > CHARACTER_LIMIT) {
4957
- text2 = text2.slice(0, CHARACTER_LIMIT) + "\n/* output truncated */";
4958
- }
4959
- return {
4960
- content: [{ type: "text", text: text2 }],
4961
- structuredContent: result
4962
- };
4963
- }
4964
- let text = formatSparqlMarkdown(data, params.endpoint_url);
4965
- if (text.length > CHARACTER_LIMIT) {
4966
- text = text.slice(0, CHARACTER_LIMIT) + "\n\n_Output truncated._";
4963
+ return jsonToolResult(result);
4967
4964
  }
4968
- return { content: [{ type: "text", text }] };
4965
+ return {
4966
+ content: [{ type: "text", text: truncateText(formatSparqlMarkdown(data, params.endpoint_url)) }]
4967
+ };
4969
4968
  } catch (error) {
4969
+ const message = `SPARQL query failed:
4970
+ ${error instanceof Error ? error.message : String(error)}`;
4970
4971
  return {
4971
4972
  content: [{
4972
4973
  type: "text",
4973
- text: `SPARQL query failed:
4974
- ${error instanceof Error ? error.message : String(error)}`
4974
+ text: formatError(message, params.response_format === "json" /* JSON */)
4975
4975
  }],
4976
4976
  isError: true
4977
4977
  };
@@ -5081,8 +5081,8 @@ Typical workflow: ckan_find_portals (discover portal URL) \u2192 ckan_status_sho
5081
5081
  });
5082
5082
  const markdown = formatMarkdown(results, active.length, params.limit);
5083
5083
  return {
5084
- content: [{ type: "text", text: addDemoFooter(markdown) }],
5085
- structuredContent: { portals: results.map((p) => ({
5084
+ content: [{ type: "text", text: truncateText(addDemoFooter(markdown)) }],
5085
+ structuredContent: cappedStructured({ portals: results.map((p) => ({
5086
5086
  url: p.Href,
5087
5087
  title: p.SiteInfo.site_title,
5088
5088
  country: p.Coordinates.country_name,
@@ -5090,7 +5090,7 @@ Typical workflow: ckan_find_portals (discover portal URL) \u2192 ckan_status_sho
5090
5090
  datasets: p.DatasetsNumber,
5091
5091
  locale: p.SiteInfo.locale_default,
5092
5092
  has_datastore: (p.Plugins || []).includes("datastore")
5093
- })) }
5093
+ })) })
5094
5094
  };
5095
5095
  } catch (error) {
5096
5096
  return {
@@ -5144,7 +5144,7 @@ function registerDatasetResource(server) {
5144
5144
  const result = await makeCkanRequest(serverUrl, "package_show", {
5145
5145
  id
5146
5146
  });
5147
- const content = truncateText(JSON.stringify(result, null, 2));
5147
+ const content = truncateJson(result);
5148
5148
  return {
5149
5149
  contents: [
5150
5150
  {
@@ -5188,7 +5188,7 @@ function registerResourceResource(server) {
5188
5188
  const result = await makeCkanRequest(serverUrl, "resource_show", {
5189
5189
  id
5190
5190
  });
5191
- const content = truncateText(JSON.stringify(result, null, 2));
5191
+ const content = truncateJson(result);
5192
5192
  return {
5193
5193
  contents: [
5194
5194
  {
@@ -5239,7 +5239,7 @@ function registerOrganizationResource(server) {
5239
5239
  include_datasets: false
5240
5240
  }
5241
5241
  );
5242
- const content = truncateText(JSON.stringify(result, null, 2));
5242
+ const content = truncateJson(result);
5243
5243
  return {
5244
5244
  contents: [
5245
5245
  {
@@ -5298,7 +5298,7 @@ var registerDatasetFilterResource = (server, config) => {
5298
5298
  q: "*:*",
5299
5299
  fq
5300
5300
  });
5301
- const content = truncateText(JSON.stringify(result, null, 2));
5301
+ const content = truncateJson(result);
5302
5302
  return {
5303
5303
  contents: [
5304
5304
  {