@aborruso/ckan-mcp-server 0.4.13 → 0.4.14

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/CLAUDE.md CHANGED
@@ -41,7 +41,7 @@ The server exposes MCP tools for:
41
41
  # Build project (uses esbuild - fast and lightweight)
42
42
  npm run build
43
43
 
44
- # Run test suite (179 tests - unit + integration)
44
+ # Run test suite (191 tests - unit + integration)
45
45
  npm test
46
46
 
47
47
  # Watch mode for tests during development
@@ -73,7 +73,7 @@ npm run deploy # Deploy to Cloudflare Workers
73
73
  The project uses **esbuild** for compilation and **vitest** for testing:
74
74
 
75
75
  - **Build**: Ultra-fast builds (milliseconds instead of minutes)
76
- - **Tests**: 179 tests (unit + integration) with 100% success rate
76
+ - **Tests**: 191 tests (unit + integration) with 100% success rate
77
77
  - **Coverage**: ~39% overall (utils: 98%, tools: 15-20%) - available via vitest with v8 coverage engine
78
78
 
79
79
  The `build:tsc` script is available as a fallback but can cause memory issues in some environments (particularly WSL). Always use `npm run build` which uses esbuild.
@@ -88,12 +88,12 @@ The project has a comprehensive test suite using **Vitest**:
88
88
  tests/
89
89
  ├── unit/
90
90
  │ ├── formatting.test.ts # Utility functions (19 tests)
91
- │ ├── http.test.ts # HTTP client (6 tests)
91
+ │ ├── http.test.ts # HTTP client (11 tests)
92
92
  │ └── uri.test.ts # URI parsing (11 tests)
93
93
  ├── integration/
94
- │ ├── package.test.ts # Package tools (29 tests)
94
+ │ ├── package.test.ts # Package tools (31 tests)
95
95
  │ ├── organization.test.ts # Organization tools (6 tests)
96
- │ ├── datastore.test.ts # DataStore tools (17 tests)
96
+ │ ├── datastore.test.ts # DataStore tools (19 tests)
97
97
  │ ├── resources.test.ts # MCP Resources (11 tests)
98
98
  │ └── status.test.ts # Status tools (2 tests)
99
99
  └── fixtures/
@@ -101,7 +101,7 @@ tests/
101
101
  └── errors/ # Error scenario mocks
