Skip to main content
Glama

legal-mcp-croatia

An MCP server that lets an AI assistant read Croatian public legal registers:

  • e-Oglasna ploča sudova — the court noticeboard

  • Sudski registar — the company register, including a change monitor

It is read-only, it stores nothing, and it logs nothing. There is no database, no account, and no service in the middle: the server runs on your machine, talks to public government APIs, and returns the answer. Nothing about what you searched for leaves the process.

That is deliberate. Which companies or people a law firm looks up is the firm's client list, and it is not ours to hold.


Install

git clone https://github.com/AvoccadoTech/legal-mcp-croatia.git
cd legal-mcp-croatia
pip install -e .

Requires Python 3.11 or newer.

Related MCP server: Law7 MCP

Run

The server speaks MCP over stdio. Register it with any MCP-capable client:

{
  "mcpServers": {
    "croatia-legal": {
      "command": "python",
      "args": ["-m", "croatia_legal_mcp"],
      "env": {
        "SUDREG_CLIENT_ID": "...",
        "SUDREG_CLIENT_SECRET": "..."
      }
    }
  }
}

e-Oglasna needs no credentials. Sudski registar does — see below. Without them the e-Oglasna tools work normally and the Sudreg tools return an error saying where to register.

Optional: set EOGLASNA_DOWNLOAD_ROOT to choose where downloaded PDFs go. It defaults to ./eoglasna-downloads, and the server refuses to write outside it.


e-Oglasna ploča sudova

The electronic noticeboard where Croatian courts publish notices they are legally required to make public — enforcement (ovrha), corporate and personal bankruptcy, land-registry proceedings, substitute service, and other court publications. Notices from FINA and from notaries appear on the same board.

Each notice carries the issuing court, the case number, publication and expiry dates, the parties named, and the scanned decision as PDF.

The API is genuinely open: no registration, no API key, no rate-limit documentation. The underlying data is published under the Croatian Otvorena dozvola (Open Licence).

It is a noticeboard, not a case-law database. There is no legislation and no judgments here — for those, see Narodne novine and the Supreme Court's case-law portal.

Tools

Tool

What it does

eoglasna_search_notices

Search by company name, personal name, case number or OIB, optionally within a publication date range

eoglasna_get_notice

One notice in full — every party, every attached document

eoglasna_download_notice_document

Save one attached PDF locally and return its path

eoglasna_list_courts

Every court and body publishing to the board, with codes

eoglasna_search_notices takes a source:

source

Endpoint

Use it for

courts (default)

/court-notice

Everything from courts, with court, case number and case type

all

/notice

Also FINA and notary notices, but without those three fields

company_bankruptcies

/court-notice/legal-person-bankruptcy

Corporate insolvency only — has a client's counterparty gone under?

personal_bankruptcies

/court-notice/natural-person-bankruptcy

Consumer insolvency only

The bankruptcy views are separate endpoints rather than a filter, because caseType is another parameter the API accepts and ignores.

In company_bankruptcies every debtor is a company — the view is filtered on LEGAL_PERSON_BANKRUPTCY — which makes it the least risky view to put on a screen in front of an audience. It is not a guarantee that no individual appears: the other participants are not filtered, and the bankrupt company's zakonski zastupnik shows up as a named natural person with an OIB. Check the page before you project it.

One thing worth knowing before you build your own

The published OpenAPI document describes the search filter as an object, and the API accepts a JSON-encoded filter= parameter and then ignores it. You get HTTP 200, a well-formed page of results, and the entire ~900,000-notice corpus sorted newest-first — for every search term, including nonsense ones.

It looks like it is working. It is not. Spring binds the filter from flat query parameters:

GET /api/v1/court-notice?text=HEP&datePublishedFrom=2026-01-01&page=0

A monitoring tool built on the documented form would report a match for every term it was ever given. There is a regression test in tests/test_smoke.py guarding this.

Personal data

Notices name natural persons and include their OIB and, in the full record, their home address. This is a public register and republication is lawful, but it is still personal data under GDPR.

Two consequences the server enforces or flags:

  • Addresses appear only when you request one specific notice, never in bulk search output.

  • Every notice carries an expiry date. That is the date it comes off the board — the state has decided it should stop being public. If you keep a copy past that date, you are republishing something that was deliberately withdrawn. Build a retention rule.

The expiry date is not a procedural deadline and must never be presented as one. It says when the notice disappears, not when anyone has to do anything.


Sudski registar

Croatia's company register, kept by the commercial courts: every company, branch and institution entered in the court register, with its registered name, legal form, seat, share capital, registry court, and whether a bankruptcy or liquidation is running.

Sole traders (obrt) are not here — they are in the Obrtni registar. Associations are in the Registar udruga. Both are separate registers with separate services.

Credentials

Free, but not open. Register at sudreg-data.gov.hr, confirm the email, and you are issued a Client ID and Client Secret. Set them in the environment of the server:

export SUDREG_CLIENT_ID=...
export SUDREG_CLIENT_SECRET=...

Both values end in two literal dots. They are part of the credential and are easily lost when copying. If the server reports rejected credentials, check the ends of the strings first.

Authentication is OAuth2 client credentials; the token lasts six hours and is cached in the process.

The credentials are issued to a registered user. Before serving several firms through one set, read the terms — a shared key across clients is a question for the register, not an implementation detail.

Tools

Tool

What it does

sudreg_get_subject

One company by OIB or MBS — name, legal form, seat, capital, status, proceedings, accounts filed

sudreg_find_subject

Find companies by registered name, returning MBS and OIB

sudreg_check_watchlist

The monitor. Which of the companies you watch have changed since you last looked

sudreg_list_snapshots

