Content
# Riigi Teataja MCP Server
An MCP server that wraps [Riigi Teataja](https://www.riigiteataja.ee/) --
Estonia's official State Gazette -- so an AI assistant can look up Estonian
legislation: metadata (title, status, validity dates) and full text.
## What this does and doesn't cover
Riigi Teataja has **no public search API** for legal acts. Every probe
against likely search/catalog endpoints during development returned either
a generic "unknown route" error or nothing at all -- the only real,
documented-by-observation API is fetch-by-id:
- `GET /public-api/api/v1/akt/{id}` -- JSON metadata (Estonian)
- `GET /public-api/api/v1/en/akt/{id}` -- JSON metadata (English translation, if one exists)
- `GET /public-api/api/v1/akt/{id}/blob-html` -- full text (HTML)
So the tools below only work when you already have an act's id or a
riigiteataja.ee link -- they cannot discover acts by topic or keyword.
**Court rulings (`kohtulahendid`/`kohtuteave`) are intentionally not
covered.** A search-shaped endpoint exists for that data
(`/public-api/api/v1/kohtuteave/kohtulahendid/otsing`), but its required
query parameters could not be determined through reasonable probing, and
`robots.txt` explicitly disallows crawling `*/kohtulahendid/*` and
`*/kohtuteave/*` -- almost certainly because rulings can contain personal
data the publisher doesn't want bulk-indexed. That was respected here.
## API quirks worth knowing (found during Phase 0 probing)
- Unknown/malformed act ids return **HTTP 500 with an empty body**, not a
404. The client (`riigiteataja_client.py`) treats both as "not found".
- The same law has **different numeric ids per language** (an Estonian
version and, if translated, an English version, cross-linked via the
`tolkeSeosId` field). Tools try the requested language first and
silently fall back to the other one if not found.
- Full-text blobs can be several MB for large codes (e.g. ~4MB HTML for the
Law of Obligations Act) -- never returned raw. Text is extracted to plain
text, cached per act to avoid re-fetching, and capped at `max_chars`
(default 6000) unless narrowed with `search_term`.
## Tools
- `get_legal_act(query, language="et")` -- metadata for an act by numeric
id or riigiteataja.ee URL (any form: `/akt/`, `/eli/.../consolide`,
`/en/akt/`, `/en/eli/...`).
- `get_legal_act_text(query, language="et", search_term=None, max_chars=6000)`
-- full text, or a focused excerpt around `search_term` if provided.
## Running locally
```bash
python3.12 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python3 test_tools.py # live verification against the real API, must exit 0
uvicorn server:app --host 0.0.0.0 --port 8000
curl http://localhost:8000/health # -> OK
```
The MCP endpoint is `http://localhost:8000/mcp`. No authentication is
required (public, read-only government data).
## Deploying
Not deployed yet. To put this on Railway:
```bash
railway login
railway init # or: railway link, if a project already exists
railway up
```
Railway needs `Procfile` (already present) and will auto-detect Python via
`.python-version` (pinned to 3.12). After the first deploy, in the Railway
UI go to **Settings -> Source -> Connect Environment to Branch -> main** --
otherwise later `git push`es won't redeploy.
Then in Intric (or any MCP client): add server URL
`https://<your-railway-domain>.railway.app/mcp`, no token needed.