102
102
  ```
103
103
 
104
- **Test Coverage**: 179 tests total (85 unit + 94 integration)
104
+ **Test Coverage**: 191 tests total (117 unit + 74 integration)
105
105
 
106
106
  When making changes:
107
107
  1. Run tests before committing: `npm test`
@@ -341,7 +341,7 @@ To test with Claude Desktop, add the MCP configuration to the config file.
341
341
  - Direct data access for datasets, resources, organizations
342
342
 
343
343
  **v0.2.0 (2026-01-08)**: Comprehensive test suite
344
- - 179 tests (unit + integration)
344
+ - 191 tests (unit + integration)
345
345
  - ~39% code coverage (utils well-tested, tools improving)
346
346
 
347
347
  **v0.1.0 (2026-01-08)**: Modular refactoring
package/LOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # LOG
2
2
 
3
+ ## 2026-01-22
4
+
5
+ ### Date Query Auto-Conversion (v0.4.14)
6
+
7
+ - **Feature**: Auto-convert NOW-based date math for `modified` and `issued` fields
8
+ - **Problem**: CKAN Solr supports `NOW-XDAYS` syntax only on `metadata_modified` and `metadata_created` fields
9
+ - **Solution**: New `convertDateMathForUnsupportedFields()` automatically converts queries like `modified:[NOW-30DAYS TO NOW]` to ISO dates `modified:[2025-12-23T... TO 2026-01-22T...]`
10
+ - **Supported fields**: `modified`, `issued` (auto-converted) | `metadata_modified`, `metadata_created` (native NOW support)
11
+ - **Supported units**: DAYS, MONTHS, YEARS (singular and plural forms)
12
+ - **Tests**: +10 unit tests (201 total, all passing)
13
+ - **Documentation**: Updated tool description with NOW syntax limitations and examples
14
+ - **Files**: `src/utils/search.ts`, `src/tools/package.ts`, `tests/unit/search.test.ts`
15
+ - **No breaking changes**: Backward compatible - existing queries work unchanged
16
+
3
17
  ## 2026-01-19
4
18
 
5
19
  ### Search Parser Escaping
package/PRD.md CHANGED
@@ -214,7 +214,7 @@ An MCP server that exposes tools to interact with CKAN API v3, enabling AI agent
214
214
  - `wrangler@^4.58.0` - Cloudflare Workers CLI
215
215
 
216
216
  **Test Framework**:
217
- - `vitest@^4.0.16` - Test runner (190 tests, 100% passing)
217
+ - `vitest@^4.0.16` - Test runner (191 tests, 100% passing)
218
218
 
219
219
  ### 4.2 Architecture Diagram
220
220
 
@@ -825,7 +825,7 @@ ckan_package_search({
825
825
  ### 10.3 Testing & Quality
826
826
 
827
827
  ✅ **Current State**:
828
- - 190 unit and integration tests (100% passing)
828
+ - 191 unit and integration tests (100% passing)
829
829
  - vitest test runner
830
830
  - Coverage for all 13 tools
831
831
  - Fixtures for offline testing
@@ -860,7 +860,7 @@ ckan_package_search({
860
860
  - **Memory Usage**: < 50MB runtime (Node.js), Workers limits apply
861
861
  - **Response Time**: < 30s (CKAN API timeout), < 10s (Workers)
862
862
  - **Cold Start**: < 60ms (Cloudflare Workers)
863
- - **Test Coverage**: 190 tests (100% passing)
863
+ - **Test Coverage**: 191 tests (100% passing)
864
864
 
865
865
  ### 11.2 Distribution Metrics
866
866
 
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 186 tests (100% passing)
20
+ - 🧪 Test suite with 191 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 (186 tests)
48
+ # Run tests (191 tests)
49
49
  npm test
50
50
  ```
51
51
 
@@ -117,22 +117,29 @@ Then add to `claude_desktop_config.json`:
117
117
  }
118
118
  ```
119
119
 
120
- #### Option 2: Local Installation (Optional)
120
+ #### Option 2: Local Project Installation (Optional)
121
121
 
122
- If you installed locally (see Installation), use this config:
122
+ If you want to install the server in a specific project:
123
+
124
+ ```bash
125
+ cd your-project
126
+ npm install @aborruso/ckan-mcp-server
127
+ ```
128
+
129
+ Then add to `claude_desktop_config.json`:
123
130
 
124
131
  ```json
125
132
  {
126
133
  "mcpServers": {
127
134
  "ckan": {
128
- "command": "node",
129
- "args": ["/absolute/path/to/project/node_modules/@username/ckan-mcp-server/dist/index.js"]
135
+ "command": "npx",
136
+ "args": ["@aborruso/ckan-mcp-server"]
130
137
  }
131
138
  }
132
139
  }
