English · Italiano
A lightweight Model Context Protocol server that exposes the Normattiva OpenData API — the official Italian legislation portal run by IPZS — to LLM clients like Claude Desktop, Cursor, and Claude Code.
It wraps the synchronous read endpoints (search, article detail, recent updates) and the static reference catalogs as MCP tools, resources, and prompts.
Landing page & quickstart: normattiva-mcp.adellorto.com — copy-paste client configs for Claude, ChatGPT, Cursor and stdio. Italian version at /it/.
| Tool | Purpose |
|---|---|
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. |
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. |
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. |
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. |
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. |
| URI | Contents |
|---|---|
normattiva://tipologiche/denominazioni |
Act-type codes (e.g. PLE → LEGGE, PDL → DECRETO-LEGGE). Use the value as the denominazione argument of search_acts. |
normattiva://tipologiche/classi-provvedimento |
Vigenza class codes: 1 = senza aggiornamenti, 2 = aggiornato, 3 = abrogato. |
normattiva://collezioni-predefinite |
Predefined collections of acts with their item counts. |
normattiva://ricerche-predefinite |
Predefined searches with their preset filters. |
| Name | Args | Purpose |
|---|---|---|
ricerca-articolo |
argomento |
Search Italian law on a topic and read the most relevant articles. |
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). |
Run via npx:
npx normattiva-mcpAdd to your MCP client config (claude_desktop_config.json for Claude Desktop, or ~/.claude.json / .mcp.json for Claude Code):
{
"mcpServers": {
"normattiva": {
"command": "npx",
"args": ["-y", "normattiva-mcp"]
}
}
}In .cursor/mcp.json:
{
"mcpServers": {
"normattiva": {
"command": "npx",
"args": ["-y", "normattiva-mcp"]
}
}
}The server always talks to Normattiva's production OpenData API (https://api.normattiva.it/t/normattiva.api). The endpoint is hard-coded — there is no environment variable to override it. The PRE/test environment is intentionally not supported because it is mTLS-protected.
The OpenData API is open and does not require authentication.
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.
Hosted endpoint: https://normattiva-mcp.adellorto.com/api/mcp — free, no signup, no auth.
Client config:
{
"mcpServers": {
"normattiva": {
"url": "https://normattiva-mcp.adellorto.com/api/mcp"
}
}
}To run your own copy on a Vercel account:
git clone https://github.com/adellorto/normattiva-mcp.git
cd normattiva-mcp
pnpm install
cd packages/http-server
npx vercel deployThe 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.
Requires Node.js ≥ 20.
git clone <repo>
cd normattiva-mcp
npm install
npm run build
node dist/index.js # speaks MCP on stdioA live smoke test (hits the public API):
node tests/smoke.mjs- No table-of-contents endpoint. Normattiva OpenData has no synchronous TOC.
list_articlesis a best-effort probe: it callsread_articleforarticolo=1,2,3,…until it stops finding results. By default it discovers base articles only;include_suffixes: trueextends the probe tobis/ter/quater/… (round-by-round, stopping each chain at the first miss) but cannot find sub-articles whose base is missing, nor articles inside structured groups (id_gruppo != 0). id_gruppois opaque. Some atti — typically structured codes (codici, testi unici, leggi articolate) — return content only whenread_articleis called with a non-zeroid_gruppo. The OpenData API offers no way to discover the right value synchronously. Practical strategy: tryid_gruppo=0first; if results look empty or wrong, try small integers (1, 2, 3, …) until the article comes back.- One article per call upstream. Normattiva's
dettaglio-attoendpoint serves one article at a time.read_articleexposes that directly (parallelize from the client when reading several articles);read_acthides the orchestration and returns a single aggregated payload, capped bymax_charsand resumable viaarticolo_successivo. - HTML stripping. The default
format: "text"collapses Normattiva's HTML markup but preserves the Italian-law amendment markers(( ... ))(insertions) and[ ... ](deletions), which carry semantic meaning. Hyperlinks to other normative acts are inlined as<text> [urn:nir:…]so the Akoma Ntoso URN survives in plain text. - Article versioning is non-discoverable. Each
read_articlecall returns the version of the article in force ondata_vigenza(the API'sversione=0default). The OpenData API does not expose how many historical versions exist for an article, so previous texts can only be retrieved by varyingdata_vigenza(or by guessingversione=1, 2, …). is_abrogatedis heuristic. The flag matches the canonical((ARTICOLO ABROGATO …))marker that successive amendments insert at the head of a wholly-abrogated article. Articles where individual commi are abrogated but the article itself is alive (e.g.((COMMA ABROGATO …))in a few subsections) are not flagged.- Async export and PRE environment (mTLS-protected) are not wrapped — this server stays light and synchronous.
recent_updatestreatsdata_fineas inclusive end-of-day (UTC). The window is capped at 12 months and 7000 atti by the API; both limits surface as IPZS error codes (1501,1502).
This server's source code is MIT — see LICENSE.
Legislative content retrieved through this server is published by IPZS under CC BY 4.0. The server is a pass-through and does not relicense the content: any downstream use or redistribution must attribute Normattiva / IPZS as the source.
Normattiva is a trademark of the Istituto Poligrafico e Zecca dello Stato Italiano (IPZS); this project is not affiliated with or endorsed by IPZS.