@aborruso/ckan-mcp-server 0.3.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.
Files changed (43) hide show
  1. package/.claude/commands/openspec/apply.md +23 -0
  2. package/.claude/commands/openspec/archive.md +27 -0
  3. package/.claude/commands/openspec/proposal.md +28 -0
  4. package/.claude/settings.local.json +31 -0
  5. package/.gemini/commands/openspec/apply.toml +21 -0
  6. package/.gemini/commands/openspec/archive.toml +25 -0
  7. package/.gemini/commands/openspec/proposal.toml +26 -0
  8. package/.mcp.json +12 -0
  9. package/.opencode/command/openspec-apply.md +24 -0
  10. package/.opencode/command/openspec-archive.md +27 -0
  11. package/.opencode/command/openspec-proposal.md +29 -0
  12. package/AGENTS.md +18 -0
  13. package/CLAUDE.md +320 -0
  14. package/EXAMPLES.md +707 -0
  15. package/LICENSE.txt +21 -0
  16. package/LOG.md +154 -0
  17. package/PRD.md +912 -0
  18. package/README.md +468 -0
  19. package/REFACTORING.md +237 -0
  20. package/dist/index.js +1277 -0
  21. package/openspec/AGENTS.md +456 -0
  22. package/openspec/changes/archive/2026-01-08-add-mcp-resources/design.md +115 -0
  23. package/openspec/changes/archive/2026-01-08-add-mcp-resources/proposal.md +52 -0
  24. package/openspec/changes/archive/2026-01-08-add-mcp-resources/specs/mcp-resources/spec.md +92 -0
  25. package/openspec/changes/archive/2026-01-08-add-mcp-resources/tasks.md +56 -0
  26. package/openspec/changes/archive/2026-01-08-expand-test-coverage-specs/design.md +355 -0
  27. package/openspec/changes/archive/2026-01-08-expand-test-coverage-specs/proposal.md +161 -0
  28. package/openspec/changes/archive/2026-01-08-expand-test-coverage-specs/tasks.md +162 -0
  29. package/openspec/changes/archive/2026-01-08-translate-project-to-english/proposal.md +115 -0
  30. package/openspec/changes/archive/2026-01-08-translate-project-to-english/specs/documentation-language/spec.md +32 -0
  31. package/openspec/changes/archive/2026-01-08-translate-project-to-english/tasks.md +115 -0
  32. package/openspec/changes/archive/add-automated-tests/design.md +324 -0
  33. package/openspec/changes/archive/add-automated-tests/proposal.md +167 -0
  34. package/openspec/changes/archive/add-automated-tests/specs/automated-testing/spec.md +143 -0
  35. package/openspec/changes/archive/add-automated-tests/tasks.md +132 -0
  36. package/openspec/project.md +113 -0
  37. package/openspec/specs/documentation-language/spec.md +32 -0
  38. package/openspec/specs/mcp-resources/spec.md +94 -0
  39. package/package.json +46 -0
  40. package/spunti.md +19 -0
  41. package/tasks/todo.md +124 -0
  42. package/test-urls.js +18 -0
  43. package/tmp/test-org-search.js +55 -0