Which daily snapshots the open-data service is serving, and how fresh they are

The monitor, and why it holds no watchlist

Every subject in the register carries scn_zadnje_izmjene — an Oracle system change number, monotonically increasing, returned on every read. That one field is enough to make monitoring a pure function:

you pass the identifiers and the SCNs you last saw, we say which ones moved, you store the new SCNs.

So there is no watchlist here, no seen-ledger, and no state of any kind. Which companies a firm watches is its client list; keeping it on the firm's side is not a limitation of this design, it is the design.

A run starts from the firm's own file, wherever the firm keeps it:

[
  { "identifier": "75399377119", "identifier_type": "oib", "last_seen_scn": 6641203, "label": "Counterparty A" },
  { "identifier": "080000604",   "identifier_type": "mbs", "last_seen_scn": 6640911, "label": "Client B" }
]

Pass those to sudreg_check_watchlist. The report leads with what moved, says what each changed subject looks like now — struck off, in bankruptcy, in liquidation — and ends with a table of SCNs to write back. Omit last_seen_scn on the first run and the report gives you the baseline instead.

Two honest limits, both stated in the output of the tool itself:

  • The register says that a subject changed, not which field. Compare sudreg_get_subject against your own record to see what moved.

  • Open data is copied from the live register at 19:00 on working days. Something filed this morning may not appear until tomorrow, and on a Monday the freshest snapshot is Friday — three days old. Every watchlist report prints the snapshot age on the first line rather than burying it, and pins every lookup in the run to that one snapshot so the whole report describes a single state.

One word of caution about register status. It means entered in the register, not trading and not solvent: a company keeps status 1 for years while in bankruptcy. The output says "registered" rather than "active" for exactly that reason, and prints any running proceeding in bold next to it.

What is not in this register

No officers, no board members, no beneficial owners. Personal data was withdrawn from the open service, so nothing here answers "who can sign for this company". That comes from a court extract (izvadak), or for ownership from the Registar stvarnih vlasnika. The tool descriptions say so, because an assistant that guesses at representation from a company record is worse than no tool at all.

No financial figures. The register records that annual accounts were filed and when — useful on its own — but the numbers are in the Fina RGFI.

One thing worth knowing before you build your own

This is a replication API, not a lookup API. Of its 39 methods, exactly one — /detalji_subjekta — takes an identifier. Everything else (/tvrtke, /sjedista, /temeljni_kapitali, /promjene) is a full table dump with offset and limit and no way to filter by company. It is built for someone mirroring the register nightly.

/promjene looks like the obvious basis for a monitor and is the wrong one for this job: it is the change list for the whole register, meant to be diffed against the copy you already hold. For a firm watching a few dozen counterparties, one /detalji_subjekta call each is smaller, exact, and needs no stored copy.

/subjekti accepts tvrtka_naziv and is the only server-side text filter the public API offers — and it carries identifiers but not the name, which lives in a separate table. So a name search resolves each candidate with a second call.

tvrtka_naziv is a SQL LIKE pattern with no implicit wildcards. Verified live on 2026-08-31:

tvrtka_naziv=KOESTLIN     → 0 rows
tvrtka_naziv=%KOESTLIN%   → the company
tvrtka_naziv=%koestlin%   → the company (matching is case-insensitive)

This is the same class of trap as the e-Oglasna filter, failing the other way. e-Oglasna returns everything and looks like it worked; here you get an empty result that reads as a fact about the world — "no such company" about a company that exists, which is the worse of the two answers to hand a lawyer. The server wraps a bare term in %…% and passes through anything containing %, and there is a live test pinning all three cases.

Two smaller things that produce wrong output rather than no output:

  • naznaka_imena is not a suffix. It is the distinguishing designation and it repeats part of the name — for MBS 010000162 the pair is ("KOESTLIN dioničko društvo,tvornica keksa i vafla", "KOESTLIN"). Concatenating them invents a company name that no register ever issued. The full name is ime alone.

  • postupak is always present, and code 1 reads "Bez postupka" — no proceeding. Rendering it verbatim puts a bankruptcy-shaped alarm on every healthy company. The live code list is 1 Bez postupka, 2 Stečaj, 3 Likvidacija, 4–7 the predstečaj stages.


Not a substitute for a lawyer

These are search tools over public registers. They do not check whether a notice was validly served, whether a deadline runs, whether a filing was correct, or what any of it means. A qualified lawyer decides that.

Tests

pip install -e ".[dev]"
pytest -m "not live"     # offline: query building, identifiers, change detection
pytest -m live           # hits the real APIs; Sudreg tests skip without credentials

Roadmap

  • e-Savjetovanja — draft legislation out for public consultation. Open licence, no key. Warns that a statute is being amended before it is.

  • Narodne novine (official gazette) — ELI metadata is open and keyless; full text is HTML/PDF and the reuse licence is unclear.

Sister servers for other jurisdictions will follow the same shape: one repository per jurisdiction, tools named for the register they query.

Licence

Apache-2.0. Built by Avoccado Tech.

The code is free and always will be. If you would rather someone else kept it working when the state moves an endpoint — as the company register did this year, breaking every tutorial written about it — that is what we do for a living.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides AI agents with access to Moroccan Court of Cassation decisions through search tools and PDF retrieval. Enables both real-time queries against the official portal and local searches across indexed decisions.
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    Provides AI assistants with up-to-date legal documents from official sources, enabling accurate legal information retrieval and analysis.
    18
    -
  • A
    license
    A
    quality
    D
    maintenance
    Connects LLMs to the EcourtsIndia Partner API for searching Indian court cases, retrieving orders, reading cause lists, and accessing AI summaries.
    9
    MIT