# OpenSpec Instructions These instructions are for AI assistants working in this project. Always open `@/openspec/AGENTS.md` when the request: - Mentions planning or proposals (words like proposal, spec, change, plan) - Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work - Sounds ambiguous and you need the authoritative spec before coding Use `@/openspec/AGENTS.md` to learn: - How to create and apply change proposals - Spec format and conventions - Project structure and guidelines Keep this managed block so 'openspec update' can refresh the instructions. # Agent Guide ## Scope - Repo: `ckan-mcp-server` - Stack: Node.js + TypeScript (ESM) - Tests: Vitest - Build: esbuild - Cloudflare Workers: `wrangler` ## Cursor and Copilot Rules - No `.cursor/rules`, `.cursorrules`, or `.github/copilot-instructions.md` found ## Commands **Install**: `npm install` **Build**: `npm run build` | `npm run build:tsc` | `npm run build:worker` **Run**: `npm start` | `npm run dev` | `npm run watch` | `npm run dev:worker` | `npm run deploy` **Test**: `npm test` | `npm run test:watch` | `npm run test:coverage` **Single test**: `npm test -- tests/unit/http.test.ts` | `npm test -- -t "testName"` ## Local MCP Client Build Test Before deploying, you can test the current dev build by pointing your MCP client at the Node entrypoint in `dist/`: 1. Build: `npm run build` 2. Example absolute path: `/home/aborruso/git/idee/ckan-mcp-server/dist/index.js` (adjust to your local checkout) ## GitHub CLI Notes When creating issues with multi-line bodies, avoid literal `\n` in `--body`. Use a here-doc or `-F -` to preserve newlines: ```bash cat <<'EOF' | gh issue create --title "Title" --body-file - --repo ondata/ckan-mcp-server Line 1 Line 2 EOF ``` When writing GitHub release notes, avoid literal `\n` in `--notes`. Always pass a here-doc via `--notes-file -` so line breaks render correctly: ```bash cat <<'EOF' | gh release create v0.X.Y --title "v0.X.Y - Title" --notes-file - ## What's New ### Changes - Item 1 - Item 2 ### No Breaking Changes - All existing functionality preserved **Full Changelog**: https://github.com/ondata/ckan-mcp-server/compare/v0.X-1.Y...v0.X.Y EOF ``` ## TypeScript Style Use strict typing, avoid `any` unless from CKAN payloads. Prefer explicit return types, `type` aliases, ESM imports with `.js` extensions. Keep `noUnusedLocals` and `noUnusedParameters` clean. ## Import Conventions Use `import type` for type-only imports. Group by kind: external, internal, types. Double quotes, relative paths. ## Naming `camelCase` vars/functions, `PascalCase` types/classes, `UPPER_SNAKE_CASE` constants. Tool names match MCP tool ids. ## Error Handling Wrap tool handlers in `try/catch`. Return `{ isError: true }` for MCP errors. Include context, map HTTP errors to readable messages, preserve cause when rethrowing. ## Tool Responses Use `ResponseFormat` for markdown vs JSON. `truncateText` for large payloads. Pretty-print JSON, include `structuredContent` for JSON mode. ## Testing Guidelines Vitest with `globals: true`. Place tests in `tests/unit` or `tests/integration`. AAA pattern, mock via fixtures in `tests/fixtures`, descriptive names. ## Test Targets Never use `demo.ckan.org` for tests. Always use `https://www.dati.gov.it/opendata`. ## Configuration Node `>=18`. Worker build in `wrangler.toml`. Vitest coverage thresholds enforced. ## Change Hygiene Minimal focused diffs. No unrelated refactors. Update tests for behavior changes. Avoid editing `dist/`. When a review flags something, reply inside each inline thread with the fixing commit or the reason for not changing the code, never with one summary comment (see `CONTRIBUTING.md`). ## Pre-commit Checklist Before committing and pushing any locally testable change: 1. Build: `npm run build` 2. Automated tests: `npm test` — all must pass 3. Manual queries: run real requests against the built server to verify end-to-end behavior ### How to run manual queries ```bash # Terminal 1 — start server TRANSPORT=http PORT=3001 node dist/index.js # Terminal 2 — call a tool curl -s -X POST http://localhost:3001/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc":"2.0", "method":"tools/call", "params":{ "name":"ckan_package_search", "arguments":{ "server_url":"https://www.dati.gov.it/opendata", "q":"ambiente", "page":1, "page_size":3 } }, "id":1 }' ``` - Always include both `Content-Type: application/json` and `Accept: application/json, text/event-stream` - Use `node dist/index.js` directly, not `npm start` - Use port 3001 to avoid conflicts ## Project Layout `src/index.ts` entry, `src/server.ts` wiring, `src/tools/` handlers, `src/utils/` helpers, `src/resources/` templates, `src/transport/` stdio/HTTP. `tests/unit/` utilities, `tests/integration/` behavior, `tests/fixtures/` mocks. ## CSV Data Exploration For exploring CSV resources from datasets, use duckdb CLI (already installed) with direct HTTP URL: ```bash duckdb -jsonlines -c "DESCRIBE SELECT * FROM read_csv('http://url/file.csv')" duckdb -jsonlines -c "SUMMARIZE SELECT * FROM read_csv('http://url/file.csv')" duckdb -jsonlines -c "SELECT * FROM read_csv('http://url/file.csv') USING SAMPLE 10" ``` Use direct resource URLs (http/https), not GitHub view/blob URLs. The `-jsonlines` parameter outputs in JSONL format, easier for AI to parse. For random sampling, use `USING SAMPLE N` syntax (where N is the number of rows): ```bash duckdb -jsonlines -c "SELECT * FROM read_csv('http://url/file.csv') USING SAMPLE 10" ```