@@ -0,0 +1,92 @@
1
+ # MCP Resources Capability
2
+
3
+ ## ADDED Requirements
4
+
5
+ ### Requirement: Dataset Resource Template
6
+
7
+ The system SHALL expose a resource template for accessing CKAN dataset metadata via the URI pattern `ckan://{server}/dataset/{id}`.
8
+
9
+ #### Scenario: Read dataset metadata successfully
10
+
11
+ - **WHEN** client reads `ckan://dati.gov.it/dataset/vaccini-covid`
12
+ - **THEN** server returns JSON with complete dataset metadata including title, description, resources, organization, tags
13
+
14
+ #### Scenario: Dataset not found
15
+
16
+ - **WHEN** client reads `ckan://demo.ckan.org/dataset/nonexistent-id`
17
+ - **THEN** server returns error indicating dataset not found
18
+
19
+ #### Scenario: Server unreachable
20
+
21
+ - **WHEN** client reads `ckan://invalid-server.example/dataset/test`
22
+ - **THEN** server returns error indicating server unreachable
23
+
24
+ ### Requirement: Resource Resource Template
25
+
26
+ The system SHALL expose a resource template for accessing CKAN resource metadata via the URI pattern `ckan://{server}/resource/{id}`.
27
+
28
+ #### Scenario: Read resource metadata successfully
29
+
30
+ - **WHEN** client reads `ckan://dati.gov.it/resource/abc-123-def`
31
+ - **THEN** server returns JSON with resource metadata including name, format, URL, size, and download link
32
+
33
+ #### Scenario: Resource not found
34
+
35
+ - **WHEN** client reads `ckan://demo.ckan.org/resource/invalid-id`
36
+ - **THEN** server returns error indicating resource not found
37
+
38
+ ### Requirement: Organization Resource Template
39
+
40
+ The system SHALL expose a resource template for accessing CKAN organization metadata via the URI pattern `ckan://{server}/organization/{name}`.
41
+
42
+ #### Scenario: Read organization metadata successfully
43
+
44
+ - **WHEN** client reads `ckan://dati.gov.it/organization/regione-toscana`
45
+ - **THEN** server returns JSON with organization metadata including title, description, and dataset count
46
+
47
+ #### Scenario: Organization not found
48
+
49
+ - **WHEN** client reads `ckan://demo.ckan.org/organization/nonexistent-org`
50
+ - **THEN** server returns error indicating organization not found
51
+
52
+ ### Requirement: URI Scheme Parsing
53
+
54
+ The system SHALL parse `ckan://` URIs extracting server hostname and path components.
55
+
56
+ #### Scenario: Parse standard URI
57
+
58
+ - **WHEN** URI is `ckan://dati.gov.it/dataset/test-id`
59
+ - **THEN** server extracts: server=`https://dati.gov.it`, type=`dataset`, id=`test-id`
60
+
61
+ #### Scenario: Parse URI with www prefix
62
+
63
+ - **WHEN** URI is `ckan://www.dati.gov.it/dataset/test-id`
64
+ - **THEN** server extracts: server=`https://www.dati.gov.it`, type=`dataset`, id=`test-id`
65
+
66
+ #### Scenario: Reject malformed URI
67
+
68
+ - **WHEN** URI is `ckan://invalid` (missing path)
69
+ - **THEN** server returns validation error
70
+
71
+ ### Requirement: Resource Response Format
72
+
73
+ The system SHALL return resource content as JSON with standard MCP resource response structure.
74
+
75
+ #### Scenario: JSON response structure
76
+
77
+ - **WHEN** client reads any valid resource URI
78
+ - **THEN** response contains `contents` array with `uri`, `mimeType` (application/json), and `text` (JSON string)
79
+
80
+ #### Scenario: Large response truncation
81
+
82
+ - **WHEN** resource content exceeds CHARACTER_LIMIT
83
+ - **THEN** response is truncated to CHARACTER_LIMIT with truncation indicator
84
+
85
+ ### Requirement: Resource Discovery
86
+
87
+ The system SHALL list available resource templates when client requests resource list.
88
+
89
+ #### Scenario: List resource templates
90
+
91
+ - **WHEN** client calls `resources/listTemplates`
92
+ - **THEN** server returns list of available URI templates with descriptions
@@ -0,0 +1,56 @@
1
+ # Tasks: Add MCP Resource Templates
2
+
3
+ ## 1. Setup
4
+
5
+ - [x] 1.1 Create `src/resources/` directory
6
+ - [x] 1.2 Create `src/resources/index.ts` with `registerAllResources()` export
7
+
8
+ ## 2. URI Utilities
9
+
10
+ - [x] 2.1 Create URI parsing function for `ckan://` scheme
11
+ - [x] 2.2 Add validation for malformed URIs
12
+ - [x] 2.3 Handle edge cases (www prefix, trailing slashes)
13
+
14
+ ## 3. Dataset Resource
15
+
16
+ - [x] 3.1 Create `src/resources/dataset.ts`
17
+ - [x] 3.2 Register template `ckan://{server}/dataset/{id}`
18
+ - [x] 3.3 Implement handler using `makeCkanRequest('package_show')`
19
+ - [x] 3.4 Return JSON response with dataset metadata
20
+
21
+ ## 4. Resource Resource
22
+
23
+ - [x] 4.1 Create `src/resources/resource.ts`
24
+ - [x] 4.2 Register template `ckan://{server}/resource/{id}`
25
+ - [x] 4.3 Implement handler using `makeCkanRequest('resource_show')`
26
+ - [x] 4.4 Include download URL in response
27
+
28
+ ## 5. Organization Resource
29
+
30
+ - [x] 5.1 Create `src/resources/organization.ts`
31
+ - [x] 5.2 Register template `ckan://{server}/organization/{name}`
32
+ - [x] 5.3 Implement handler using `makeCkanRequest('organization_show')`
33
+ - [x] 5.4 Return JSON response with organization metadata
34
+
35
+ ## 6. Integration
36
+
37
+ - [x] 6.1 Import `registerAllResources` in `src/index.ts`
38
+ - [x] 6.2 Call `registerAllResources(server)` after tool registration
39
+ - [x] 6.3 Build and verify no compilation errors
40
+
41
+ ## 7. Testing
42
+
43
+ - [x] 7.1 Create `tests/integration/resources.test.ts`
44
+ - [x] 7.2 Add tests for dataset resource template
45
+ - [x] 7.3 Add tests for resource resource template
46
+ - [x] 7.4 Add tests for organization resource template
47
+ - [x] 7.5 Add tests for URI parsing edge cases
48
+ - [x] 7.6 Add tests for error handling (invalid URI, server errors)
49
+ - [x] 7.7 Run full test suite, ensure 100% pass
50
+
51
+ ## 8. Documentation
52
+
53
+ - [x] 8.1 Update README.md with resource templates section
54
+ - [x] 8.2 Update CLAUDE.md architecture section
55
+ - [x] 8.3 Add examples to EXAMPLES.md (deferred - basic examples in README)
56
+ - [x] 8.4 Update LOG.md with changes
@@ -0,0 +1,355 @@
1
+ # Design: Expand Test Coverage for Remaining Tools
2
+
3
+ This document explains the testing approach for expanding coverage to package, organization, and datastore tools, following patterns established in the initial testing implementation.
4
+
5
+ ## Testing Approach
6
+
7
+ ### Pattern Reuse
8
+
9
+ We follow the successful pattern from the initial testing proposal:
10
+
11
+ ```typescript
12
+ // 1. Mock axios
13
+ vi.mock('axios');
14
+
15
+ // 2. Import fixtures
16
+ import packageSearchFixture from '../fixtures/responses/package-search-success.json';
17
+
18
+ // 3. Setup test
19
+ it('returns expected structure', async () => {
20
+ vi.mocked(axios.get).mockResolvedValue({ data: packageSearchFixture });
21
+
22
+ const result = await ckan_package_search({...});
23
+
24
+ // 4. Validate result
25
+ expect(result).toBeDefined();
26
+ expect(result.content[0].text).toContain('...');
27
+ });
28
+ ```
29
+
30
+ ### Test Structure
31
+
32
+ Each tool category gets its own test file:
33
+ - `tests/integration/package.test.ts`
34
+ - `tests/integration/organization.test.ts`
35
+ - `tests/integration/datastore.test.ts`
36
+
37
+ ## Test Scenarios by Tool Category
38
+
39
+ ### Package Tools
40
+
41
+ #### ckan_package_search Tests
42
+
43
+ **Happy Paths:**
44
+ 1. Basic search with query parameter
45
+ 2. Search with facets (facet_field, facet_limit)
46
+ 3. Search with pagination (start, rows)
47
+ 4. Search with sorting (sort)
48
+ 5. Search with filter query (fq)
49
+ 6. Empty results (q returns no matches)
50
+
51
+ **Edge Cases:**
52
+ 7. Very large results (rows > 1000)
53
+ 8. Multiple facet fields
54
+ 9. Complex query (q + fq + facets)
55
+ 10. Markdown output format validation
56
+ 11. JSON output format validation
57
+
58
+ **Error Scenarios:**
59
+ 12. Invalid query syntax (if applicable)
60
+ 13. Server error during search
61
+ 14. Timeout during search
62
+
63
+ #### ckan_package_show Tests
64
+
65
+ **Happy Paths:**
66
+ 1. Show package with valid ID
67
+ 2. Package with multiple resources
68
+ 3. Package with tags
69
+ 4. Package with organization
70
+ 5. Package without resources
71
+
72
+ **Edge Cases:**
73
+ 6. Package with very long description (test truncation)
74
+ 7. Package with many resources (>10)
75
+
76
+ **Error Scenarios:**
77
+ 8. Invalid package ID (404)
78
+ 9. Server error during fetch
79
+ 10. Timeout during fetch
80
+
81
+ ### Organization Tools
82
+
83
+ #### ckan_organization_list Tests
84
+
85
+ **Happy Paths:**
86
+ 1. List all organizations (default)
87
+ 2. List with limit parameter
88
+ 3. List with offset parameter
89
+ 4. List with sort parameter
90
+ 5. Empty result (no organizations)
91
+
92
+ **Edge Cases:**
93
+ 6. Very large list (all organizations)
94
+ 7. Sorting by different fields (name, title, package_count)
95
+
96
+ **Error Scenarios:**
97
+ 8. Server error during list
98
+ 9. Timeout during list
99
+
100
+ #### ckan_organization_show Tests
101
+
102
+ **Happy Paths:**
103
+ 1. Show organization with valid ID
104
+ 2. Organization with users
105
+ 3. Organization with package_count
106
+ 4. Organization with extras (custom fields)
107
+
108
+ **Edge Cases:**
109
+ 5. Organization with very long description
110
+
111
+ **Error Scenarios:**
112
+ 6. Invalid organization ID (404)
113
+ 7. Server error during fetch
114
+ 8. Timeout during fetch
115
+
116
+ #### ckan_organization_search Tests
117
+
118
+ **Happy Paths:**
119
+ 1. Search with query
120
+ 2. Search with wildcard (*)
121
+ 3. Search with facets
122
+ 4. Search with pagination
123
+
124
+ **Edge Cases:**
125
+ 5. Search returning many results
126
+ 6. Search returning no results
127
+
128
+ **Error Scenarios:**
129
+ 7. Invalid query
130
+ 8. Server error during search
131
+ 9. Timeout during search
132
+
133
+ ### DataStore Tools
134
+
135
+ #### ckan_datastore_search Tests
136
+
137
+ **Happy Paths:**
138
+ 1. Basic query with resource_id
139
+ 2. Query with filters
140
+ 3. Query with sorting
141
+ 4. Query with pagination (limit, offset)
142
+ 5. Query with multiple filters
143
+ 6. Query with field list (fields parameter)
144
+
145
+ **Edge Cases:**
146
+ 7. Large result set (test truncation)
147
+ 8. Complex filters (AND/OR logic)
148
+ 9. Empty result (no records)
149
+
150
+ **Error Scenarios:**
151
+ 10. Invalid resource_id
152
+ 11. Invalid filter syntax
153
+ 12. Server error during query
154
+ 13. Timeout during query
155
+ 14. DataStore not enabled for resource
156
+
157
+ ## Output Format Validation
158
+
159
+ All tools support both markdown and JSON output. Tests should validate:
160
+
161
+ ### Markdown Format
162
+ ```typescript
163
+ test('returns markdown format', async () => {
164
+ vi.mocked(axios.get).mockResolvedValue({ data: fixture });
165
+
166
+ const result = await ckan_package_search({ format: 'markdown', ... });
167
+
168
+ expect(result.content[0].type).toBe('text');
169
+ expect(result.content[0].text).toMatch(/^# /); // Markdown heading
170
+ });
171
+ ```
172
+
173
+ ### JSON Format
174
+ ```typescript
175
+ test('returns JSON format', async () => {
176
+ vi.mocked(axios.get).mockResolvedValue({ data: fixture });
177
+
178
+ const result = await ckan_package_search({ format: 'json', ... });
179
+
180
+ expect(result.content[0].type).toBe('resource');
181
+ expect(result.structuredContent).toBeDefined();
182
+ });
183
+ ```
184
+
185
+ ## Error Handling Tests
186
+
187
+ All tools should handle errors gracefully:
188
+
189
+ ```typescript
190
+ test('handles 404 error', async () => {
191
+ vi.mocked(axios.get).mockRejectedValue({
192
+ isAxiosError: true,
193
+ response: { status: 404, data: notFoundFixture }
194
+ });
195
+
196
+ const result = await ckan_package_show({ package_id: 'invalid' });
197
+
198
+ expect(result.isError).toBe(true);
199
+ expect(result.content[0].text).toContain('Not found');
200
+ });
201
+ ```
202
+
203
+ ## Fixture Strategy
204
+
205
+ ### Existing Fixtures
206
+
207
+ Reuse fixtures from initial proposal:
208
+ - `package-search-success.json` - already exists
209
+ - `package-show-success.json` - already exists
210
+ - `organization-list-success.json` - already exists
211
+ - `organization-show-success.json` - already exists
212
+ - `organization-search-success.json` - already exists
213
+ - `datastore-search-success.json` - already exists
214
+
215
+ ### Additional Fixtures Needed
216
+
217
+ May need to create:
218
+ - `package-empty-results.json` - for empty search results
219
+ - `organization-empty-results.json` - for empty org list
220
+ - `datastore-empty-results.json` - for empty data queries
221
+ - Error fixtures specific to each tool if needed
222
+
223
+ ### Creating New Fixtures
224
+
225
+ Follow existing structure:
226
+
227
+ **Success fixture:**
228
+ ```json
229
+ {
230
+ "help": "API help URL",
231
+ "success": true,
232
+ "result": { ... }
233
+ }
234
+ ```
235
+
236
+ **Error fixture:**
237
+ ```json
238
+ {
239
+ "success": false,
240
+ "error": {
241
+ "__type": "Error Type",
242
+ "message": "Error message",
243
+ "status": 404
244
+ }
245
+ }
246
+ ```
247
+
248
+ ## Coverage Goals
249
+
250
+ ### Target Metrics
251
+
252
+ | Component | Current Coverage | Target Coverage | Tests Needed |
253
+ |-----------|------------------|-----------------|---------------|
254
+ | Utils | 100% | 100% | ✅ Done |
255
+ | Status Tool | ~50% | 75-85% | ✅ Done |
256
+ | Package Tools | 0% | 75-85% | ~16 tests |
257
+ | Organization Tools | 0% | 75-85% | ~18 tests |
258
+ | DataStore Tool | 0% | 75-85% | ~14 tests |
259
+ | **Overall** | **~68%** | **80%+** | **~48 tests** |
260
+
261
+ ### Verification
262
+
263
+ After each tool category is tested, run:
264
+ ```bash
265
+ npm run test:coverage
266
+ ```
267
+
268
+ Ensure coverage increases and meets targets.
269
+
270
+ ## Test Writing Guidelines
271
+
272
+ ### Naming Convention
273
+ ```typescript
274
+ // Good
275
+ test('ckan_package_search with facets returns aggregated data', () => { ... });
276
+
277
+ // Bad
278
+ test('search works', () => { ... });
279
+ ```
280
+
281
+ ### AAA Pattern
282
+ ```typescript
283
+ test('search with pagination', async () => {
284
+ // Arrange
285
+ const params = { q: 'test', start: 10, rows: 20 };
286
+
287
+ // Act
288
+ const result = await ckan_package_search(params);
289
+
290
+ // Assert
291
+ expect(result.content[0].text).toContain('Showing 10-20 of');
292
+ });
293
+ ```
294
+
295
+ ### One Assertion Per Test
296
+ ```typescript
297
+ // Good
298
+ test('truncates long descriptions', async () => {
299
+ const result = await ckan_package_show({ package_id: 'long-desc' });
300
+ expect(result.content[0].text).toContain('[Response truncated]');
301
+ });
302
+
303
+ // Bad
304
+ test('package_show works', async () => {
305
+ const result = await ckan_package_show({ package_id: 'pkg' });
306
+ expect(result).toBeDefined();
307
+ expect(result.content).toBeDefined();
308
+ expect(result.content[0]).toBeDefined();
309
+ expect(result.content[0].text).toBeDefined();
310
+ });
311
+ ```
312
+
313
+ ## Implementation Order
314
+
315
+ 1. **Package Tools** (highest priority)
316
+ - Most complex
317
+ - Most used
318
+ - Largest code block (~350 lines)
319
+
320
+ 2. **Organization Tools** (medium priority)
321
+ - Medium complexity
322
+ - Medium code block (~341 lines)
323
+
324
+ 3. **DataStore Tool** (lower priority)
325
+ - Simpler logic
326
+ - Smaller code block (~146 lines)
327
+ - May have platform-specific behavior
328
+
329
+ ## Future Enhancements
330
+
331
+ ### Short Term
332
+ - Contract tests for CKAN API responses
333
+ - Visual regression tests for markdown output
334
+ - Performance benchmarks for queries
335
+
336
+ ### Medium Term
337
+ - Increase coverage to 90%+
338
+ - Add contract tests for all tool schemas
339
+ - Add integration with real test CKAN instance (optional)
340
+
341
+ ### Long Term
342
+ - Fuzz testing for input validation
343
+ - Chaos testing for error handling
344
+ - Property-based testing
345
+
346
+ ## Conclusion
347
+
348
+ This test expansion follows the proven patterns from the initial testing proposal, focusing on:
349
+
350
+ - **Consistency**: Same structure, fixtures, and patterns
351
+ - **Completeness**: Cover all tool categories
352
+ - **Quality**: Test both happy paths and error scenarios
353
+ - **Maintainability**: Simple, focused, well-documented tests
354
+
355
+ The incremental implementation ensures we achieve 80%+ coverage while keeping the work manageable and debuggable.
@@ -0,0 +1,161 @@
1
+ # Proposal: Expand Test Coverage to Remaining Tools
2
+
3
+ **Status:** Completed
4
+ **Created:** 2026-01-08
5
+ **Author:** OpenCode
6
+ **Related:** Archived proposal "add-automated-tests"
7
+
8
+ ## Summary
9
+
10
+ Add integration tests for the remaining MCP tools (package, organization, datastore) to achieve 80%+ code coverage and improve test reliability.
11
+
12
+ ## Motivation
13
+
14
+ The initial automated testing implementation successfully established the testing foundation but only covered:
15
+ - Utility functions (100% coverage)
16
+ - Status tool (partial coverage)
17
+ - Overall coverage: ~68%
18
+
19
+ To meet the 80% coverage target and ensure reliability, we need to test the remaining three tool categories that represent the majority of the codebase:
20
+ - Package tools: ~350 lines
21
+ - Organization tools: ~341 lines
22
+ - DataStore tools: ~146 lines
23
+
24
+ Adding these tests will:
25
+ - Increase overall coverage to 80%+
26
+ - Catch bugs in tool implementations
27
+ - Document expected behavior through tests
28
+ - Enable confident refactoring of tool code
29
+ - Provide safety net for future changes
30
+
31
+ ## Scope
32
+
33
+ ### Included
34
+ - **Integration tests** for package tools (ckan_package_search, ckan_package_show)
35
+ - **Integration tests** for organization tools (ckan_organization_list, ckan_organization_show, ckan_organization_search)
36
+ - **Integration tests** for datastore tool (ckan_datastore_search)
37
+ - Additional fixtures for error scenarios if needed
38
+ - Tests for both markdown and JSON output formats
39
+ - Tests for edge cases (empty results, pagination, filters)
40
+
41
+ ### Excluded
42
+ - E2e tests with real CKAN servers (already excluded)
43
+ - Performance/benchmark tests (future enhancement)
44
+ - UI/interaction tests (no UI in this project)
45
+ - Testing MCP SDK internals (out of scope)
46
+ - Testing external libraries (axios, zod, express)
47
+
48
+ ## Proposed Changes
49
+
50
+ ### Test Strategy
51
+
52
+ Following the same pattern established in the first proposal:
53
+ 1. Use Vitest with mocked axios
54
+ 2. Reuse existing fixtures from `tests/fixtures/`
55
+ 3. Create new fixtures only if needed
56
+ 4. Test success and error scenarios
57
+ 5. Test both markdown and JSON output formats
58
+
59
+ ### Test Coverage Breakdown
60
+
61
+ #### Package Tools (~6 tests)
62
+ - Basic search with query
63
+ - Search with facets
64
+ - Search with pagination
65
+ - Search with sorting
66
+ - Search with filter query
67
+ - Show package details
68
+
69
+ #### Organization Tools (~8 tests)
70
+ - List organizations
71
+ - List with sorting
72
+ - Show organization details
73
+ - Search organizations
74
+ - Error scenarios (404, invalid ID)
75
+
76
+ #### DataStore Tool (~6 tests)
77
+ - Basic query
78
+ - Query with filters
79
+ - Query with sorting
80
+ - Query with pagination
81
+ - Error scenarios
82
+ - Output format validation
83
+
84
+ ### Coverage Target
85
+
86
+ **Goal**: Increase from ~68% to 80%+
87
+
88
+ **Expected breakdown after completion**:
89
+ - Utils: 100% (already achieved)
90
+ - Status tool: 75-85%
91
+ - Package tools: 75-85%
92
+ - Organization tools: 75-85%
93
+ - DataStore tool: 75-85%
94
+ - **Overall**: 80%+
95
+
96
+ ### Incremental Approach
97
+
98
+ 1. Start with package tools (largest, most complex)
99
+ 2. Add organization tests
100
+ 3. Add datastore tests
101
+ 4. Validate coverage after each batch
102
+ 5. Adjust if needed
103
+
104
+ ## Alternatives Considered
105
+
106
+ 1. **Test all tools at once**
107
+ - *Rejected*: Too large, hard to debug, follow incremental pattern
108
+
109
+ 2. **Skip integration tests, only unit tests**
110
+ - *Rejected*: Would miss tool behavior and CKAN API interactions
111
+
112
+ 3. **Use real CKAN servers for testing**
113
+ - *Rejected*: Already excluded in original proposal (slow, unreliable)
114
+
115
+ 4. **Test only happy paths, ignore errors**
116
+ - *Rejected*: Error handling is critical for robust tools
117
+
118
+ ## Impact Assessment
119
+
120
+ ### Benefits
121
+ - + Increased coverage from 68% to 80%+
122
+ - + Better bug detection in tool code
123
+ - + Safer refactoring of tool implementations
124
+ - + Documented expected behavior for all tools
125
+ - + Higher confidence in code quality
126
+
127
+ ### Risks
128
+ - - Initial time investment for writing ~20 more tests
129
+ - - Test maintenance overhead as tool code evolves
130
+ - - Possible need for additional fixtures
131
+
132
+ ### Mitigation
133
+ - Reuse existing fixtures where possible
134
+ - Follow established test patterns
135
+ - Write tests incrementally, validate after each batch
136
+ - Keep tests simple and focused
137
+
138
+ ## Open Questions
139
+
140
+ None - reusing established patterns from first proposal.
141
+
142
+ ## Dependencies
143
+
144
+ Depends on:
145
+ - Proposal "add-automated-tests" (archived) - provides test infrastructure and fixtures
146
+ - Existing fixtures in `tests/fixtures/` - may need additions
147
+
148
+ ## Success Criteria
149
+
150
+ - [ ] All package tools have integration tests
151
+ - [ ] All organization tools have integration tests
152
+ - [ ] DataStore tool has integration tests
153
+ - [ ] Tests pass (green CI if exists)
154
+ - [ ] Overall coverage meets 80% threshold
155
+ - [ ] Tests follow established patterns from first proposal
156
+ - [ ] Documentation updated if needed
157
+ - [ ] Validation: `openspec validate expand-test-coverage --strict` passes
158
+
159
+ ## Notes
160
+
161
+ This proposal is a direct continuation of the "add-automated-tests" proposal. All testing infrastructure (Vitest, fixtures, patterns) is already in place, so the focus is purely on writing the actual test files for the remaining tools.