@aborruso/ckan-mcp-server 0.4.10 → 0.4.13

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 (34) hide show
  1. package/LOG.md +57 -0
  2. package/NOTICE.md +77 -0
  3. package/PRD.md +6 -6
  4. package/README.md +35 -14
  5. package/dist/index.js +124 -7
  6. package/dist/worker.js +56 -56
  7. package/openspec/changes/add-ckan-host-allowlist-env/design.md +38 -0
  8. package/openspec/changes/add-ckan-host-allowlist-env/proposal.md +16 -0
  9. package/openspec/changes/add-ckan-host-allowlist-env/specs/ckan-request-allowlist/spec.md +15 -0
  10. package/openspec/changes/add-ckan-host-allowlist-env/specs/cloudflare-deployment/spec.md +11 -0
  11. package/openspec/changes/add-ckan-host-allowlist-env/tasks.md +12 -0
  12. package/openspec/changes/add-escape-text-query/proposal.md +12 -0
  13. package/openspec/changes/add-escape-text-query/specs/ckan-search/spec.md +11 -0
  14. package/openspec/changes/add-escape-text-query/tasks.md +8 -0
  15. package/openspec/changes/archive/2026-01-15-add-mcp-resource-filters/proposal.md +13 -0
  16. package/openspec/changes/archive/2026-01-15-add-mcp-resource-filters/specs/mcp-resources/spec.md +38 -0
  17. package/openspec/changes/archive/2026-01-15-add-mcp-resource-filters/tasks.md +10 -0
  18. package/openspec/changes/archive/2026-01-19-update-repo-owner-ondata/proposal.md +13 -0
  19. package/openspec/changes/archive/2026-01-19-update-repo-owner-ondata/specs/repository-metadata/spec.md +14 -0
  20. package/openspec/changes/archive/2026-01-19-update-repo-owner-ondata/tasks.md +12 -0
  21. package/openspec/specs/ckan-insights/spec.md +5 -1
  22. package/openspec/specs/ckan-search/spec.md +16 -0
  23. package/openspec/specs/mcp-prompts/spec.md +26 -0
  24. package/openspec/specs/mcp-resources/spec.md +30 -4
  25. package/openspec/specs/repository-metadata/spec.md +19 -0
  26. package/package.json +1 -1
  27. package/private/commenti-privati.yaml +14 -0
  28. /package/openspec/changes/{add-mcp-prompts → archive/2026-01-15-add-mcp-prompts}/proposal.md +0 -0
  29. /package/openspec/changes/{add-mcp-prompts → archive/2026-01-15-add-mcp-prompts}/specs/mcp-prompts/spec.md +0 -0
  30. /package/openspec/changes/{add-mcp-prompts → archive/2026-01-15-add-mcp-prompts}/tasks.md +0 -0
  31. /package/openspec/changes/{update-search-parser-config → archive/2026-01-19-update-search-parser-config}/proposal.md +0 -0
  32. /package/openspec/changes/{update-search-parser-config → archive/2026-01-19-update-search-parser-config}/specs/ckan-insights/spec.md +0 -0
  33. /package/openspec/changes/{update-search-parser-config → archive/2026-01-19-update-search-parser-config}/specs/ckan-search/spec.md +0 -0
  34. /package/openspec/changes/{update-search-parser-config → archive/2026-01-19-update-search-parser-config}/tasks.md +0 -0