133
140
  ```
134
141
 
135
- Replace `/absolute/path/to/project` with your actual project path.
142
+ **Note**: `npx` will use the locally installed package in `node_modules`. Make sure to install the package first.
136
143
 
137
144
  #### Option 3: From Source
138
145
 
@@ -559,7 +566,7 @@ ckan-mcp-server/
559
566
  │ └── transport/
560
567
  │ ├── stdio.ts # Stdio transport
561
568
  │ └── http.ts # HTTP transport
562
- ├── tests/ # Test suite (184 tests)
569
+ ├── tests/ # Test suite (191 tests)
563
570
  ├── dist/ # Compiled files (generated)
564
571
  ├── package.json
565
572
  └── README.md
package/dist/index.js CHANGED
@@ -168,6 +168,31 @@ function isFieldedQuery(query) {
168
168
  function escapeSolrQuery(query) {
169
169
  return query.replace(SOLR_SPECIAL_CHARS, "\\$&");
170
170
  }
171
+ function convertDateMathForUnsupportedFields(query) {
172
+ const now = /* @__PURE__ */ new Date();
173
+ const nowIso = now.toISOString();
174
+ const pattern = /\b(?!metadata_)(modified|issued):\[NOW-(\d+)(DAYS?|MONTHS?|YEARS?)\s+TO\s+NOW\]/gi;
175
+ return query.replace(pattern, (match, field, amount, unit) => {
176
+ const amountNum = parseInt(amount, 10);
177
+ const startDate = new Date(now);
178
+ const normalizedUnit = unit.toLowerCase().replace(/s$/, "");
179
+ switch (normalizedUnit) {
180
+ case "day":
181
+ startDate.setDate(startDate.getDate() - amountNum);
182
+ break;
183
+ case "month":
184
+ startDate.setMonth(startDate.getMonth() - amountNum);
185
+ break;
186
+ case "year":
187
+ startDate.setFullYear(startDate.getFullYear() - amountNum);
188
+ break;
189
+ default:
190
+ return match;
191
+ }
192
+ const startIso = startDate.toISOString();
193
+ return `${field}:[${startIso} TO ${nowIso}]`;
194
+ });
195
+ }
171
196
  function resolveSearchQuery(serverUrl, query, parserOverride) {
172
197
  const portalSearchConfig = getPortalSearchConfig(serverUrl);
173
198
  const portalForce = portalSearchConfig.force_text_field ?? false;
@@ -180,7 +205,8 @@ function resolveSearchQuery(serverUrl, query, parserOverride) {
180
205
  const trimmedQuery = query.trim();
181
206
  forceTextField = trimmedQuery !== DEFAULT_SEARCH_QUERY && !isFieldedQuery(trimmedQuery);
182
207
  }
183
- const effectiveQuery = forceTextField ? `text:(${escapeSolrQuery(query)})` : query;
208
+ let effectiveQuery = forceTextField ? `text:(${escapeSolrQuery(query)})` : query;
209
+ effectiveQuery = convertDateMathForUnsupportedFields(effectiveQuery);
184
210
  return { effectiveQuery, forcedTextField: forceTextField };
185
211
  }
186
212
 
@@ -333,6 +359,9 @@ Query Syntax (parameter q):
333
359
  - NOW/DAY, NOW/MONTH (round down)
334
360
  - Combined: "metadata_modified:[NOW-2MONTHS TO NOW]"
335
361
  - Example: "metadata_created:[NOW-1YEAR TO *]"
362
+ - IMPORTANT: NOW syntax works on metadata_modified and metadata_created fields
363
+ - For 'modified' and 'issued' fields, NOW syntax is auto-converted to ISO dates
364
+ - Manual ISO dates always work: "modified:[2026-01-15T00:00:00Z TO *]"
336
365
 
337
366
  Field existence:
338
367
  - Exists: "field:*" or "field:[* TO *]"
@@ -351,6 +380,7 @@ Examples:
351
380
  - Proximity: { q: "notes:"open data"~3" }
352
381
  - Date range: { q: "metadata_modified:[2024-01-01T00:00:00Z TO 2024-12-31T23:59:59Z]" }
353
382
  - Date math: { q: "metadata_modified:[NOW-6MONTHS TO *]" }
383
+ - Date math (auto-converted): { q: "modified:[NOW-30DAYS TO NOW]" }
354
384
  - Field exists: { q: "organization:* AND num_resources:[1 TO *]" }
355
385
  - Boosting: { q: "title:climate^2 OR notes:climate" }
356
386
  - Filter org: { fq: "organization:regione-siciliana" }