normattiva-mcp 0.1.2 → 0.1.4

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 (3) hide show
  1. package/README.md +35 -3
  2. package/dist/index.js +12 -12
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -12,11 +12,11 @@ It wraps the synchronous read endpoints (search, article detail, recent updates)
12
12
 
13
13
  | Tool | Purpose |
14
14
  | --- | --- |
15
- | `search_acts` | Search Italian legislation by free text, title, act type, year/number, date range, vigenza class, or in-force date. Returns metadata for each matching atto including `codice_redazionale` and `data_gu` (which `read_article` needs). |
15
+ | `search_acts` | Search Italian legislation by free text, title, act type, year/number, date range, vigenza class, or in-force date. Returns metadata for each matching atto including `codice_redazionale` and `data_gu` (which `read_article` needs). To find acts **newly published** in Gazzetta Ufficiale within a window, set `data_pubblicazione_da` / `data_pubblicazione_a` — this is the right tool for "what was published recently?", not `recent_updates`. |
16
16
  | `list_articles` | Discover the article numbers of an atto by sequentially probing `read_article` from `articolo=1`. Finds base articles by default; pass `include_suffixes: true` to also probe `bis`/`ter`/`quater`/… for each one. Each entry carries optional `is_preamble` and `is_abrogated` flags (informational; nothing is filtered server-side). Articles inside structured groups (`id_gruppo != 0`) are still not enumerated automatically. |
17
17
  | `read_article` | Fetch the text of a single article of a specific atto at a given date of vigenza. Returns plain text by default; pass `format: "html"` for the original markup (preserves amendment markers). In text mode, hyperlink targets are inlined as `L. 5/2003 [urn:nir:stato:legge:2003-06-05;131]` so URN citations survive the conversion. Successful responses include `found: true`; non-existent articles (wrong `sotto_articolo`, `id_gruppo`, or out-of-range `articolo`) return `{ found: false, reason, richiesta }` instead of throwing — so probing is exception-free. The LLM can call this in parallel for several articles of the same atto. |
18
18
  | `read_act` | Aggregate-read of an entire atto: internally enumerates articles and fetches each one, returning them in order capped by `max_chars` (default 80 000 ≈ 20k Claude tokens). Honors `include_suffixes` and `id_gruppo` like `list_articles`. When the budget is exhausted the response includes `truncated: true`, `truncated_reason`, and `articolo_successivo` for resuming via a follow-up call with `articolo_da` set to that value. |
19
- | `recent_updates` | Lists atti normativi modified within a date window (max 12 months). Useful for monitoring legislative changes. |
19
+ | `recent_updates` | Lists atti normativi whose **consolidated text was amended** within a date window (max 12 months) — i.e. the in-force version changed because another act modified it. Does **not** list acts newly published in Gazzetta Ufficiale; for those, use `search_acts` with `data_pubblicazione_da` / `data_pubblicazione_a`. |
20
20
 
21
21
  ### Resources
22
22
 
@@ -32,7 +32,7 @@ It wraps the synchronous read endpoints (search, article detail, recent updates)
32
32
  | Name | Args | Purpose |
33
33
  | --- | --- | --- |
34
34
  | `ricerca-articolo` | `argomento` | Search Italian law on a topic and read the most relevant articles. |
35
- | `monitoraggio-modifiche` | `giorni` (default `7`) | Summarize legislative changes in the last *N* days. |
35
+ | `monitoraggio-modifiche` | `giorni` (default `7`) | Summarize amendments to the consolidated text of existing acts in the last *N* days (uses `recent_updates`; for newly published acts use `search_acts` with `data_pubblicazione_da/a`). |
36
36
 
37
37
  ## Installation & client configuration
38
38
 
@@ -78,6 +78,38 @@ The server always talks to Normattiva's **production OpenData API** (`https://ap
78
78
 
79
79
  The OpenData API is open and does not require authentication.
80
80
 
