@aborruso/ckan-mcp-server 0.4.0 → 0.4.1

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
@@ -348,7 +348,7 @@ To test with Claude Desktop, add the MCP configuration to the config file.
348
348
  ### Known Limitations
349
349
 
350
350
  - **Output limit**: 50,000 characters hardcoded in `types.ts` (could be configurable)
351
- - **Date formatting**: Uses 'it-IT' locale in `utils/formatting.ts` (could be parameterized)
351
+ - **Date formatting**: Uses fixed ISO `YYYY-MM-DD` in `utils/formatting.ts` (could be configurable)
352
352
  - **Read-only**: All tools are read-only (no data modification on CKAN)
353
353
  - **No caching**: Every request makes fresh HTTP call to CKAN APIs
354
354
  - **No authentication**: Uses only public CKAN endpoints
@@ -358,7 +358,7 @@ To test with Claude Desktop, add the MCP configuration to the config file.
358
358
 
359
359
  1. Create new file in `src/tools/`
360
360
  2. Export `registerXxxTools(server: McpServer)` function
361
- 3. Import and call in both `src/index.ts` and `src/worker.ts`
361
+ 3. Add to `registerAll()` in `src/server.ts`
362
362
  4. Add tests in `tests/integration/`
363
363
  5. Build and test: `npm run build && npm test`
364
364
 
