legislation-nz-mcp
This MCP server lets you search and read New Zealand legislation from the official legislation.govt.nz API.
Search Acts, Bills, secondary legislation, and amendment papers by title or full text, or browse by filters alone.
Filter by type, status, classification, administering agency, publisher, and more.
List every point-in-time version of a work and find the version in force on a given date.
Get metadata and download URLs for a specific version in HTML, PDF, XML, or original scanned PDF.
List a document's structure: Parts, subparts, sections, and schedules.
Read a whole document, a single section, or a single schedule as clean plain text.
Exclude cover pages, history notes, comparative references, editorial notes, defined-term links, and end matter.
Retrieve a PDF or original scan URL instead of text.
Search and monitor legislation through legacy RSS/Atom feeds, if you have a separate RSS key.
Check remaining daily API and feed quota.
Provides tools for searching New Zealand legislation and listing version histories through legacy Atom/RSS feed endpoints, returning parsed feed entries and subscribable feed URLs for change monitoring.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@legislation-nz-mcp What does section 22 of the Privacy Act 2020 say?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
legislation-nz-mcp
A Model Context Protocol server for the New Zealand legislation.govt.nz developer API.
It lets an MCP client such as Claude search New Zealand legislation, browse every point-in-time version of an Act, Bill, or piece of secondary legislation, and read the text of a single section or schedule, all through the official API published by the Parliamentary Counsel Office (PCO).
Written in TypeScript on the official MCP SDK. It runs over stdio for local clients (Claude Desktop, Claude Code, Cursor, and any other MCP client) or over streamable HTTP as a standalone localhost service.
What it can do
Ask your assistant things like:
"What does section 22 of the Privacy Act 2020 say?"
"List every version of the Crimes Act 1961 and give me the one in force on 1 July 2013."
"Which Acts mention 'notifiable privacy breach'?"
"Show me all in-force secondary legislation administered by the Takeovers Panel."
"What is the structure of the Trusts Act 2019, and which Part covers trustees' duties?"
"Give me the PDF of the Crimes Act as originally enacted in 1961."
"How much of my daily API quota is left?"
Every answer comes with the official website link for the work or version so a reader can verify it at the source.
Related MCP server: au-eli-mcp
Tools
Tool | What it does |
| Search by title or full text with every filter the API offers, or browse by filters alone. Returns matching works, their newest matching version, website URLs, and a flag for content matches that only hit an older version. |
| Every point-in-time version of a work, with the work's metadata reported once at the top. Fetches all pages automatically, or one page on request. |
| Metadata for a single version, including the download URLs for each available format ( |
| The structure of a document: Parts, subparts, section numbers and headings, and schedules. Use it to find a section number before reading text. |
| The text of a whole document, a single section, or a single schedule, as clean plain text from the official XML. Can also return a PDF download URL instead. |
| The same search through the legacy Atom feed endpoint, returning parsed feed entries and a subscribable feed URL. Needs a separate RSS key. |
| A work's version history as an Atom feed, with a subscribable URL for change monitoring. Needs a separate RSS key. |
| Remaining daily quota for the API key and the feed key, read from the API's rate-limit response headers. |
Typical workflows
Read one section of an Act
search_legislationwithsearch_term: "Privacy Act 2020"gives thework_idact_public_2020_31.list_sectionswith thatwork_idlists every Part and section heading.get_legislation_textwithwork_idandsection: "22"returns just that section, with its Part and subpart context.
Read the law as it stood on a date
list_versionswith thework_idlists every version with its effective date.get_legislation_textwith the chosenversion_idand asectionorschedule.
Find legislation by what it says
search_legislation with search_field: "content" searches the body of every
document rather than titles. Wrap a phrase in double quotes to require the
exact words; otherwise stemming is on and "levy" also matches "levies".
Browse without a search term
Omit search_term and pass filters alone. Type-specific filters imply the
type, so act_status: "in_force" on its own returns in-force Acts, and
instrument_type_group: "regulations" returns only regulations.
Tool reference
search_legislation
Parameter | Notes |
| Optional. ElasticSearch simple query syntax. Omit to browse by filters. |
|
|
|
|
|
|
| Acts only. |
| Bills only. |
| Secondary legislation only. |
| The agency's full name exactly as listed on the browse agencies page. |
|
|
|
|
| Default 20 per page, maximum 100. |
Mixing filters from two types, or contradicting an explicit
legislation_type, returns a clear error rather than an empty result.
Each result includes work_id, title, url (the work's latest page),
publisher, administering_agencies, the type-specific fields, and the
matching version: latest_matching_version_id, its date, its URL, the format
types it offers, and latest_matching_version_is_latest. That last flag is
false when a content search matched only an older version, for example text
that has since been repealed.
list_versions
Takes a work_id and optional sort (desc by default). With no page it
fetches every page (the API serves at most 100 per page) and returns them all,
with pages_fetched and a truncated flag. With page (and optionally
per_page) it returns one page with has_more.
Work-level metadata (title, type, status, agencies) is reported once at the
top. Each version carries its version_id, version_date, url, and format
types; the newest version carries is_latest_version: true.
get_version_details
Takes a version_id. Returns the version's metadata, its url, the work's
work_url, and formats[] with a download URL for each format.
list_sections
Takes a version_id or a work_id (the newest version is used). Returns
parts[] with their subparts and sections, any sections outside a Part, and
schedules[]. Needs the XML format, which all PCO-published documents have.
get_legislation_text
Parameter | Notes |
| One is required. A |
| A section number such as |
| A schedule number such as |
|
|
| Truncates whole-document output (default 100,000 characters). Not applied to a single section or schedule. |
| Content types to strip: |
Text comes from the official XML format, which carries the document structure.
The HTML format is only a fallback, because it is the full website page. The
table of contents is always dropped, since list_sections covers it better.
Each table row is one line, with cells separated by | and paragraphs
inside a cell by /. Blank or spanned cells keep an empty slot, so every
cell stays under its column heading (| | Support for Walking Access | 3,595).
A literal | in cell text is escaped as \|. Footnotes appear inline as
[footnote: …], and text a bill strikes out as [deleted: …].
search_legislation_rss and list_versions_rss
These call the two legacy feed endpoints in the API documentation. They need a
separate RSS-only key (see Setup). Entries are returned parsed: title, website
URL, timestamps, and a derived work_id and, for dated entries, version_id,
so results can be passed straight to the JSON tools. Each response includes
the public feed_url (without your key) for subscribing in a feed reader. The
search feed accepts search_term, search_field (title or content),
legislation_type, legislation_status, and a client-side limit; it returns
at most 100 entries and does not paginate.
get_rate_limit_status
Returns the latest quota figures for the API key and the feed key: limit,
remaining, and when the quota resets, in UTC and New Zealand time. By default
it spends one minimal request to refresh the API figure; refresh can be
api, rss, both, or none.
Quota on every response
Every tool result carries a rate_limit object taken from the response
headers of the request the tool just made:
"rate_limit": {
"scope": "api",
"limit": 10000,
"remaining": 9997,
"resets_at": "2026-09-22T12:00:00.000Z",
"resets_at_nz": "23/09/2026, 12:00:00 am NZST"
}Links and ephemeral identifiers
Results carry website links (url) built from the identifiers, following the
URL pattern the API documents: a work links to its latest alias and a
version to its dated page. Identifiers containing ~ are ephemeral (built
from fallback data for agency-published legislation and liable to change) and
carry ephemeral: true.
Setup
Requirements
Node.js 18 or later.
A legislation.govt.nz developer API key. Request one on the developer API page.
Optionally, an RSS-only key for the two feed tools. The feeds reject the regular API key, and the other six tools do not need the RSS key.
Install and build
git clone https://github.com/ezydubs/legislation-nz-mcp.git
cd legislation-nz-mcp
npm install
npm run buildProvide the API key
The recommended way is a .env file in the project root. The server loads it
at startup, so the key stays out of your shell and out of your MCP client
config:
cp .env.example .env
# edit .env and set LEGISLATION_NZ_API_KEY=....env is gitignored. A key already set in the environment, for example via an
MCP client env block, takes precedence over the file.
Use with Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"legislation-nz": {
"command": "node",
"args": ["/absolute/path/to/legislation-nz-mcp/dist/index.js"],
"env": {
"LEGISLATION_NZ_API_KEY": "your_api_key_here"
}
}
}
}Leave out the env block if you use a .env file.
Use with Claude Code
claude mcp add legislation-nz -- node /absolute/path/to/legislation-nz-mcp/dist/index.jsAdd --env LEGISLATION_NZ_API_KEY=your_api_key_here if you are not using a
.env file. Add -s user to make the server available in every project.
Use with other MCP clients
Any client that can launch a stdio server works: the command is node and the
single argument is the absolute path to dist/index.js.
Run over HTTP
The server can also run as its own localhost HTTP service, for example behind a reverse proxy or gateway that handles authentication:
npm run start:httpIt serves the MCP streamable HTTP transport at http://127.0.0.1:8091/mcp.
Override the bind address with LEGISLATION_HTTP_HOST and
LEGISLATION_HTTP_PORT. The HTTP entry point has no authentication of its own
and must not be exposed on a public interface.
Environment variables
Variable | Required | Purpose |
| Yes | Key for the JSON API (10,000 requests/day). |
| No | RSS-only key for the two feed tools (1,500 requests/day). |
| No | Bind address for the HTTP transport (default |
| No | Port for the HTTP transport (default |
How it works
Search and metadata come from the JSON API at
https://api.legislation.govt.nz/v0/.Document text is fetched from the
formats[].urllinks a version advertises. The XML format is parsed to find a section by its label inside the main body only, so schedule provisions with the same number never collide, and to walk up to the enclosing Part and subpart.Feeds come from
https://www.legislation.govt.nz/api/rss/, are parsed as Atom, and have their website URLs mapped back to identifiers.Quota headers on every response are recorded per key and reported.
Errors are returned as MCP tool errors with readable messages, never as a crashed server.
Key handling
The API key is sent only to
legislation.govt.nzhosts. Agency-published secondary legislation can link to the agency's own website, and no key is sent there.The feed key travels in the query string because the feed endpoints accept nothing else. It is scrubbed from any error text and never included in the
feed_urlreturned to the client.Keys never appear in tool output.
Retries
Transient upstream errors (502, 503, 504) are retried up to three times with capped backoff. A 429 is not retried: it means the daily quota is spent until midnight New Zealand time, and the error message says when that is. A 403 means the burst limit (2,000 requests per five minutes per IP) was hit.
API notes
Base URLs:
https://api.legislation.govt.nzfor the JSON API;https://www.legislation.govt.nz/api/rss/for the legacy feeds.Auth:
X-Api-Keyheader for the JSON API;api_keyquery parameter for the feeds.Rate limits: 10,000 requests/day per API key, 1,500/day per feed key, 2,000 requests per five minutes per IP. Quotas reset at midnight NZ time.
The API returns document metadata; the text itself lives behind the
formats[].urllinks.Official documentation: https://api.legislation.govt.nz/docs/.
Observed API behaviour
The following notes record places where the live API behaves differently from what the published documentation implies. Each one affected the client implementation and may be useful to others building against the same API. Behaviour was verified against live responses in September 2026; confirm against the canonical documentation before relying on any point.
List responses use a
resultsenvelope, not a bare array. Search and version listings return{ "results": [ ... ], "total": N, "page": N, "per_page": N }.titleis on the version, not the work. In search results a work has no top-leveltitle; it appears only inlatest_matching_version.title.Versions carry no discrete date field. The effective date is encoded in the trailing segment of
version_id(e.g.act_public_2020_31_en_2026-05-01), possibly with a deduplication suffix letter (2020-06-30B), and must be parsed from the id.administering_agenciesis a string filter but an array in responses.The
htmlformat URL returns the full website page, with navigation and menus. Thexmlformat is the clean, machine-readable content.Identifiers are not zero-padded. Observed ids are
act_public_2020_31, notact_public_2020_0031.The versions endpoint paginates, and accepts
page/per_page. The documentation lists neither parameter for it, but the default page holds 20 versions and both parameters work;per_pageis capped at 100. The Income Tax Act 2007 has 206 versions, so a full listing takes three requests.The versions endpoint does not send
is_latest_version, only search does. The server derives it from the sort order.The legacy feeds live on a different host, need a different key, and are Atom.
/api/rss/search/and/api/rss/works/{work_id}/versions/return 404 onapi.legislation.govt.nz; they are served fromwww.legislation.govt.nz. They accept the key only as theapi_keyquery parameter, and a regular API key is rejected (401 Unauthorized API key). The body is an Atom 1.0 feed, not RSS 2.0. The search feed returns up to 100 entries and ignorespageandper_page. The documentedsearch_field=fulltextvalue returns no entries at all; the feed honourscontent, so the tool sendscontentfor either. An unknownwork_idin the versions feed returns HTTP 200 with<id>not_found</id>and no entries rather than a 404.Agency-published secondary legislation links off-site. Its format URLs can point at the agency's own website, and such records usually offer only a PDF, so
list_sectionsand text extraction are unavailable for them.get_legislation_textwithformat: "pdf"returns the link.
Development
npm run build # compile to dist/
npm run dev # compile on change
npm run typecheck # type-check without emitting
npm start # run the stdio server
npm run start:http # run the HTTP serverThere is no automated test suite yet. Changes have been verified with a stdio MCP client script exercising every tool against the live API, and with a script that extracts the OpenAPI spec from the docs page and checks every path, parameter, enum value, response field, and header against the source.
Limitations and future work
No caching. Every call hits the API. An in-memory or on-disk cache for document fetches and version lists would stretch the daily quota for repeated reads.
Agency-published documents are usually PDF-only and hosted off-site, so their text cannot be extracted.
Change monitoring. A "notify me of changes" feature could poll the version feed from
list_versions_rssand diff versions.
Project layout
src/
├─ index.ts # stdio entry point (Claude Desktop / Claude Code)
├─ http.ts # streamable HTTP entry point (localhost service)
├─ server.ts # transport-independent server factory; registers all tools
├─ client.ts # HTTP client: auth, retries, rate-limit headers, feed fetching
├─ types.ts # API response types (confirmed against live responses)
├─ util.ts # response envelope, version dates, website URLs, ephemeral ids
├─ format.ts # HTML → plain text
├─ xml.ts # XML parsing and document-structure extraction
├─ feed.ts # Atom feed parsing for the legacy /api/rss/ endpoints
└─ tools/
├─ search.ts # search_legislation
├─ versions.ts # list_versions
├─ versionDetails.ts # get_version_details
├─ listSections.ts # list_sections
├─ getText.ts # get_legislation_text
├─ searchRss.ts # search_legislation_rss
├─ versionsRss.ts # list_versions_rss
├─ rateLimit.ts # get_rate_limit_status
└─ resolve.ts # work_id → newest version_id helperLicense
MIT
Available Tools
8 toolsget_legislation_textA
Retrieve the text of a piece of legislation. Provide a version_id or work_id. Without section/schedule params, returns the whole document as cleaned plain text. With a section param (e.g. "22" or "25A"), returns just that section with its Part/subpart context. With a schedule param (e.g. "1"), returns just that schedule. Use list_sections to discover numbers first. With format:"pdf", returns the official PDF download URL instead of text; format:"pdf_original_scan" returns the scan of the original printed Act (pre-2008 as-enacted versions only).
Inputs: format, exclude, section, work_id, +3 more.
get_rate_limit_statusA
Report remaining daily quota from the X-RateLimit-* response headers: the JSON API key (10,000 requests/day) and the feed key (1,500 requests/day). Quotas reset at midnight NZ time. By default spends one minimal API request to get fresh numbers; refresh:"none" returns the values last observed. The separate burst limit of 2,000 requests per 5 minutes per IP is not reported in headers.
Inputs: refresh.
get_version_detailsA
Get metadata for a single version, including the list of available document formats (html, pdf, xml) and their URLs.
Inputs: version_id (required).
list_sectionsA
List the structure of a legislation document: Parts, subparts, section numbers and headings, and schedules. Use this before get_legislation_text to find the right section or schedule number.
Inputs: work_id, version_id.
list_versionsA
List all point-in-time versions of a work (identified by work_id), newest first by default, each with its website URL; the newest carries is_latest_version: true. Work-level metadata (title, type, status, agencies) is reported once at the top rather than repeated per version. Fetches every page automatically (the API serves at most 100 per page); pass page (and optionally per_page) to fetch a single page instead. Use this to find a version_id for a specific date.
Inputs: work_id (required), page, sort, per_page.
list_versions_rssA
Read the legacy Atom feed of a work's versions (the documented /api/rss/works/{work_id}/versions/ endpoint). Returns one entry per version with its website URL, derived version_id, and publication timestamp, plus a subscribable feed URL for change monitoring. Needs LEGISLATION_NZ_RSS_API_KEY, a separate RSS-only key. For on-demand queries prefer list_versions.
Inputs: work_id (required).
search_legislationA
Search New Zealand legislation (acts, bills, secondary legislation, amendment papers) by title or full-text content, or browse by omitting search_term and using filters alone. Returns matching works with their newest matching version id, website URLs, publisher, and available formats. latest_matching_version_is_latest is false when a content search matched only an older version (e.g. text since repealed). Type-specific filters (act_*, bill_*, instrument_*) imply legislation_type; mixing types is rejected. Ids containing ~ are ephemeral and carry ephemeral: true. Use this first to find a work_id or version_id.
Inputs: page, sort_by, act_type, per_page, +13 more.
search_legislation_rssA
Search New Zealand legislation through the legacy Atom feed (the documented /api/rss/search/ endpoint) by title or full text, with type and status filters; omit search_term to browse by filters alone. Returns up to 100 feed entries (title, website URL, derived work_id/version_id, timestamps) plus a subscribable feed URL. Needs LEGISLATION_NZ_RSS_API_KEY, a separate RSS-only key. For on-demand queries prefer search_legislation, which is a superset.
Inputs: limit, search_term, search_field, legislation_type, +1 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
Each tool has a clearly distinct purpose: searching vs listing versions vs retrieving text vs structure vs rate limits. The RSS variants are explicitly marked as legacy alternatives and are superseded by their modern counterparts, so no ambiguity remains.
All tools follow a consistent snake_case verb_noun pattern (search_, list_, get_). The _rss suffix is applied uniformly to the two RSS-specific tools, and the rest follow the same style without deviation.
8 tools is well-scoped for a legislation retrieval server. Each tool serves a distinct need—search, version enumeration, metadata, text, structure, RSS alternatives, and quota monitoring—without unnecessary bloat.
The tool surface covers the full read-only lifecycle: discovery (search, browse), version exploration, document retrieval in multiple formats, structure navigation, and rate limit monitoring. No obvious gaps exist for the domain; even PDF downloads and legacy RSS access are included.
Related MCP Connectors
Search and read UK legislation at any date, resolve citations, list amendments, track changes.
Japan Law MCP — Japanese national laws & ordinances via the e-Gov Law API.
Search UK Acts, Statutory Instruments, and legislation with full text retrieval
Official MCP connector for Legalize: read and search its whole open corpus, at any point in time.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables searching and retrieval of UK legislation (Acts, Statutory Instruments) from legislation.gov.uk with persistent identifiers and verifiable citations.965 PyPIApache 2.0
- AlicenseAqualityBmaintenanceMCP server for Australia's Federal Register of Legislation. Enables searching and fetching Commonwealth Acts with verifiable citations.375 PyPIApache 2.0
- AlicenseAqualityBmaintenanceEnables retrieval of Canadian federal legislation (Acts and regulations) from the Justice Laws Website by code, providing metadata and full consolidated text in English and French.335 PyPIApache 2.0
- AlicenseAqualityBmaintenanceMCP server for the Federal Register of Legislation, enabling point-in-time searches, retrieval, and verification of Australian Commonwealth law, including citation checking.886 npm5MIT