81
+ ## HTTP transport
82
+
83
+ In addition to the npm-published stdio binary, this repo also ships `packages/http-server/` — a Vercel deployment of the same MCP server over the Streamable HTTP transport. Useful for clients that need a remote endpoint instead of a local subprocess.
84
+
85
+ **Hosted endpoint:** `https://normattiva-mcp.adellorto.com/api/mcp` — free, no signup, no auth.
86
+
87
+ Client config:
88
+
89
+ ```json
90
+ {
91
+ "mcpServers": {
92
+ "normattiva": {
93
+ "url": "https://normattiva-mcp.adellorto.com/api/mcp"
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ ### Self-hosting
100
+
101
+ To run your own copy on a Vercel account:
102
+
103
+ ```bash
104
+ git clone https://github.com/adellorto/normattiva-mcp.git
105
+ cd normattiva-mcp
106
+ pnpm install
107
+ cd packages/http-server
108
+ npx vercel deploy
109
+ ```
110
+
111
+ The handler exposes only Streamable HTTP (SSE is removed from the 2025-03-26 MCP spec). No authentication by default — anyone with the URL can call it.
112
+
81
113
  ## Building from source
82
114
 
83
115
  Requires Node.js ≥ 20.
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ var COMMON_HEADERS = {
15
15
  Accept: "application/json, text/plain, */*",
16
16
  "Accept-Language": "it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7",
17
17
  Origin: "https://dati.normattiva.it",
18
- "User-Agent": "normattiva-mcp/0.1.2 (+https://www.npmjs.com/package/normattiva-mcp)"
18
+ "User-Agent": "normattiva-mcp/0.1.4 (+https://www.npmjs.com/package/normattiva-mcp)"
19
19
  };
20
20
  var REQUEST_TIMEOUT_MS = 2e4;
21
21
  async function request(path, init) {
@@ -165,7 +165,7 @@ function jsonContent(data) {
165
165
  function registerTools(server, _ctx) {
166
166
  server.registerTool("search_acts", {
167
167
  title: "Search Italian legislation (Normattiva)",
168
- description: "Search Italian legislation on Normattiva by free text, title, act type, year/number, date range, or vigenza class. Returns metadata for each matching atto including codice_redazionale and data_gu, which are required to fetch article text via read_article. The denominazione parameter expects an Italian act-type label like 'LEGGE' or 'DECRETO LEGISLATIVO' \u2014 see resource normattiva://tipologiche/denominazioni for the full list.",
168
+ description: "Search Italian legislation on Normattiva by free text, title, act type, year/number, date range, or vigenza class. Returns metadata for each matching atto including codice_redazionale and data_gu, which are required to fetch article text via read_article. The denominazione parameter expects an Italian act-type label like 'LEGGE' or 'DECRETO LEGISLATIVO' \u2014 see resource normattiva://tipologiche/denominazioni for the full list. To find acts newly published in Gazzetta Ufficiale within a window, set data_pubblicazione_da and data_pubblicazione_a (this is the right tool for 'what was published recently?', not recent_updates).",
169
169
  inputSchema: {
170
170
  testo: z.string().optional().describe("Keywords searched in the act body."),
171
171
  titolo: z.string().optional().describe("Keywords searched in the act title."),
@@ -176,8 +176,8 @@ function registerTools(server, _ctx) {
176
176
  giorno: z.number().int().min(1).max(31).optional(),
177
177
  data_emanazione_da: dateString.optional(),
178
178
  data_emanazione_a: dateString.optional(),
179
- data_pubblicazione_da: dateString.optional(),
180
- data_pubblicazione_a: dateString.optional(),
179
+ data_pubblicazione_da: dateString.optional().describe("Start of the publication window in Gazzetta Ufficiale (YYYY-MM-DD). Use this (with data_pubblicazione_a) to answer 'which acts were published recently'."),
180
+ data_pubblicazione_a: dateString.optional().describe("End of the publication window in Gazzetta Ufficiale (YYYY-MM-DD)."),
181
181
  classe: z.enum(["1", "2", "3"]).optional().describe("Vigenza class: 1=senza aggiornamenti (single-version atto), 2=aggiornato, 3=abrogato."),
182
182
  codice_tipo: z.string().optional().describe("Faceted filter: act-type code (e.g. 'PLE' for LEGGE)."),
183
183
  data_vigenza: dateString.optional().describe("Restrict results to atti in force on this date (YYYY-MM-DD). Maps to the spec's 'vigenza' field."),
@@ -456,11 +456,11 @@ function registerTools(server, _ctx) {
456
456
  });
457
457
  });
458
458
  server.registerTool("recent_updates", {
459
- title: "Atti recently modified on Normattiva",
460
- description: "Lists Italian normative acts whose text was modified within a date window. Useful for monitoring legislative changes. The window must be at most 12 months wide and data_fine cannot precede data_inizio.",
459
+ title: "Atti whose consolidated text was amended on Normattiva",
460
+ description: "Lists Italian normative acts whose consolidated text was amended within a date window (i.e. the in-force version changed because another act modified it). Does NOT return acts newly published in Gazzetta Ufficiale \u2014 for 'what was published recently', call search_acts with data_pubblicazione_da / data_pubblicazione_a instead. Useful for monitoring how existing legislation evolves. The window must be at most 12 months wide and data_fine cannot precede data_inizio.",
461
461
  inputSchema: {
462
- data_inizio: dateString.describe("Start of the modification window (YYYY-MM-DD)."),
463
- data_fine: dateString.describe("End of the modification window (YYYY-MM-DD), max 12 months after data_inizio.")
462
+ data_inizio: dateString.describe("Start of the amendment window (YYYY-MM-DD). Refers to the date the consolidated text changed, not to publication in Gazzetta Ufficiale."),
463
+ data_fine: dateString.describe("End of the amendment window (YYYY-MM-DD), max 12 months after data_inizio.")
464
464
  }
465
465
  }, async ({ data_inizio, data_fine }) => {
466
466
  const data = await ricercaAggiornati({
@@ -698,8 +698,8 @@ Procedi cos\xEC:
698
698
  ]
699
699
  }));
700
700
  server.registerPrompt("monitoraggio-modifiche", {
701
- title: "Monitora modifiche normative recenti",
702
- description: "Recupera gli atti normativi italiani modificati negli ultimi N giorni e ne fornisce un riepilogo.",
701
+ title: "Monitora modifiche al testo consolidato di atti esistenti",
702
+ description: "Recupera gli atti normativi italiani il cui testo consolidato \xE8 stato modificato negli ultimi N giorni e ne fornisce un riepilogo. Per gli atti pubblicati di recente in Gazzetta Ufficiale usa invece search_acts con data_pubblicazione_da/a.",
703
703
  argsSchema: {
704
704
  giorni: z2.string().regex(/^\d+$/, "Numero di giorni come stringa intera").default("7").describe("Quanti giorni indietro guardare (default 7).")
705
705
  }
@@ -714,7 +714,7 @@ Procedi cos\xEC:
714
714
  role: "user",
715
715
  content: {
716
716
  type: "text",
717
- text: `Recupera con recent_updates gli atti normativi italiani modificati tra il ${fmt(inizio)} e il ${fmt(oggi)}. Raggruppa i risultati per tipologia di atto e fornisci un riepilogo delle modifiche pi\xF9 rilevanti, evidenziando gli atti modificanti principali.`
717
+ text: `Recupera con recent_updates gli atti normativi italiani il cui testo consolidato \xE8 stato modificato tra il ${fmt(inizio)} e il ${fmt(oggi)} (non si tratta di nuove pubblicazioni in Gazzetta Ufficiale: per quelle usa search_acts con data_pubblicazione_da/a). Raggruppa i risultati per tipologia di atto e fornisci un riepilogo delle modifiche pi\xF9 rilevanti, evidenziando gli atti modificanti principali.`
718
718
  }
719
719
  }
720
720
  ]
@@ -726,7 +726,7 @@ Procedi cos\xEC:
726
726
  async function main() {
727
727
  const server = new McpServer({
728
728
  name: "normattiva-mcp",
729
- version: "0.1.2"
729
+ version: "0.1.4"
730
730
  });
731
731
  const ctx = {};
732
732
  registerTools(server, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "normattiva-mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "MCP server wrapping the Normattiva OpenData API for Italian legislation (search, read articles, monitor recent updates).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,10 +44,10 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@modelcontextprotocol/sdk": "^1.29.0",
47
- "@normattiva/core": "workspace:*",
48
47
  "zod": "^4.4.3"
49
48
  },
50
49
  "devDependencies": {
50
+ "@normattiva/core": "workspace:*",
51
51
  "esbuild": "^0.24.0"
52
52
  }
53
53
  }