package/EXAMPLES.md CHANGED
@@ -289,6 +289,56 @@ ckan_package_search({
289
289
 
290
290
  CKAN uses Apache Solr for search. The `q` parameter supports advanced Solr query syntax including fuzzy matching, proximity search, boosting, and complex boolean logic.
291
291
 
292
+ ### Understanding Solr Field Types: Exact vs Fuzzy Search
293
+
294
+ **Important**: CKAN's Solr schema defines two main field types that behave differently in searches:
295
+
296
+ #### String Fields (type="string")
297
+ - **Behavior**: Exact match, case-sensitive, no normalization
298
+ - **Fields**: `res_format`, `tags`, `organization`, `license`, `license_id`, `state`, `name`
299
+ - **Example**: `res_format:CSV` finds 43,836 results, but `res_format:csv` finds 0 results
300
+
301
+ ```typescript
302
+ // Works - exact match
303
+ ckan_package_search({
304
+ server_url: "https://www.dati.gov.it/opendata",
305
+ fq: "res_format:CSV",
306
+ rows: 10
307
+ })
308
+ // → 43,836 results
309
+
310
+ // Fails - wrong case
311
+ ckan_package_search({
312
+ server_url: "https://www.dati.gov.it/opendata",
313
+ fq: "res_format:csv",
314
+ rows: 10
315
+ })
316
+ // → 0 results
317
+ ```
318
+
319
+ #### Text Fields (type="text")
320
+ - **Behavior**: Fuzzy search enabled, normalized (accents/punctuation removed), tokenized
321
+ - **Fields**: `title`, `notes`, `author`, `maintainer`, `res_name`, `res_description`
322
+ - **Example**: `title:sanità` also finds "sanita", "sanità", "Sanità" (variations)
323
+
324
+ ```typescript
325
+ // Fuzzy search automatically applied on text fields
326
+ ckan_package_search({
327
+ server_url: "https://www.dati.gov.it/opendata",
328
+ q: "title:sanità~2", // Finds variations with up to 2 character differences
329
+ rows: 20
330
+ })
331
+ ```
332
+
333
+ #### Accessing the Schema
334
+
335
+ You can view CKAN's Solr schema to see all field types:
336
+
337
+ 1. **GitHub**: https://github.com/ckan/ckan/blob/master/ckan/config/solr/schema.xml
338
+ 2. **Solr API** (if available): `http://your-ckan-server:8983/solr/ckan/schema`
339
+
340
+ Note: Public CKAN portals (like dati.gov.it) do not expose the Solr endpoint directly for security reasons - only the CKAN API is public.
341
+
292
342
  ### Fuzzy Search
293
343
 
294
344
  Find terms with similar spelling (edit distance matching). Useful for typos or variations.
package/LOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## 2026-01-10
4
4
 
5
+ ### Version 0.4.1 - Maintenance
6
+ - **Date formatting**: ISO `YYYY-MM-DD` output, tests aligned
7
+ - **HTTP transport**: Single shared transport per process
8
+ - **Registration**: Centralized tool/resource setup via `registerAll()`
9
+ - **Docs**: Updated CLAUDE/PRD/REFACTORING notes
10
+
11
+ ## 2026-01-10
12
+
13
+ ### Documentation Enhancement - Solr Field Types
14
+ - **New section in EXAMPLES.md**: "Understanding Solr Field Types: Exact vs Fuzzy Search"
15
+ - Documents difference between `type=string` (exact match) and `type=text` (fuzzy)
16
+ - String fields: res_format, tags, organization, license, state, name (case-sensitive)
17
+ - Text fields: title, notes, author, maintainer (normalized, fuzzy enabled)
18
+ - Practical example: `res_format:CSV` (43,836 results) vs `res_format:csv` (0 results)
19
+ - Links to CKAN Solr schema on GitHub
20
+ - Explains why some searches are exact and others are fuzzy
21
+ - **Impact**: Users understand when exact matching is required vs when fuzzy search works
22
+
5
23
  ### Version 0.4.0 - Cloudflare Workers Deployment ⭐
6
24
 
7
25
  - **Production deployment**: Server now live on Cloudflare Workers
package/PRD.md CHANGED
@@ -662,7 +662,7 @@ ckan_package_search({
662
662
  - Non configurabile
663
663
 
664
664
  7. **Locale**:
665
- - Date formattate in 'it-IT'
665
+ - Date formattate in ISO `YYYY-MM-DD`
666
666
  - Non parametrizzato
667
667
 
668
668
  ### 9.2 External Dependencies
package/REFACTORING.md CHANGED
@@ -70,7 +70,7 @@ src/
70
70
 
71
71
  **`utils/formatting.ts`** (Output Formatting)
72
72
  - `truncateText()` - Limita output a CHARACTER_LIMIT
73
- - `formatDate()` - Format date in Italian locale
73
+ - `formatDate()` - Format date in ISO `YYYY-MM-DD`
74
74
  - `formatBytes()` - Human-readable file sizes
75
75
 
76
76
  ### Tools
@@ -98,6 +98,7 @@ src/
98
98
 
99
99
  **`transport/http.ts`** (HTTP Transport)
100
100
  - `runHTTP()` - HTTP server on configurable port
101
+ - Single shared transport per process
101
102
  - For remote access via HTTP POST
102
103
 
103
104
  ## Build Configuration
package/dist/index.js CHANGED
@@ -2,12 +2,6 @@
2
2
 
3
3
  // src/server.ts
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- function createServer() {
6
- return new McpServer({
7
- name: "ckan-mcp-server",
8
- version: "0.1.0"
9
- });
10
- }
11
5
 
12
6
  // src/tools/package.ts
13
7
  import { z as z2 } from "zod";
@@ -71,9 +65,16 @@ function truncateText(text, limit = CHARACTER_LIMIT) {
71
65
  }
72
66
  function formatDate(dateStr) {
73
67
  try {
74
- return new Date(dateStr).toLocaleString("it-IT");
68
+ if (!dateStr) {
69
+ return "Invalid Date";
70
+ }
71
+ const date = new Date(dateStr);
72
+ if (Number.isNaN(date.getTime())) {
73
+ return "Invalid Date";
74
+ }
75
+ return date.toISOString().slice(0, 10);
75
76
  } catch {
76
- return dateStr;
77
+ return "Invalid Date";
77
78
  }
78
79
  }
79
80
 
@@ -1227,6 +1228,21 @@ function registerAllResources(server2) {
1227
1228
  registerOrganizationResource(server2);
1228
1229
  }
1229
1230
 
1231
+ // src/server.ts
1232
+ function createServer() {
1233
+ return new McpServer({
1234
+ name: "ckan-mcp-server",
1235
+ version: "0.4.1"
1236
+ });
1237
+ }
1238
+ function registerAll(server2) {
1239
+ registerPackageTools(server2);
1240
+ registerOrganizationTools(server2);
1241
+ registerDatastoreTools(server2);
1242
+ registerStatusTools(server2);
1243
+ registerAllResources(server2);
1244
+ }
1245
+
1230
1246
  // src/transport/stdio.ts
1231
1247
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1232
1248
  async function runStdio(server2) {
@@ -1241,13 +1257,12 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
1241
1257
  async function runHTTP(server2) {
1242
1258
  const app = express();
1243
1259
  app.use(express.json());
1260
+ const transport2 = new StreamableHTTPServerTransport({
1261
+ sessionIdGenerator: void 0,
1262
+ enableJsonResponse: true
1263
+ });
1264
+ await server2.connect(transport2);
1244
1265
  app.post("/mcp", async (req, res) => {
1245
- const transport2 = new StreamableHTTPServerTransport({
1246
- sessionIdGenerator: void 0,
1247
- enableJsonResponse: true
1248
- });
1249
- res.on("close", () => transport2.close());
1250
- await server2.connect(transport2);
1251
1266
  await transport2.handleRequest(req, res, req.body);
1252
1267
  });
1253
1268
  const port = parseInt(process.env.PORT || "3000");
@@ -1258,11 +1273,7 @@ async function runHTTP(server2) {
1258
1273
 
1259
1274
  // src/index.ts
1260
1275
  var server = createServer();
1261
- registerPackageTools(server);
1262
- registerOrganizationTools(server);
1263
- registerDatastoreTools(server);
1264
- registerStatusTools(server);
1265
- registerAllResources(server);
1276
+ registerAll(server);
1266
1277
  var transport = process.env.TRANSPORT || "stdio";
1267
1278
  if (transport === "http") {
1268
1279
  runHTTP(server).catch((error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aborruso/ckan-mcp-server",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "MCP server for interacting with CKAN open data portals",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",