@aborruso/ckan-mcp-server 0.4.129 → 0.4.130

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 (3) hide show
  1. package/LOG.md +10 -0
  2. package/dist/index.js +18 -6
  3. package/package.json +1 -1
package/LOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # LOG
2
2
 
3
+ ## 2026-10-19
4
+
5
+ ### v0.4.130
6
+
7
+ Ships #561: linear shrink in `truncateJson` (GHSA-w3gp-3hrh-v465). Tests 589 passed, smoke 17/17.
8
+
9
+ ### Security: linear shrink in `truncateJson` (GHSA-w3gp-3hrh-v465)
10
+
11
+ Reported by @olevasseur. `truncateJson()` popped one array element at a time and re-serialized the whole object after each pop: O(n²). A portal answering `status_show` (or any tool in `response_format: json`) with a ~3 MB, 32,000-item array blocked the event loop for ~59 s. Replaced with a binary search on the largest fitting prefix: same output (543 rows kept), 17 `JSON.stringify` calls instead of 31,459, 55 ms instead of 59 s. Regression test counts serializations. Tests 589 passed; e2e on dati.gov.it `status_show` and messina DataStore JSON (1000 records → 254 kept, valid JSON).
12
+
3
13
  ## 2026-10-06
4
14
 
5
15
  ### v0.4.129
package/dist/index.js CHANGED
@@ -1137,14 +1137,26 @@ function truncateJson(obj, limit = CHARACTER_LIMIT) {
1137
1137
  const data = structuredClone(obj);
1138
1138
  for (const key of SHRINKABLE_KEYS) {
1139
1139
  if (Array.isArray(data[key]) && data[key].length > 0) {
1140
- const originalCount = data[key].length;
1140
+ const original = data[key];
1141
+ const originalCount = original.length;
1141
1142
  data["_truncated"] = true;
1142
1143
  data["_original_count"] = originalCount;
1143
- while (data[key].length > 0) {
1144
- data[key].pop();
1145
- json = JSON.stringify(data);
1146
- if (json.length <= limit) return json;
1144
+ let low = 0;
1145
+ let high = originalCount - 1;
1146
+ let best = null;
1147
+ while (low <= high) {
1148
+ const mid = Math.floor((low + high) / 2);
1149
+ data[key] = original.slice(0, mid);
1150
+ const candidate = JSON.stringify(data);
1151
+ if (candidate.length <= limit) {
1152
+ best = candidate;
1153
+ low = mid + 1;
1154
+ } else {
1155
+ high = mid - 1;
1156
+ }
1147
1157
  }
1158
+ if (best !== null) return best;
1159
+ data[key] = [];
1148
1160
  }
1149
1161
  }
1150
1162
  const explained = JSON.stringify({
@@ -6089,7 +6101,7 @@ var registerAllPrompts = (server) => {
6089
6101
  function createServer() {
6090
6102
  return new McpServer({
6091
6103
  name: "ckan-mcp-server",
6092
- version: "0.4.129"
6104
+ version: "0.4.130"
6093
6105
  });
6094
6106
  }
6095
6107
  function registerAll(server) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aborruso/ckan-mcp-server",
3
- "version": "0.4.129",
3
+ "version": "0.4.130",
4
4
  "mcpName": "io.github.aborruso/ckan-mcp-server",
5
5
  "description": "MCP server for interacting with CKAN open data portals",
6
6
  "repository": {