@@ -0,0 +1,38 @@
1
+ # Design: CKAN Host Allowlist via Environment
2
+
3
+ ## Overview
4
+ Introduce an optional, environment-driven allowlist for CKAN hosts. When configured, all CKAN requests must target hosts in the allowlist. When not configured, behavior remains unchanged.
5
+
6
+ ## Configuration
7
+ - Environment variable: `ALLOWED_CKAN_HOSTS`
8
+ - Format: comma-separated hostnames (e.g., `dati.gov.it,data.gov,catalog.data.gov`)
9
+ - Parsing rules:
10
+ - Split on commas
11
+ - Trim whitespace
12
+ - Lowercase
13
+ - Drop empty entries
14
+
15
+ ## Enforcement
16
+ - Validate hostnames extracted from:
17
+ - Tool inputs: `server_url`
18
+ - Resource URIs: `ckan://{server}/...`
19
+ - Reject requests where the hostname is not in the allowlist.
20
+ - Error message should be explicit: `Host not allowed: <hostname>`.
21
+
22
+ ## Runtime Scope
23
+ - Node.js (stdio/http) and Workers runtimes share the same validation utility.
24
+ - Workers reads env via `env.ALLOWED_CKAN_HOSTS` (wrangler var), Node via `process.env.ALLOWED_CKAN_HOSTS`.
25
+
26
+ ## Integration Points
27
+ - Add a small utility module, e.g. `src/utils/allowlist.ts`:
28
+ - `parseAllowedHosts(value?: string): Set<string> | null`
29
+ - `assertAllowedHost(serverUrl: string, allowed: Set<string> | null): void`
30
+ - Call `assertAllowedHost` inside CKAN request flow or immediately in each tool/resource handler before network calls.
31
+
32
+ ## Backwards Compatibility
33
+ - If `ALLOWED_CKAN_HOSTS` is unset or empty, allow all hosts (no behavior change).
34
+
35
+ ## Testing
36
+ - Unit tests for parsing behavior (case, whitespace, empty entries).
37
+ - Unit tests for allow/deny logic with known hostnames and invalid URLs.
38
+ - Tool/resource tests to verify rejection when allowlist is set.
@@ -0,0 +1,16 @@
1
+ # Change: Add allowlist for CKAN hosts via environment
2
+
3
+ ## Why
4
+ Public HTTP/Workers deployments can be abused to proxy requests to arbitrary hosts. A host allowlist provides a simple, configurable barrier without breaking local usage.
5
+
6
+ ## What Changes
7
+ - Add `ALLOWED_CKAN_HOSTS` env var (comma-separated hostnames) to restrict `server_url` targets.
8
+ - Validate `server_url` host against the allowlist for all tools/resources that call CKAN.
9
+ - Document and expose the env var in `wrangler.toml` for Workers deployments.
10
+
11
+ ## Design Reference
12
+ See `openspec/changes/add-ckan-host-allowlist-env/design.md` for configuration, enforcement, and runtime details.
13
+
14
+ ## Impact
15
+ - Affected specs: `cloudflare-deployment`, new `ckan-request-allowlist`
16
+ - Affected code: request validation utilities; Workers/Node configuration handling; tools/resources that accept `server_url` or `ckan://` URIs.
@@ -0,0 +1,15 @@
1
+ ## ADDED Requirements
2
+ ### Requirement: CKAN host allowlist validation
3
+ The system SHALL validate every CKAN request target against an optional allowlist configured via `ALLOWED_CKAN_HOSTS`.
4
+
5
+ #### Scenario: Allowed host
6
+ - **WHEN** a tool or resource is called with a `server_url` whose hostname is in `ALLOWED_CKAN_HOSTS`
7
+ - **THEN** the request proceeds as normal
8
+
9
+ #### Scenario: Disallowed host
10
+ - **WHEN** a tool or resource is called with a `server_url` whose hostname is not in `ALLOWED_CKAN_HOSTS`
11
+ - **THEN** the request is rejected with a clear error indicating the host is not allowed
12
+
13
+ #### Scenario: Allowlist not set
14
+ - **WHEN** `ALLOWED_CKAN_HOSTS` is unset or empty
15
+ - **THEN** the system accepts any valid `server_url`
@@ -0,0 +1,11 @@
1
+ ## ADDED Requirements
2
+ ### Requirement: Workers allowlist configuration
3
+ The system SHALL allow configuring an allowlist of CKAN hosts for Workers deployments via environment variable.
4
+
5
+ #### Scenario: Wrangler allowlist configuration
6
+ - **WHEN** `wrangler.toml` sets `ALLOWED_CKAN_HOSTS` to a comma-separated list of hostnames
7
+ - **THEN** the Workers runtime reads the variable and restricts CKAN requests to those hosts
8
+
9
+ #### Scenario: Allowlist not set
10
+ - **WHEN** `ALLOWED_CKAN_HOSTS` is unset or empty in the Workers environment
11
+ - **THEN** the Workers runtime allows requests to any CKAN host
@@ -0,0 +1,12 @@
1
+ ## 1. Implementation
2
+ - [ ] Add allowlist parsing utility for `ALLOWED_CKAN_HOSTS` (comma-separated hostnames, case-insensitive, trim whitespace).
3
+ - [ ] Enforce allowlist for all CKAN requests (tools and resource templates) with clear error messaging.
4
+ - [ ] Ensure allowlist applies to both Node and Workers runtimes.
5
+
6
+ ## 2. Configuration
7
+ - [ ] Add `ALLOWED_CKAN_HOSTS` to `wrangler.toml` with example values.
8
+ - [ ] Update docs/README to describe the env var and behavior (optional if required by spec).
9
+
10
+ ## 3. Tests
11
+ - [ ] Add unit tests for allowlist parsing and validation.
12
+ - [ ] Add tool/resource tests verifying rejection for non-allowed hosts when env var is set.
@@ -0,0 +1,12 @@
1
+ # Change: Escape text-field query wrapping in search parser
2
+
3
+ ## Why
4
+ Wrapping arbitrary user input in `text:(...)` without escaping allows query parser errors or unintended semantics when the input contains Solr/Lucene metacharacters (e.g., `"` or `)`).
5
+
6
+ ## What Changes
7
+ - Escape Solr/Lucene special characters before wrapping user input in `text:(...)`.
8
+ - Add tests to confirm escaped output and prevent regressions.
9
+
10
+ ## Impact
11
+ - Affected specs: `ckan-search`
12
+ - Affected code: `src/utils/search.ts`, package search tool behavior, related tests.
@@ -0,0 +1,11 @@
1
+ ## MODIFIED Requirements
2
+ ### Requirement: Package search parser override
3
+ The system SHALL support a per-portal default and a per-request override to force package search queries through the `text` field when needed, and SHALL escape Solr/Lucene special characters when wrapping queries in `text:(...)`.
4
+
5
+ #### Scenario: Portal default applies
6
+ - **WHEN** a portal is configured to force the text-field parser
7
+ - **THEN** `ckan_package_search` uses `text:(...)` for non-fielded queries by default with escaped query content
8
+
9
+ #### Scenario: Request override applies
10
+ - **WHEN** a client explicitly requests the text-field parser
11
+ - **THEN** `ckan_package_search` uses `text:(...)` regardless of portal defaults with escaped query content
@@ -0,0 +1,8 @@
1
+ ## 1. Implementation
2
+ - [x] Add a Solr/Lucene escaping helper for text-field queries.
3
+ - [x] Apply escaping when `resolveSearchQuery` forces `text:(...)`.
4
+ - [x] Ensure behavior is unchanged when not forcing the text parser.
5
+
6
+ ## 2. Tests
7
+ - [x] Add unit tests for escaping behavior (quotes, parentheses, backslashes, colons).
8
+ - [x] Add tests to cover `resolveSearchQuery` effectiveQuery output with forced text parser.
@@ -0,0 +1,13 @@
1
+ # Change: add filtered CKAN dataset resource templates
2
+
3
+ ## Why
4
+ Users need quick, direct access to filtered dataset lists (by theme, publisher, tag, format) without building complex tool queries.
5
+
6
+ ## What Changes
7
+ - Add new MCP resource templates under the existing `ckan://{server}/...` scheme for group, organization, tag, and format dataset filters.
8
+ - Extend resource discovery to list the new templates.
9
+ - Document new resource URIs and examples.
10
+
11
+ ## Impact
12
+ - Affected specs: mcp-resources
13
+ - Affected code: `src/resources/*`, `src/resources/uri.ts`, README/docs
@@ -0,0 +1,38 @@
1
+ ## ADDED Requirements
2
+
3
+ ### Requirement: Group Datasets Resource Template
4
+ The system SHALL expose a resource template for accessing CKAN datasets by group via the URI pattern `ckan://{server}/group/{name}/datasets`.
5
+
6
+ #### Scenario: Read datasets by group
7
+ - **WHEN** client reads `ckan://dati.gov.it/group/governo/datasets`
8
+ - **THEN** server returns JSON with the matching datasets from `package_search`
9
+
10
+ ### Requirement: Organization Datasets Resource Template
11
+ The system SHALL expose a resource template for accessing CKAN datasets by organization via the URI pattern `ckan://{server}/organization/{name}/datasets`.
12
+
13
+ #### Scenario: Read datasets by organization
14
+ - **WHEN** client reads `ckan://dati.gov.it/organization/regione-toscana/datasets`
15
+ - **THEN** server returns JSON with the matching datasets from `package_search`
16
+
17
+ ### Requirement: Tag Datasets Resource Template
18
+ The system SHALL expose a resource template for accessing CKAN datasets by tag via the URI pattern `ckan://{server}/tag/{name}/datasets`.
19
+
20
+ #### Scenario: Read datasets by tag
21
+ - **WHEN** client reads `ckan://dati.gov.it/tag/turismo/datasets`
22
+ - **THEN** server returns JSON with the matching datasets from `package_search`
23
+
24
+ ### Requirement: Format Datasets Resource Template
25
+ The system SHALL expose a resource template for accessing CKAN datasets by resource format via the URI pattern `ckan://{server}/format/{format}/datasets`.
26
+
27
+ #### Scenario: Read datasets by format
28
+ - **WHEN** client reads `ckan://dati.gov.it/format/csv/datasets`
29
+ - **THEN** server returns JSON with the matching datasets from `package_search` filtered by resource format
30
+
31
+ ## MODIFIED Requirements
32
+
33
+ ### Requirement: Resource Discovery
34
+ The system SHALL list available resource templates when client requests resource list, including dataset, resource, organization, and dataset filter templates (group, organization, tag, format).
35
+
36
+ #### Scenario: List resource templates
37
+ - **WHEN** client calls `resources/listTemplates`
38
+ - **THEN** server returns list of available URI templates with descriptions, including the dataset filter templates
@@ -0,0 +1,10 @@
1
+ ## 1. Implementation
2
+ - [x] Add new resource handlers for group, organization datasets, tag, and format filters under `src/resources/`.
3
+ - [x] Register new resource templates in `src/resources/index.ts`.
4
+ - [x] Update URI parsing/validation to accept the new dataset filter paths.
5
+ - [x] Add tests for each new resource template and error cases.
6
+ - [x] Update README/docs with new `ckan://{server}/...` examples.
7
+
8
+ ## 2. Validation
9
+ - [x] Run `npm test` (or targeted resource tests).
10
+ - [x] Run `openspec validate add-mcp-resource-filters --strict`.
@@ -0,0 +1,13 @@
1
+ # Change: Move repository ownership to ondata organization
2
+
3
+ ## Why
4
+ The repository is moving from the personal account to the ondata organization. Documentation, badges, and published references must reflect the new canonical URL to avoid confusion and broken links.
5
+
6
+ ## What Changes
7
+ - Update documentation and in-app links to the new repository URL under the ondata organization.
8
+ - Preserve npm package ownership and scope as @aborruso (no npm ownership change).
9
+ - Confirm GitHub Pages and related references align with the new organization ownership.
10
+
11
+ ## Impact
12
+ - Affected specs: repository-metadata, cloudflare-deployment (docs references)
13
+ - Affected code: README.md, docs/*, src/worker.ts, .github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,14 @@
1
+ ## ADDED Requirements
2
+ ### Requirement: Canonical repository ownership references
3
+ The project documentation and UI MUST reference the canonical repository under the ondata organization once the repository is migrated.
4
+
5
+ #### Scenario: Repository references updated
6
+ - **WHEN** a user follows documentation or UI links to the repository
7
+ - **THEN** the links point to the ondata organization repository URL
8
+
9
+ ### Requirement: NPM package ownership remains personal
10
+ The project MUST continue to document the npm package under the @aborruso scope unless an explicit npm ownership change is approved.
11
+
12
+ #### Scenario: npm install instructions unchanged
13
+ - **WHEN** a user follows installation instructions
14
+ - **THEN** the package name remains @aborruso/ckan-mcp-server
@@ -0,0 +1,12 @@
1
+ ## 1. Documentation and Links
2
+ - [x] 1.1 Inventory repo URL references (README, docs, issue templates, web GUI).
3
+ - [x] 1.2 Update GitHub repository links to the ondata org.
4
+ - [x] 1.3 Update any badges and external links that embed the old owner.
5
+
6
+ ## 2. Deployment Guidance
7
+ - [x] 2.1 Update deployment docs that reference the old GitHub repo.
8
+ - [x] 2.2 Validate GitHub Pages URL references where applicable.
9
+
10
+ ## 3. Verification
11
+ - [x] 3.1 Confirm npm scope and publish instructions remain unchanged.
12
+ - [x] 3.2 Run openspec validation and address any issues.
@@ -11,9 +11,13 @@ The system SHALL maintain this specification to document dataset insight tools w
11
11
  - **THEN** their requirements are captured in this specification
12
12
 
13
13
  ### Requirement: Find relevant datasets
14
- The system SHALL provide a `ckan_find_relevant_datasets` tool that accepts `server_url`, `query`, `limit`, and optional weights for title, notes, tags, and organization, and returns the top N datasets ranked by score with a per-field score breakdown.
14
+ The system SHALL provide a `ckan_find_relevant_datasets` tool that accepts `server_url`, `query`, `limit`, optional weights for title, notes, tags, and organization, and an optional search parser override, and returns the top N datasets ranked by score with a per-field score breakdown.
15
15
 
16
16
  #### Scenario: Ranked results returned
17
17
  - **WHEN** the tool is invoked with a query and limit
18
18
  - **THEN** the system uses `package_search` results to return the top N datasets with numeric scores and field contributions
19
19
 
20
+ #### Scenario: Search parser override applied
21
+ - **WHEN** the tool is invoked with a search parser override
22
+ - **THEN** the system applies the override to the underlying `package_search` query
23
+
@@ -0,0 +1,16 @@
1
+ # ckan-search Specification
2
+
3
+ ## Purpose
4
+ TBD - created by archiving change update-search-parser-config. Update Purpose after archive.
5
+ ## Requirements
6
+ ### Requirement: Package search parser override
7
+ The system SHALL support a per-portal default and a per-request override to force package search queries through the `text` field when needed.
8
+
9
+ #### Scenario: Portal default applies
10
+ - **WHEN** a portal is configured to force the text-field parser
11
+ - **THEN** `ckan_package_search` uses `text:(...)` for non-fielded queries by default
12
+
13
+ #### Scenario: Request override applies
14
+ - **WHEN** a client explicitly requests the text-field parser
15
+ - **THEN** `ckan_package_search` uses `text:(...)` regardless of portal defaults
16
+
@@ -0,0 +1,26 @@
1
+ # mcp-prompts Specification
2
+
3
+ ## Purpose
4
+ TBD - created by archiving change add-mcp-prompts. Update Purpose after archive.
5
+ ## Requirements
6
+ ### Requirement: Guided prompts are available
7
+ The system SHALL expose a set of guided MCP prompts for common CKAN discovery workflows.
8
+
9
+ #### Scenario: User requests a guided search prompt
10
+ - **WHEN** a client lists MCP prompts
11
+ - **THEN** the guided prompts are returned with name, description, and parameters
12
+
13
+ ### Requirement: Prompt output includes tool usage instructions
14
+ Each guided prompt SHALL produce instructions that reference the appropriate CKAN tools and parameters.
15
+
16
+ #### Scenario: Prompt generation for thematic search
17
+ - **WHEN** the user generates a prompt for a specific theme and format
18
+ - **THEN** the output includes the correct tool name and required parameters
19
+
20
+ ### Requirement: Prompt content is localized and consistent
21
+ Guided prompts SHALL use consistent language and parameter naming aligned with CKAN tool schemas.
22
+
23
+ #### Scenario: Prompt content consistency
24
+ - **WHEN** a prompt is generated
25
+ - **THEN** parameter names match the tool schema (e.g., `server_url`, `q`, `fq`, `rows`)
26
+
@@ -84,11 +84,37 @@ The system SHALL return resource content as JSON with standard MCP resource resp
84
84
  - **THEN** response is truncated to CHARACTER_LIMIT with truncation indicator
85
85
 
86
86
  ### Requirement: Resource Discovery
87
-
88
- The system SHALL list available resource templates when client requests resource list.
87
+ The system SHALL list available resource templates when client requests resource list, including dataset, resource, organization, and dataset filter templates (group, organization, tag, format).
89
88
 
90
89
  #### Scenario: List resource templates
91
-
92
90
  - **WHEN** client calls `resources/listTemplates`
93
- - **THEN** server returns list of available URI templates with descriptions
91
+ - **THEN** server returns list of available URI templates with descriptions, including the dataset filter templates
92
+
93
+ ### Requirement: Group Datasets Resource Template
94
+ The system SHALL expose a resource template for accessing CKAN datasets by group via the URI pattern `ckan://{server}/group/{name}/datasets`.
95
+
96
+ #### Scenario: Read datasets by group
97
+ - **WHEN** client reads `ckan://dati.gov.it/group/governo/datasets`
98
+ - **THEN** server returns JSON with the matching datasets from `package_search`
99
+
100
+ ### Requirement: Organization Datasets Resource Template
101
+ The system SHALL expose a resource template for accessing CKAN datasets by organization via the URI pattern `ckan://{server}/organization/{name}/datasets`.
102
+
103
+ #### Scenario: Read datasets by organization
104
+ - **WHEN** client reads `ckan://dati.gov.it/organization/regione-toscana/datasets`
105
+ - **THEN** server returns JSON with the matching datasets from `package_search`
106
+
107
+ ### Requirement: Tag Datasets Resource Template
108
+ The system SHALL expose a resource template for accessing CKAN datasets by tag via the URI pattern `ckan://{server}/tag/{name}/datasets`.
109
+
110
+ #### Scenario: Read datasets by tag
111
+ - **WHEN** client reads `ckan://dati.gov.it/tag/turismo/datasets`
112
+ - **THEN** server returns JSON with the matching datasets from `package_search`
113
+
114
+ ### Requirement: Format Datasets Resource Template
115
+ The system SHALL expose a resource template for accessing CKAN datasets by resource format via the URI pattern `ckan://{server}/format/{format}/datasets`.
116
+
117
+ #### Scenario: Read datasets by format
118
+ - **WHEN** client reads `ckan://dati.gov.it/format/csv/datasets`
119
+ - **THEN** server returns JSON with the matching datasets from `package_search` filtered by resource format
94
120
 
@@ -0,0 +1,19 @@
1
+ # repository-metadata Specification
2
+
3
+ ## Purpose
4
+ TBD - created by archiving change update-repo-owner-ondata. Update Purpose after archive.
5
+ ## Requirements
6
+ ### Requirement: Canonical repository ownership references
7
+ The project documentation and UI MUST reference the canonical repository under the ondata organization once the repository is migrated.
8
+
9
+ #### Scenario: Repository references updated
10
+ - **WHEN** a user follows documentation or UI links to the repository
11
+ - **THEN** the links point to the ondata organization repository URL
12
+
13
+ ### Requirement: NPM package ownership remains personal
14
+ The project MUST continue to document the npm package under the @aborruso scope unless an explicit npm ownership change is approved.
15
+
16
+ #### Scenario: npm install instructions unchanged
17
+ - **WHEN** a user follows installation instructions
18
+ - **THEN** the package name remains @aborruso/ckan-mcp-server
19
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aborruso/ckan-mcp-server",
3
- "version": "0.4.10",
3
+ "version": "0.4.13",
4
4
  "description": "MCP server for interacting with CKAN open data portals",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -0,0 +1,14 @@
1
+ # File YAML per commenti privati su ckan-mcp-server
2
+ # Ogni elemento rappresenta un commento ricevuto
3
+ - autore: Giovanni Pirrotta
4
+ data: 2026-01-19
5
+ modalità: telegram
6
+ testo: >
7
+ Ciao Andy, se non lo hai già fatto secondo me dovresti contattare AGID. È uno strumento così potente da meritare massima visibilità, non solo per le PA, ma soprattutto per chi oggi fa fatica a trovare dati o a interagire con i portali opendata. Vedo grandissime potenzialità!!
8
+ - autore: Matteo Fortini
9
+ data: 2026-01-18
10
+ modalità: call
11
+ testo: |
12
+ Ho visto il video, molto interessante, lo proverò e lo racconterò.
13
+ Ti confesso che quando vedo queste cose mi chiedo se si possa implementare un meccanismo di feedback per ottenere lo stesso risultato con meno risorse. Intendo "come miglioreresti questo sito/API per renderlo più efficiente per queste cose?"
14
+ In generale a me piacerebbe che la IA pian piano venisse usata per "oliare" le interazioni, adesso la stiamo usando come una schiava per fare le cose scomode