@aborruso/ckan-mcp-server 0.3.2 → 0.4.0

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.
@@ -0,0 +1,734 @@
1
+ # Design: Cloudflare Workers Deployment
2
+
3
+ ## Architecture Overview
4
+
5
+ ### Current Architecture (Node.js)
6
+
7
+ ```
8
+ ┌─────────────────────────────────────────┐
9
+ │ CKAN MCP Server (Node.js) │
10
+ ├─────────────────────────────────────────┤
11
+ │ │
12
+ │ ┌──────────────┐ ┌──────────────┐ │
13
+ │ │ stdio.ts │ │ http.ts │ │
14
+ │ │ Transport │ │ Transport │ │
15
+ │ └──────┬───────┘ └──────┬───────┘ │
16
+ │ │ │ │
17
+ │ └──────┬────────────┘ │
18
+ │ │ │
19
+ │ ┌──────▼───────┐ │
20
+ │ │ MCP Server │ │
21
+ │ │ (SDK) │ │
22
+ │ └──────┬───────┘ │
23
+ │ │ │
24
+ │ ┌───────────┴───────────┐ │
25
+ │ │ Tool Handlers │ │
26
+ │ │ (7 CKAN tools) │ │
27
+ │ └───────────┬───────────┘ │
28
+ │ │ │
29
+ │ ┌──────▼───────┐ │
30
+ │ │ HTTP Client │ │
31
+ │ │ (axios) │ │
32
+ │ └──────┬───────┘ │
33
+ │ │ │
34
+ └────────────────┼───────────────────────┘
35
+
36
+ ┌──────▼───────┐
37
+ │ CKAN API │
38
+ │ (external) │
39
+ └──────────────┘
40
+ ```
41
+
42
+ ### Proposed Architecture (Cloudflare Workers)
43
+
44
+ ```
45
+ ┌─────────────────────────────────────────┐
46
+ │ CKAN MCP Server (Workers Runtime) │
47
+ ├─────────────────────────────────────────┤
48
+ │ │
49
+ │ ┌──────────────┐ │
50
+ │ │ worker.ts │ │
51
+ │ │ (fetch API) │ │
52
+ │ └──────┬───────┘ │
53
+ │ │ │
54
+ │ ┌──────▼───────┐ │
55
+ │ │ MCP Server │ │
56
+ │ │ (SDK) │ │
57
+ │ └──────┬───────┘ │
58
+ │ │ │
59
+ │ ┌───────────┴───────────┐ │
60
+ │ │ Tool Handlers │ │
61
+ │ │ (7 CKAN tools) │ │
62
+ │ │ [REUSED CODE] │ │
63
+ │ └───────────┬───────────┘ │
64
+ │ │ │
65
+ │ ┌──────▼───────┐ │
66
+ │ │ HTTP Client │ │
67
+ │ │ (fetch API) │ │
68
+ │ └──────┬───────┘ │
69
+ │ │ │
70
+ └────────────────┼───────────────────────┘
71
+
72
+ ┌──────▼───────┐
73
+ │ CKAN API │
74
+ │ (external) │
75
+ └──────────────┘
76
+ ```
77
+
78
+ **Key differences**:
79
+ - **Entry point**: `worker.ts` replaces `stdio.ts` and `http.ts`
80
+ - **HTTP client**: Native `fetch()` replaces `axios`
81
+ - **Runtime**: Cloudflare Workers runtime (V8) instead of Node.js
82
+ - **Tool handlers**: Unchanged (100% code reuse)
83
+
84
+ ---
85
+
86
+ ## Component Design
87
+
88
+ ### 1. Worker Entry Point (`src/worker.ts`)
89
+
90
+ **Purpose**: Handle incoming HTTP requests and route to MCP server
91
+
92
+ **Interface**:
93
+ ```typescript
94
+ export default {
95
+ async fetch(request: Request, env: Env): Promise<Response>
96
+ }
97
+ ```
98
+
99
+ **Responsibilities**:
100
+ 1. Health check endpoint (`GET /health`)
101
+ 2. MCP protocol endpoint (`POST /mcp`)
102
+ 3. Error handling and logging
103
+ 4. Request/response transformation
104
+
105
+ **Code structure**:
106
+ ```typescript
107
+ // src/worker.ts
108
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
109
+ import { registerPackageTools } from './tools/package.js';
110
+ import { registerOrganizationTools } from './tools/organization.js';
111
+ import { registerDatastoreTools } from './tools/datastore.js';
112
+ import { registerStatusTools } from './tools/status.js';
113
+ import { registerResources } from './resources/index.js';
114
+
115
+ export default {
116
+ async fetch(request: Request, env: Env): Promise<Response> {
117
+ const url = new URL(request.url);
118
+
119
+ // Health check
120
+ if (request.method === 'GET' && url.pathname === '/health') {
121
+ return new Response(JSON.stringify({
122
+ status: 'ok',
123
+ version: '0.4.0',
124
+ tools: 7,
125
+ resources: 3
126
+ }), {
127
+ headers: { 'Content-Type': 'application/json' }
128
+ });
129
+ }
130
+
131
+ // MCP endpoint
132
+ if (request.method === 'POST' && url.pathname === '/mcp') {
133
+ return handleMcpRequest(request);
134
+ }
135
+
136
+ // 404 for all other routes
137
+ return new Response('Not Found', { status: 404 });
138
+ }
139
+ };
140
+
141
+ async function handleMcpRequest(request: Request): Promise<Response> {
142
+ try {
143
+ // Parse JSON-RPC request
144
+ const body = await request.json();
145
+
146
+ // Create MCP server instance
147
+ const server = new Server({
148
+ name: 'ckan-mcp-server',
149
+ version: '0.4.0'
150
+ }, {
151
+ capabilities: {
152
+ tools: {},
153
+ resources: {}
154
+ }
155
+ });
156
+
157
+ // Register all tools
158
+ registerPackageTools(server);
159
+ registerOrganizationTools(server);
160
+ registerDatastoreTools(server);
161
+ registerStatusTools(server);
162
+ registerResources(server);
163
+
164
+ // Process MCP request
165
+ const response = await server.handleRequest(body);
166
+
167
+ // Return JSON-RPC response
168
+ return new Response(JSON.stringify(response), {
169
+ headers: { 'Content-Type': 'application/json' }
170
+ });
171
+ } catch (error) {
172
+ return new Response(JSON.stringify({
173
+ jsonrpc: '2.0',
174
+ error: {
175
+ code: -32603,
176
+ message: 'Internal error',
177
+ data: error.message
178
+ },
179
+ id: null
180
+ }), {
181
+ status: 500,
182
+ headers: { 'Content-Type': 'application/json' }
183
+ });
184
+ }
185
+ }
186
+ ```
187
+
188
+ ---
189
+
190
+ ### 2. HTTP Client Adaptation
191
+
192
+ **Current**: `src/utils/http.ts` uses `axios`
193
+ **Problem**: `axios` uses Node.js `http` module, not available in Workers
194
+ **Solution**: Replace with native `fetch()`
195
+
196
+ **Before** (axios):
197
+ ```typescript
198
+ import axios from 'axios';
199
+
200
+ export async function makeCkanRequest<T>(
201
+ serverUrl: string,
202
+ endpoint: string,
203
+ params?: Record<string, any>
204
+ ): Promise<T> {
205
+ const response = await axios.get(`${serverUrl}/api/3/action/${endpoint}`, {
206
+ params,
207
+ timeout: 30000,
208
+ headers: { 'User-Agent': 'CKAN-MCP-Server/1.0' }
209
+ });
210
+ return response.data.result;
211
+ }
212
+ ```
213
+
214
+ **After** (fetch):
215
+ ```typescript
216
+ export async function makeCkanRequest<T>(
217
+ serverUrl: string,
218
+ endpoint: string,
219
+ params?: Record<string, any>
220
+ ): Promise<T> {
221
+ const url = new URL(`${serverUrl}/api/3/action/${endpoint}`);
222
+ if (params) {
223
+ Object.entries(params).forEach(([key, value]) => {
224
+ url.searchParams.append(key, String(value));
225
+ });
226
+ }
227
+
228
+ const controller = new AbortController();
229
+ const timeoutId = setTimeout(() => controller.abort(), 30000);
230
+
231
+ try {
232
+ const response = await fetch(url.toString(), {
233
+ method: 'GET',
234
+ headers: { 'User-Agent': 'CKAN-MCP-Server/1.0' },
235
+ signal: controller.signal
236
+ });
237
+
238
+ if (!response.ok) {
239
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
240
+ }
241
+
242
+ const data = await response.json();
243
+ if (!data.success) {
244
+ throw new Error(data.error?.message || 'CKAN API error');
245
+ }
246
+
247
+ return data.result;
248
+ } finally {
249
+ clearTimeout(timeoutId);
250
+ }
251
+ }
252
+ ```
253
+
254
+ **Changes**:
255
+ - Replace `axios.get()` with `fetch()`
256
+ - Build URL with `URLSearchParams`
257
+ - Implement timeout with `AbortController`
258
+ - Manual error handling (no axios interceptors)
259
+
260
+ **Impact**:
261
+ - Remove `axios` dependency from `package.json`
262
+ - Workers bundle size decreases (~20KB smaller)
263
+ - No changes to tool handlers (same API)
264
+
265
+ ---
266
+
267
+ ### 3. Build System
268
+
269
+ **Current**: Single build target (Node.js)
270
+ ```
271
+ esbuild.config.js → dist/index.js (CommonJS, Node.js platform)
272
+ ```
273
+
274
+ **Proposed**: Dual build targets
275
+
276
+ ```
277
+ esbuild.config.js → dist/index.js (CommonJS, Node.js platform)
278
+ esbuild.worker.js → dist/worker.js (ESM, browser platform)
279
+ ```
280
+
281
+ **New file**: `esbuild.worker.js`
282
+ ```javascript
283
+ import esbuild from 'esbuild';
284
+
285
+ await esbuild.build({
286
+ entryPoints: ['src/worker.ts'],
287
+ bundle: true,
288
+ outfile: 'dist/worker.js',
289
+ format: 'esm', // Workers require ESM
290
+ platform: 'browser', // Workers use Web APIs
291
+ target: 'es2022',
292
+ external: [], // Bundle everything (no node_modules in Workers)
293
+ minify: true, // Reduce bundle size
294
+ sourcemap: false,
295
+ treeShaking: true,
296
+ mainFields: ['browser', 'module', 'main'],
297
+ conditions: ['worker', 'browser'],
298
+ });
299
+ ```
300
+
301
+ **Key differences**:
302
+ - **format**: ESM (not CommonJS)
303
+ - **platform**: browser (not node)
304
+ - **external**: empty array (bundle all dependencies)
305
+ - **minify**: true (reduce Workers script size)
306
+
307
+ **Bundle analysis**:
308
+ - Current Node.js bundle: ~50KB
309
+ - Estimated Workers bundle: ~30-40KB (no axios, smaller MCP SDK)
310
+ - Workers limit: 1MB (10x safety margin)
311
+
312
+ ---
313
+
314
+ ### 4. Configuration
315
+
316
+ **File**: `wrangler.toml`
317
+ ```toml
318
+ name = "ckan-mcp-server"
319
+ main = "dist/worker.js"
320
+ compatibility_date = "2024-01-01"
321
+
322
+ # Build configuration
323
+ [build]
324
+ command = "npm run build:worker"
325
+ watch_dir = "src"
326
+
327
+ # Environment variables (optional)
328
+ [vars]
329
+ DEFAULT_CKAN_SERVER = "https://demo.ckan.org"
330
+
331
+ # Production environment
332
+ [env.production]
333
+ name = "ckan-mcp-server"
334
+ routes = [
335
+ { pattern = "ckan-mcp.example.com", custom_domain = true }
336
+ ]
337
+
338
+ # Development environment
339
+ [env.development]
340
+ name = "ckan-mcp-server-dev"
341
+ ```
342
+
343
+ **Configuration options**:
344
+ - `name`: Workers script name (shows in Cloudflare dashboard)
345
+ - `main`: Entry point (must be ESM)
346
+ - `compatibility_date`: Workers runtime version
347
+ - `build.command`: Runs before deployment
348
+ - `vars`: Environment variables accessible in worker
349
+ - `env.*`: Multiple environments (dev/staging/prod)
350
+
351
+ ---
352
+
353
+ ## Design Decisions
354
+
355
+ ### Decision 1: Keep Node.js Modes Unchanged
356
+
357
+ **Options**:
358
+ 1. Replace Node.js runtime entirely with Workers (breaking change)
359
+ 2. Support both Node.js and Workers (dual build)
360
+
361
+ **Choice**: Option 2 (dual build)
362
+
363
+ **Rationale**:
364
+ - **No breaking changes**: Existing users unaffected
365
+ - **Flexibility**: Users can choose deployment model
366
+ - **Development**: Local testing still uses Node.js (`npm start`)
367
+ - **Cost**: Minimal (2 build configs, ~100 lines of adapter code)
368
+
369
+ **Trade-offs**:
370
+ - **Pro**: Zero migration pain
371
+ - **Pro**: Supports offline/airgapped usage (stdio mode)
372
+ - **Con**: Maintain 2 entry points (worker.ts + index.ts)
373
+ - **Con**: Test 2 runtimes (Node.js + Workers)
374
+
375
+ ---
376
+
377
+ ### Decision 2: Replace axios with fetch()
378
+
379
+ **Options**:
380
+ 1. Bundle axios for Workers (use polyfill)
381
+ 2. Replace with native fetch()
382
+
383
+ **Choice**: Option 2 (native fetch)
384
+
385
+ **Rationale**:
386
+ - **Bundle size**: axios is ~15KB minified, fetch is native (0KB)
387
+ - **Compatibility**: fetch is standard across Node.js 18+ and Workers
388
+ - **Simplicity**: No polyfills or adapters needed
389
+ - **Performance**: fetch is optimized in Workers runtime
390
+
391
+ **Trade-offs**:
392
+ - **Pro**: Smaller bundle, faster cold starts
393
+ - **Pro**: Future-proof (fetch is Web standard)
394
+ - **Con**: Rewrite HTTP client (~50 lines)
395
+ - **Con**: Different error handling (manual checks vs axios interceptors)
396
+
397
+ **Impact**:
398
+ - Modify `src/utils/http.ts` (~30 lines changed)
399
+ - No changes to tool handlers (API unchanged)
400
+ - Node.js modes continue using axios (or migrate to fetch for consistency)
401
+
402
+ ---
403
+
404
+ ### Decision 3: No Server-Side Caching
405
+
406
+ **Options**:
407
+ 1. Add Workers KV cache for CKAN responses
408
+ 2. No caching (same as Node.js version)
409
+
410
+ **Choice**: Option 2 (no caching)
411
+
412
+ **Rationale**:
413
+ - **Consistency**: Same behavior as Node.js version
414
+ - **Freshness**: Always return latest CKAN data
415
+ - **Simplicity**: No cache invalidation logic
416
+ - **Free tier**: KV has storage limits (1GB)
417
+
418
+ **Trade-offs**:
419
+ - **Pro**: Simple, consistent, fresh data
420
+ - **Con**: Higher latency for repeated queries
421
+ - **Con**: More CKAN API requests
422
+
423
+ **Future enhancement**: Add optional caching with TTL (documented in future-ideas.md)
424
+
425
+ ---
426
+
427
+ ### Decision 4: Single Global Deployment
428
+
429
+ **Options**:
430
+ 1. Deploy to single Workers instance (user's account)
431
+ 2. Provide official public endpoint (Anthropic-hosted)
432
+ 3. Both
433
+
434
+ **Choice**: Option 1 (user deploys own instance)
435
+
436
+ **Rationale**:
437
+ - **No hosting cost**: Users deploy to their own free tier
438
+ - **No rate limit sharing**: Each user gets 100k req/day
439
+ - **No liability**: Anthropic not responsible for uptime/abuse
440
+ - **Easy forking**: Users can customize and deploy
441
+
442
+ **Trade-offs**:
443
+ - **Pro**: Zero hosting cost, infinite scalability
444
+ - **Pro**: Users control their deployment
445
+ - **Con**: Users must create Cloudflare account (5 min)
446
+ - **Con**: No "official" endpoint to share
447
+
448
+ **Future option**: Provide public endpoint in addition to user deployments
449
+
450
+ ---
451
+
452
+ ## Runtime Compatibility
453
+
454
+ ### Node.js APIs → Workers APIs
455
+
456
+ | Node.js API | Workers API | Impact |
457
+ |----------------------|-----------------------|-----------------------|
458
+ | `http.createServer` | `fetch(request)` | Different entry point |
459
+ | `axios` | `fetch()` | Rewrite HTTP client |
460
+ | `process.env` | `env` parameter | Minimal (no env vars) |
461
+ | `console.log` | `console.log` | ✅ Compatible |
462
+ | `JSON.*` | `JSON.*` | ✅ Compatible |
463
+ | `URL` | `URL` | ✅ Compatible |
464
+ | `AbortController` | `AbortController` | ✅ Compatible |
465
+
466
+ **Compatibility score**: 95%
467
+ - Most code works unchanged
468
+ - Only HTTP client and entry point need adaptation
469
+
470
+ ---
471
+
472
+ ## Error Handling
473
+
474
+ ### Workers-Specific Errors
475
+
476
+ 1. **Script exceeded CPU time limit**
477
+ - **Cause**: Worker runs > 10ms CPU time (free tier)
478
+ - **Mitigation**: CKAN API calls are I/O (don't count toward CPU)
479
+ - **Likelihood**: Very low (current tools are I/O-bound)
480
+
481
+ 2. **Script exceeded memory limit**
482
+ - **Cause**: Worker uses > 128MB memory (free tier)
483
+ - **Mitigation**: No large in-memory datasets
484
+ - **Likelihood**: Very low (responses truncated to 50KB)
485
+
486
+ 3. **Subrequest limit exceeded**
487
+ - **Cause**: > 50 subrequests per invocation (free tier)
488
+ - **Mitigation**: Tools make 1-2 CKAN API calls max
489
+ - **Likelihood**: Zero (tools designed for single queries)
490
+
491
+ 4. **Network timeout**
492
+ - **Cause**: CKAN API takes > 30s
493
+ - **Mitigation**: Same as Node.js (30s timeout with AbortController)
494
+ - **Likelihood**: Low (most CKAN queries < 5s)
495
+
496
+ **Error response format** (JSON-RPC):
497
+ ```json
498
+ {
499
+ "jsonrpc": "2.0",
500
+ "error": {
501
+ "code": -32603,
502
+ "message": "Internal error",
503
+ "data": "Error details here"
504
+ },
505
+ "id": null
506
+ }
507
+ ```
508
+
509
+ ---
510
+
511
+ ## Performance Characteristics
512
+
513
+ ### Latency Breakdown
514
+
515
+ **Total latency** = Cold start + Worker execution + CKAN API + Response
516
+
517
+ 1. **Cold start**: 0-50ms (first request after idle)
518
+ - Workers spin down after ~15 seconds of inactivity
519
+ - Subsequent requests: 0ms (hot start)
520
+
521
+ 2. **Worker execution**: 1-5ms (CPU time)
522
+ - Parse JSON-RPC request
523
+ - Validate parameters
524
+ - Call CKAN API (I/O - doesn't count)
525
+ - Format response
526
+
527
+ 3. **CKAN API**: 500-5000ms (network + server)
528
+ - Depends on CKAN portal load
529
+ - Italy (dati.gov.it): ~3s average
530
+ - US (data.gov): ~2s average
531
+
532
+ 4. **Response serialization**: 1-5ms
533
+ - JSON.stringify()
534
+ - Truncate to 50KB if needed
535
+
536
+ **Expected total latency**:
537
+ - **First request** (cold start): 0.5-5.1s
538
+ - **Hot requests**: 0.5-5s
539
+ - **Target**: < 5s for 95th percentile
540
+
541
+ **Comparison to Node.js**:
542
+ - Similar (CKAN API dominates latency)
543
+ - Slight advantage: Workers edge routing (closer to CKAN servers)
544
+
545
+ ---
546
+
547
+ ## Security Considerations
548
+
549
+ ### 1. Public Access
550
+ - **Risk**: Anyone can call Workers endpoint
551
+ - **Mitigation**: Read-only operations, no sensitive data
552
+ - **Decision**: Acceptable (CKAN portals are public)
553
+
554
+ ### 2. Rate Limiting
555
+ - **Risk**: Abuse could exhaust free tier (100k req/day)
556
+ - **Mitigation**: Cloudflare automatic rate limiting, user can upgrade
557
+ - **Decision**: Monitor usage, add custom limits if needed
558
+
559
+ ### 3. CKAN API Keys
560
+ - **Risk**: Some CKAN APIs require authentication
561
+ - **Mitigation**: Store API keys in Workers secrets (not implemented yet)
562
+ - **Decision**: Document in future-ideas.md (v0.5.0)
563
+
564
+ ### 4. Input Validation
565
+ - **Risk**: Malformed requests crash worker
566
+ - **Mitigation**: Existing Zod schemas validate all inputs
567
+ - **Decision**: No changes needed (already secure)
568
+
569
+ ### 5. CORS
570
+ - **Risk**: Browser clients blocked by CORS
571
+ - **Mitigation**: Add CORS headers to all responses
572
+ - **Decision**: Add `Access-Control-Allow-Origin: *` (public API)
573
+
574
+ ---
575
+
576
+ ## Testing Strategy
577
+
578
+ ### 1. Local Testing (wrangler dev)
579
+ ```bash
580
+ npm run dev:worker
581
+ curl http://localhost:8787/health
582
+ ```
583
+
584
+ **Validates**:
585
+ - Build succeeds
586
+ - Worker starts without errors
587
+ - Basic routing works
588
+
589
+ ### 2. Integration Testing (all tools)
590
+ ```bash
591
+ # Test each tool locally
592
+ curl -X POST http://localhost:8787/mcp \
593
+ -H "Content-Type: application/json" \
594
+ -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"ckan_status_show","arguments":{"server_url":"https://demo.ckan.org"}},"id":1}'
595
+ ```
596
+
597
+ **Validates**:
598
+ - All 7 tools work in Workers runtime
599
+ - CKAN API integration works
600
+ - Response formatting correct
601
+
602
+ ### 3. Production Testing (after deployment)
603
+ ```bash
604
+ # Test on live Workers endpoint
605
+ curl -X POST https://ckan-mcp-server.aborruso.workers.dev/mcp \
606
+ -H "Content-Type: application/json" \
607
+ -d '...'
608
+ ```
609
+
610
+ **Validates**:
611
+ - Deployment successful
612
+ - HTTPS works
613
+ - Global routing works
614
+
615
+ ### 4. Claude Desktop Integration
616
+ - Configure Claude Desktop with Workers URL
617
+ - Test all tools through Claude UI
618
+ - Verify markdown formatting
619
+
620
+ **Validates**:
621
+ - End-to-end MCP protocol
622
+ - User experience matches local mode
623
+
624
+ ### 5. Load Testing (optional)
625
+ ```bash
626
+ # Use wrk or ab to simulate load
627
+ wrk -t4 -c100 -d30s https://ckan-mcp-server.aborruso.workers.dev/health
628
+ ```
629
+
630
+ **Validates**:
631
+ - Handles concurrent requests
632
+ - No rate limit issues
633
+ - Stable under load
634
+
635
+ ---
636
+
637
+ ## Monitoring and Debugging
638
+
639
+ ### 1. Cloudflare Dashboard
640
+ - **Metrics**: Requests/day, errors, CPU time
641
+ - **Access**: https://dash.cloudflare.com → Workers & Pages → ckan-mcp-server
642
+
643
+ ### 2. Real-Time Logs
644
+ ```bash
645
+ wrangler tail
646
+ ```
647
+ Shows live logs from Workers (console.log, errors)
648
+
649
+ ### 3. Error Tracking
650
+ - Workers dashboard shows error rate
651
+ - Stack traces in tail output
652
+ - Sentry integration (future)
653
+
654
+ ### 4. Analytics
655
+ - Free tier includes basic analytics
656
+ - Paid tier: detailed metrics, alerts
657
+
658
+ ---
659
+
660
+ ## Rollout Plan
661
+
662
+ ### Phase 1: Development (tasks 1-3)
663
+ - Set up local environment
664
+ - Implement worker.ts
665
+ - Test locally with wrangler dev
666
+
667
+ ### Phase 2: Deployment (task 4)
668
+ - Deploy to workers.dev
669
+ - Test in production
670
+ - Verify all tools work
671
+
672
+ ### Phase 3: Documentation (tasks 4.3-4.5)
673
+ - Write DEPLOYMENT.md
674
+ - Update README.md
675
+ - Update LOG.md
676
+
677
+ ### Phase 4: Validation (task 4.6)
678
+ - Claude Desktop integration test
679
+ - User acceptance
680
+
681
+ ### Rollback Triggers
682
+ - Any tool fails in production
683
+ - Workers limits exceeded
684
+ - Unexpected errors > 1%
685
+
686
+ **Rollback action**: Keep using Node.js modes, document Workers issues
687
+
688
+ ---
689
+
690
+ ## Future Enhancements
691
+
692
+ ### 1. Response Caching (v0.5.0)
693
+ ```typescript
694
+ // Use Workers KV for caching
695
+ const cache = await env.CACHE.get(`ckan:${serverUrl}:${endpoint}:${hash}`);
696
+ if (cache) return JSON.parse(cache);
697
+
698
+ const result = await makeCkanRequest(...);
699
+ await env.CACHE.put(`ckan:${serverUrl}:${endpoint}:${hash}`, JSON.stringify(result), {
700
+ expirationTtl: 3600 // 1 hour TTL
701
+ });
702
+ ```
703
+
704
+ ### 2. Custom Domain (v0.5.0)
705
+ ```toml
706
+ # wrangler.toml
707
+ routes = [
708
+ { pattern = "ckan-mcp.example.com", custom_domain = true }
709
+ ]
710
+ ```
711
+
712
+ ### 3. Analytics Dashboard (v0.6.0)
713
+ - Track most used tools
714
+ - Monitor CKAN portal health
715
+ - Usage statistics
716
+
717
+ ### 4. Multi-Region Deployment (v0.6.0)
718
+ - Deploy to specific Cloudflare regions
719
+ - Route to nearest CKAN portal
720
+ - Reduce latency
721
+
722
+ ---
723
+
724
+ ## Conclusion
725
+
726
+ This design enables Cloudflare Workers deployment with:
727
+ - **Minimal code changes** (~150 lines new code)
728
+ - **No breaking changes** (Node.js modes unchanged)
729
+ - **95% code reuse** (only entry point + HTTP client adapted)
730
+ - **Production-ready** (all 7 tools fully functional)
731
+ - **Future-proof** (uses Web standards: fetch, ESM)
732
+
733
+ **Risk**: Low (well-understood technologies, small scope)
734
+ **Reward**: High (global deployment, zero hosting cost, great UX)