Search Austrian federal and state law, court decisions, and the authentic Bundesgesetzblatt (RIS).
One address for any MCP client. No sign-in needed on this server.
8 read1 other
Grouped by tool name. At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Search Austrian consolidated federal & state law, court decisions, and the authentic Bundesgesetzblatt from the official RIS via MCP. Keyless. STDIO or Streamable HTTP.
Public Hosted Server: https://ris-austria.caseyjhand.com/mcp
Austrian law from RIS, the government's official legal information system: federal, state, and municipal law, court decisions, official gazettes back to 1848, draft legislation, and ministerial decrees. Every document is labeled with its binding status. No API key required.
| Tool | Description |
|---|---|
ris_search_legislation | Search consolidated federal, state, and municipal law as in force on a date (default today), plus English translations of selected laws |
ris_search_case_law | Search case law in one court or tribunal per call, across 17 court codes |
ris_search_gazette | Browse the federal, state, district, and municipal gazettes, with federal history auto-routed back to 1848 |
ris_search_drafts | Search ministerial review drafts (Begutachtungsentwürfe) and government bills (Regierungsvorlagen) |
ris_search_announcements | Search seven sectoral collections, from social-insurance notices to ministerial decrees and council-of-ministers minutes |
ris_lookup_citation | Resolve one Austrian legal citation ("§ 6 DSG", "BGBl. I Nr. 165/1999", a Geschäftszahl or ECLI) to its document |
ris_get_document | Fetch one document as markdown, HTML, or XML, or just its rendition URLs, with its binding status |
ris_track_changes | List every document added, changed, or deleted in one application within a date window |
ris_list_reference | Look up RIS vocabularies offline: applications, courts, states, ministries, gazette parts, citation formats, search syntax |
| Resource | Description |
|---|---|
ris://document/{application}/{documentNumber} | Markdown text of one RIS document |
The resource mirrors ris_get_document, so tool-only clients lose nothing.
ris_search_legislation toolquery (full text) or title (title, short title, or abbreviation such as "DSG"). scope is federal (default) or a Bundesland, municipality switches a state scope to municipal law, and language: english searches the ~138 English translationsin_force_as_of defaults to today in Austria and is echoed as appliedInForceAsOf; include_all_versions and the entered_force_* / left_force_* windows are mutually exclusive alternativeslaw_id (filter on it for every section of a law), section_label, abbreviation, keywords, in-force dates, eli, celex_references, and content_urlsris_search_case_law toolcourt is required, one code per call. Filter by query, cited norm ("DSG §1"), case_number, decided_from/decided_to, and decision_type (headnote, full_text, or all, the default). Court-specific filters include court_name (justiz), state (lvwg/uvs), and collection_number (vfgh/vwgh/uvs); one sent to the wrong court fails as court_filter_mismatchcase_numbers (a headnote lists every decision it covers), decision_date, ecli, norms_cited, and guiding_principle on headnotescourt: normenliste returns indexed laws under norm_index instead of decisionsris_search_gazette toolscope is federal (default), a Bundesland, district, or municipal. Filter by number, part, type, published_from/published_to, issuer, district_authority, or municipality; state scopes also take series and state_eraservedApplication; a date range spanning two eras fails as cross_tier_range with the dates to split atbinding (authentic, historical_record, consolidated_informational), authentic_pdf_url where one exists, alex_url (ÖNB scan) for 1848–1940, celex_references where applicable, and promulgation on federal entries from 1848–2003ris_search_drafts toolstage is required: review_drafts (with in_review_on) or government_bills (2004+, with decided_from/decided_to); ministry accepts an abbreviation such as "BMF" or a full designationreview_start and review_deadline or decided, abbreviation and keywords where RIS supplies them, plus materials[] (Erläuterungen, Textgegenüberstellung, Vorblatt/WFA, annexes) classified by type. Pass a materials[].url to ris_get_document to read oneris_search_announcements toolcollection is required: social_insurance, veterinary, court_rules, trade_exam_rules, health_structure_plans, ministerial_decrees, or council_minutes. A filter its collection doesn't accept fails as collection_filter_mismatch, including changed_since on social_insurance and veterinary. issuer expands ministry ("BMF") and social-insurance carrier ("ÖGK") abbreviationsbinding (authentic, administrative_directive, or preparatory), authentic_pdf_url, and document_url, the only browsable view of council minutes and decreescase_numbers, in_force_from/in_force_until, keywords, court_name, plan_state, and session_numberris_lookup_citation toolcitation per call, classified as a norm ("§ 6 DSG"), gazette number ("BGBl. I Nr. 165/1999"), case number ("VfGH G 13/2025") or ECLI, or collection number ("VfSlg 19.632/2012"). kind forces a route, court and state narrow it, and in_force_as_of pins a norm's version (default today)found, kind, and a record in the matching search tool's shape, with alternatives_count when several documents matched. A miss returns found: false with guidance instead of an errorfound: false with its EUR-Lex link, without a RIS request; "BGBl. Nr. 50/2025" without its part comes back ambiguous, naming parts I, II, and IIIris_get_document tooldocument_number + application, or by a document_url from content_urls or a draft's materials[].url; format is markdown (default), html, xml, or urls_onlybinding_status and authentic_pdf_url where one exists. Applications with no text rendition (district and municipal promulgations, court rules, council minutes, 1848–1940 gazettes) return a notice with the usable URL instead of failingkind: outline; re-call with sections to fetch entries. html and xml over 40,000 bytes return kind: link with byte_size and content_urls, so read with markdownris_track_changes toolapplication (any of the 39 codes) plus changed_from/changed_to; include_deleted adds removals, which no other tool reportschanged, published, and binding_status; deletions set deleted: true with deleted_atsection_label, law_id, and in_force_from/in_force_until (no end date is not the same as in force now), and decision rows carry case_numbers and decision_dateris_list_reference tooltopic selects one of 17 static tables: applications, courts, states, decision types and kinds, issuing bodies, ministries, collections, stages, changed-since intervals, section types, gazette parts, law types, district authorities, justiz subject areas, search syntax, and citation formatssummary and entries (value, label, details) with no upstream callris://document/{application}/{documentNumber} resourcetext/markdown of one document, addressed by application + documentNumber from a search or lookup result; same content as ris_get_document with format: markdownBuilt on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
RIS-specific:
YYYY-MM-DD; search tools page with page and page_size (1–100, served as pages of 10 or 20; default 20); omit a text filter rather than sending it blankris_list_referenceAgent-friendly output:
authentic, consolidated_informational, historical_record, decision, preparatory, administrative_directive, translation)totalCount and truncated and echo what the server applied, such as appliedInForceAsOf and servedApplicationcourt_filter_mismatch, cross_tier_range, invalid_query, document_not_found, …) and zero-hit notices name the next callA public instance is available at https://ris-austria.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"ris-austria-mcp-server": {
"type": "streamable-http",
"url": "https://ris-austria.caseyjhand.com/mcp"
}
}
}
Add the server to your MCP client configuration with Bun:
{
"mcpServers": {
"ris-austria-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/ris-austria-mcp-server@latest"]
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"ris-austria-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/ris-austria-mcp-server@latest"]
}
}
}
Or with Docker:
{
"mcpServers": {
"ris-austria-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/ris-austria-mcp-server:latest"
]
}
}
}
Add any configuration variables under an "env" key for bunx/npx, or as additional -e NAME=value arguments for Docker.
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
git clone https://github.com/cyanheads/ris-austria-mcp-server.git
cd ris-austria-mcp-server
bun install
bun run rebuild
{
"mcpServers": {
"ris-austria-mcp-server": {
"type": "stdio",
"command": "node",
"args": ["/absolute/path/to/ris-austria-mcp-server/dist/index.js"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
| Variable | Description | Default |
|---|---|---|
RIS_API_BASE_URL | RIS OGD REST API base URL. | https://data.bka.gv.at/ris/api/v2.6 |
RIS_CONTENT_BASE_URL | Document content host that rendition URLs are built on and documents are fetched from. https://www.ris.bka.gv.at answers automated clients with a bot-check page. | https://ogd.ris.bka.gv.at |
RIS_CONTACT | Contact string appended to the User-Agent. | none |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for the full list of optional overrides.
Build and run:
# One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# or
bun run start:http
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against spec
docker build -t ris-austria-mcp-server .
docker run --rm -p 3010:3010 ris-austria-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/ris-austria-mcp-server. OpenTelemetry peer dependencies are installed by default; build with --build-arg OTEL_ENABLED=false to omit them.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools and resources. |
src/config | Optional RIS endpoint and contact configuration, parsed with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). |
src/mcp-server/resources | Resource definitions (*.resource.ts). |
src/services/ris | RIS request building, HTTP access, normalization, and static reference data. |
docs/design.md | Design notes: tool surface, service spec, live-confirmed RIS API reference. |
tests/ | Unit and integration tests mirroring src/. |
framework-skills/ | Development skills synced from @cyanheads/mcp-ts-core. |
See CLAUDE.md / AGENTS.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagecreateApp() in src/index.tsIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.
RIS OGD data is CC BY 4.0 (attribution: RIS, Bundeskanzleramt Österreich). The underlying legal texts are copyright-free official works. Only the authentic, amtssignierte gazette wording is legally binding — consolidated RIS text is informational.