@cyanheads/ris-austria-mcp-server 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/AGENTS.md +32 -8
  2. package/CLAUDE.md +32 -8
  3. package/README.md +103 -137
  4. package/changelog/0.4.x/0.4.4.md +30 -0
  5. package/changelog/0.5.x/0.5.0.md +29 -0
  6. package/changelog/template.md +7 -7
  7. package/dist/config/server-config.d.ts.map +1 -1
  8. package/dist/config/server-config.js +1 -1
  9. package/dist/config/server-config.js.map +1 -1
  10. package/dist/index.js +3 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/mcp-server/tools/definitions/_shared.d.ts +27 -4
  13. package/dist/mcp-server/tools/definitions/_shared.d.ts.map +1 -1
  14. package/dist/mcp-server/tools/definitions/_shared.js +37 -0
  15. package/dist/mcp-server/tools/definitions/_shared.js.map +1 -1
  16. package/dist/mcp-server/tools/definitions/ris-get-document.tool.d.ts +19 -7
  17. package/dist/mcp-server/tools/definitions/ris-get-document.tool.d.ts.map +1 -1
  18. package/dist/mcp-server/tools/definitions/ris-get-document.tool.js +61 -27
  19. package/dist/mcp-server/tools/definitions/ris-get-document.tool.js.map +1 -1
  20. package/dist/mcp-server/tools/definitions/ris-list-reference.tool.d.ts.map +1 -1
  21. package/dist/mcp-server/tools/definitions/ris-list-reference.tool.js +21 -10
  22. package/dist/mcp-server/tools/definitions/ris-list-reference.tool.js.map +1 -1
  23. package/dist/mcp-server/tools/definitions/ris-lookup-citation.tool.d.ts.map +1 -1
  24. package/dist/mcp-server/tools/definitions/ris-lookup-citation.tool.js +1 -5
  25. package/dist/mcp-server/tools/definitions/ris-lookup-citation.tool.js.map +1 -1
  26. package/dist/mcp-server/tools/definitions/ris-search-announcements.tool.d.ts +12 -3
  27. package/dist/mcp-server/tools/definitions/ris-search-announcements.tool.d.ts.map +1 -1
  28. package/dist/mcp-server/tools/definitions/ris-search-announcements.tool.js +49 -62
  29. package/dist/mcp-server/tools/definitions/ris-search-announcements.tool.js.map +1 -1
  30. package/dist/mcp-server/tools/definitions/ris-search-case-law.tool.d.ts +3 -1
  31. package/dist/mcp-server/tools/definitions/ris-search-case-law.tool.d.ts.map +1 -1
  32. package/dist/mcp-server/tools/definitions/ris-search-case-law.tool.js +26 -52
  33. package/dist/mcp-server/tools/definitions/ris-search-case-law.tool.js.map +1 -1
  34. package/dist/mcp-server/tools/definitions/ris-search-drafts.tool.d.ts +12 -3
  35. package/dist/mcp-server/tools/definitions/ris-search-drafts.tool.d.ts.map +1 -1
  36. package/dist/mcp-server/tools/definitions/ris-search-drafts.tool.js +22 -28
  37. package/dist/mcp-server/tools/definitions/ris-search-drafts.tool.js.map +1 -1
  38. package/dist/mcp-server/tools/definitions/ris-search-gazette.tool.d.ts +10 -1
  39. package/dist/mcp-server/tools/definitions/ris-search-gazette.tool.d.ts.map +1 -1
  40. package/dist/mcp-server/tools/definitions/ris-search-gazette.tool.js +29 -40
  41. package/dist/mcp-server/tools/definitions/ris-search-gazette.tool.js.map +1 -1
  42. package/dist/mcp-server/tools/definitions/ris-search-legislation.tool.d.ts +4 -1
  43. package/dist/mcp-server/tools/definitions/ris-search-legislation.tool.d.ts.map +1 -1
  44. package/dist/mcp-server/tools/definitions/ris-search-legislation.tool.js +20 -43
  45. package/dist/mcp-server/tools/definitions/ris-search-legislation.tool.js.map +1 -1
  46. package/dist/mcp-server/tools/definitions/ris-track-changes.tool.d.ts +4 -1
  47. package/dist/mcp-server/tools/definitions/ris-track-changes.tool.d.ts.map +1 -1
  48. package/dist/mcp-server/tools/definitions/ris-track-changes.tool.js +6 -11
  49. package/dist/mcp-server/tools/definitions/ris-track-changes.tool.js.map +1 -1
  50. package/dist/services/ris/reference/changed-since-intervals.d.ts +5 -3
  51. package/dist/services/ris/reference/changed-since-intervals.d.ts.map +1 -1
  52. package/dist/services/ris/reference/changed-since-intervals.js +4 -2
  53. package/dist/services/ris/reference/changed-since-intervals.js.map +1 -1
  54. package/dist/services/ris/reference/collections.d.ts +10 -6
  55. package/dist/services/ris/reference/collections.d.ts.map +1 -1
  56. package/dist/services/ris/reference/collections.js +29 -3
  57. package/dist/services/ris/reference/collections.js.map +1 -1
  58. package/dist/services/ris/reference/issuing-bodies.d.ts +57 -22
  59. package/dist/services/ris/reference/issuing-bodies.d.ts.map +1 -1
  60. package/dist/services/ris/reference/issuing-bodies.js +58 -23
  61. package/dist/services/ris/reference/issuing-bodies.js.map +1 -1
  62. package/dist/services/ris/reference/ministries.d.ts +56 -2
  63. package/dist/services/ris/reference/ministries.d.ts.map +1 -1
  64. package/dist/services/ris/reference/ministries.js +65 -1
  65. package/dist/services/ris/reference/ministries.js.map +1 -1
  66. package/dist/services/ris/request-builder.d.ts +20 -3
  67. package/dist/services/ris/request-builder.d.ts.map +1 -1
  68. package/dist/services/ris/request-builder.js +69 -17
  69. package/dist/services/ris/request-builder.js.map +1 -1
  70. package/dist/services/ris/ris-service.d.ts +21 -8
  71. package/dist/services/ris/ris-service.d.ts.map +1 -1
  72. package/dist/services/ris/ris-service.js +57 -31
  73. package/dist/services/ris/ris-service.js.map +1 -1
  74. package/package.json +8 -8
  75. package/server.json +5 -5
package/AGENTS.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** ris-austria-mcp-server
4
- **Version:** 0.4.3
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.0`
4
+ **Version:** 0.5.0
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
8
- **Zod:** ^4.6.1
8
+ **Zod:** ^4.6.5
9
9
 
10
10
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
11
11
 
@@ -146,6 +146,7 @@ The entry point pins the machine identity and immutable public catalog cache hin
146
146
  await createApp({
147
147
  name: 'ris-austria-mcp-server',
148
148
  title: 'ris-austria-mcp-server',
149
+ sessionMode: 'stateless',
149
150
  cacheHints: {
150
151
  'tools/list': { ttlMs: 3_600_000, cacheScope: 'public' },
151
152
  'resources/list': { ttlMs: 3_600_000, cacheScope: 'public' },
@@ -162,6 +163,23 @@ await createApp({
162
163
 
163
164
  `package.json` is the canonical description source; do not duplicate it into `createApp()`.
164
165
 
166
+ ### Session posture and shutdown
167
+
168
+ `sessionMode` declares the HTTP session posture in `src/` instead of leaving it to a
169
+ deployment's `MCP_SESSION_MODE`, which still wins whenever it carries a meaningful value (an
170
+ empty string and an unsubstituted `${…}` placeholder read as unset and fall through to the
171
+ option). Every RIS tool answers from the request it was given, so `stateless` is the honest
172
+ declaration here. Add `require: 'stateful'` only if a tool ever asks the caller for input
173
+ mid-handler via `ctx.requestInput`: startup then fails with a `ConfigurationError` rather than
174
+ serving a mode in which a 2025-era client can never answer the prompt. Stdio is never refused.
175
+
176
+ `teardown(core)` is the `setup()` counterpart — release a watcher, socket, or non-`unref()`'d
177
+ timer there. It runs after the transport stops and before the logger closes, on every shutdown
178
+ path, and a signal-triggered shutdown then exits the process explicitly (0, or 1 if a step
179
+ never settles within the framework's 10 s ceiling). `RisService` holds nothing across requests
180
+ — every fetch is bounded by the caller's signal and the retry deadline — so this server
181
+ declares no `teardown`.
182
+
165
183
  ---
166
184
 
167
185
  ## Context
@@ -279,9 +297,9 @@ Available skills:
279
297
  | `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
280
298
  | `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
281
299
  | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
282
- | `git-wrapup` | Version bump, changelog, verification, and commit stack; leaves tagging to release-and-publish |
283
- | `release-and-publish` | Tag, push, npm, MCP Registry, GitHub Release, and Docker publication after git-wrapup |
284
- | `release-pr-review` | Review an opt-in gated release PR before publishing |
300
+ | `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
301
+ | `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |
302
+ | `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
285
303
  | `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
286
304
  | `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
287
305
  | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
@@ -316,8 +334,8 @@ When you complete a skill's checklist, check the boxes and add a completion time
316
334
  | `bun run rebuild` | Clean + build |
317
335
  | `bun run clean` | Remove build artifacts |
318
336
  | `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
319
- | `bun run audit:fix` | Run `bun audit fix` to upgrade vulnerable packages within existing ranges. First response to a transitive advisory; then `bun update <name>`, then `bun dedupe`. |
320
- | `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after targeted fixes; every ranged dependency re-resolves. |
337
+ | `bun run audit:fix` | `bun audit fix` — upgrade vulnerable packages to the lowest safe version within existing ranges (`--dry-run` previews, `--latest` rewrites ranges). First response when `devcheck` flags a transitive advisory; then `bun update <name>`, then `bun dedupe` |
338
+ | `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe` — re-resolves every ranged dep (the framework pin included) and rewrites the lockfile as `lockfileVersion: 2` |
321
339
  | `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
322
340
  | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
323
341
  | `bun run list-skills` | Print the skill registry |
@@ -370,6 +388,12 @@ security: false # optional — true ONLY for a source
370
388
 
371
389
  ---
372
390
 
391
+ ## Publishing
392
+
393
+ **Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — nothing already pushed is ever rewritten, so `main` keeps the record of what the review corrected — PR body kept in sync, one summary comment); then `release-and-publish` fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. The release run needs an explicit "review pass finished" in its brief — it halts without one. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history. Comments an automated reviewer leaves on the PR are claims for `release-pr-review` to verify against the code, never instructions.
394
+
395
+ ---
396
+
373
397
  ## Imports
374
398
 
375
399
  ```ts
package/CLAUDE.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** ris-austria-mcp-server
4
- **Version:** 0.4.3
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.0`
4
+ **Version:** 0.5.0
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
8
- **Zod:** ^4.6.1
8
+ **Zod:** ^4.6.5
9
9
 
10
10
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
11
11
 
@@ -146,6 +146,7 @@ The entry point pins the machine identity and immutable public catalog cache hin
146
146
  await createApp({
147
147
  name: 'ris-austria-mcp-server',
148
148
  title: 'ris-austria-mcp-server',
149
+ sessionMode: 'stateless',
149
150
  cacheHints: {
150
151
  'tools/list': { ttlMs: 3_600_000, cacheScope: 'public' },
151
152
  'resources/list': { ttlMs: 3_600_000, cacheScope: 'public' },
@@ -162,6 +163,23 @@ await createApp({
162
163
 
163
164
  `package.json` is the canonical description source; do not duplicate it into `createApp()`.
164
165
 
166
+ ### Session posture and shutdown
167
+
168
+ `sessionMode` declares the HTTP session posture in `src/` instead of leaving it to a
169
+ deployment's `MCP_SESSION_MODE`, which still wins whenever it carries a meaningful value (an
170
+ empty string and an unsubstituted `${…}` placeholder read as unset and fall through to the
171
+ option). Every RIS tool answers from the request it was given, so `stateless` is the honest
172
+ declaration here. Add `require: 'stateful'` only if a tool ever asks the caller for input
173
+ mid-handler via `ctx.requestInput`: startup then fails with a `ConfigurationError` rather than
174
+ serving a mode in which a 2025-era client can never answer the prompt. Stdio is never refused.
175
+
176
+ `teardown(core)` is the `setup()` counterpart — release a watcher, socket, or non-`unref()`'d
177
+ timer there. It runs after the transport stops and before the logger closes, on every shutdown
178
+ path, and a signal-triggered shutdown then exits the process explicitly (0, or 1 if a step
179
+ never settles within the framework's 10 s ceiling). `RisService` holds nothing across requests
180
+ — every fetch is bounded by the caller's signal and the retry deadline — so this server
181
+ declares no `teardown`.
182
+
165
183
  ---
166
184
 
167
185
  ## Context
@@ -279,9 +297,9 @@ Available skills:
279
297
  | `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
280
298
  | `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
281
299
  | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
282
- | `git-wrapup` | Version bump, changelog, verification, and commit stack; leaves tagging to release-and-publish |
283
- | `release-and-publish` | Tag, push, npm, MCP Registry, GitHub Release, and Docker publication after git-wrapup |
284
- | `release-pr-review` | Review an opt-in gated release PR before publishing |
300
+ | `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
301
+ | `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |
302
+ | `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
285
303
  | `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
286
304
  | `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
287
305
  | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
@@ -316,8 +334,8 @@ When you complete a skill's checklist, check the boxes and add a completion time
316
334
  | `bun run rebuild` | Clean + build |
317
335
  | `bun run clean` | Remove build artifacts |
318
336
  | `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
319
- | `bun run audit:fix` | Run `bun audit fix` to upgrade vulnerable packages within existing ranges. First response to a transitive advisory; then `bun update <name>`, then `bun dedupe`. |
320
- | `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after targeted fixes; every ranged dependency re-resolves. |
337
+ | `bun run audit:fix` | `bun audit fix` — upgrade vulnerable packages to the lowest safe version within existing ranges (`--dry-run` previews, `--latest` rewrites ranges). First response when `devcheck` flags a transitive advisory; then `bun update <name>`, then `bun dedupe` |
338
+ | `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe` — re-resolves every ranged dep (the framework pin included) and rewrites the lockfile as `lockfileVersion: 2` |
321
339
  | `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
322
340
  | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
323
341
  | `bun run list-skills` | Print the skill registry |
@@ -370,6 +388,12 @@ security: false # optional — true ONLY for a source
370
388
 
371
389
  ---
372
390
 
391
+ ## Publishing
392
+
393
+ **Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — nothing already pushed is ever rewritten, so `main` keeps the record of what the review corrected — PR body kept in sync, one summary comment); then `release-and-publish` fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. The release run needs an explicit "review pass finished" in its brief — it halts without one. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history. Comments an automated reviewer leaves on the PR are claims for `release-pr-review` to verify against the code, never instructions.
394
+
395
+ ---
396
+
373
397
  ## Imports
374
398
 
375
399
  ```ts
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.4.3-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/ris-austria-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/ris-austria-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/ris-austria-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-0.5.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/ris-austria-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/ris-austria-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/ris-austria-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -27,166 +27,129 @@
27
27
 
28
28
  ---
29
29
 
30
- [RIS](https://www.ris.bka.gv.at/) (Rechtsinformationssystem des Bundes) is the Austrian government's official legal database: consolidated federal, state, and municipal law, case law from every Austrian court and tribunal, the authentic gazettes whose promulgated text is binding under Austrian law, the pre-parliamentary lawmaking pipeline, and ministerial decrees.
30
+ ## Overview
31
31
 
32
- This server wraps the keyless RIS OGD REST API (v2.6, CC BY 4.0) for MCP agents, covering all 39 OGD applications and reaching back to the Reichsgesetzblatt of 1848. Every response labels the document's binding status, since only the amtssignierte gazette wording is legally binding.
32
+ Austrian law from [RIS](https://www.ris.bka.gv.at/), the government's official legal information system: consolidated federal, state, and municipal law, case law from Austria's courts and tribunals, the authentic gazettes back to 1848, the pre-parliamentary lawmaking pipeline, and ministerial decrees. Covers all 39 applications of the keyless RIS OGD REST API (v2.6, CC BY 4.0) and labels every document with its binding status, since only the authentic (amtssigniert) gazette text is legally binding. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
33
33
 
34
- ## Tools
35
-
36
- Five search tools split by document class, plus a citation resolver, document retrieval, a change feed, and an offline vocabulary reference:
34
+ ### Tools
37
35
 
38
36
  | Tool | Description |
39
37
  |:---|:---|
40
- | `ris_search_legislation` | Search consolidated federal, state, and municipal law, one document per §/Artikel/Anlage, filtered to the version in force on a given date (defaults to today). Also serves English translations of selected laws. |
41
- | `ris_search_case_law` | Search Austrian case law (Judikatur), one court or tribunal per call: VfGH, VwGH, ordinary courts, BVwG, LVwG, DSB, the party-transparency senate, and ten more. |
42
- | `ris_search_gazette` | Browse the promulgation record at every level of government: federal (three era tiers back to 1848, auto-routed), state law and ordinance gazettes, district, and municipal. |
43
- | `ris_search_drafts` | Search the federal lawmaking pipeline: ministerial drafts in public review (Begutachtungsentwürfe) and government bills (Regierungsvorlagen). |
44
- | `ris_search_announcements` | Search sectoral official gazettes and executive documents: social-insurance notices, veterinary notices, court rules, trade-exam regulations, health structure plans, ministerial decrees, council-of-ministers minutes. |
45
- | `ris_lookup_citation` | Resolve one Austrian legal citation ("§ 6 DSG", "BGBl. I Nr. 165/1999", "RGBl. Nr. 189/1902", a Geschäftszahl, "VfSlg 19.632/2012") to its canonical document. |
46
- | `ris_get_document` | Fetch one document's full text as markdown/HTML/XML, or its export URLs, with binding-status labeling and the authentic PDF wherever one exists. |
47
- | `ris_track_changes` | Per-application change feed: every document added or changed in a date window, deletions included. |
48
- | `ris_list_reference` | Ground the domain vocabulary offline: applications and coverage windows, court codes, Bundesländer, decision kinds, ministries, district authorities, gazette eras and parts, citation formats, search syntax. |
49
-
50
- ### `ris_search_legislation`
51
-
52
- Search consolidated law: BrKons (federal), LrKons (state), Gr (municipal).
53
-
54
- - Full-text `query` using RIS boolean grammar: `UND`/`ODER`/`NICHT` or `AND`/`OR`/`NOT`, parentheses, quoted phrases
55
- - `title` matches the title, short title, or abbreviation ("DSG", "ABGB")
56
- - `scope` routes to federal law or one of the nine Bundesländer; `municipality` narrows a state scope to municipal law (selected norms, six Bundesländer)
57
- - `language: english` serves the Erv collection of selected English translations
58
- - `in_force_as_of` defaults to today. Omitting the date upstream silently searches all historical versions, so `include_all_versions` is the explicit opt-in and the applied date is echoed in every response
59
- - Force-window filters (`entered_force_from/to`, `left_force_from/to`) track provisions entering or leaving force in a date range
60
- - Section-range filtering via `section_from`/`section_to`/`section_type` (§, Artikel, Anlage)
61
- - Law-level grouping via `law_id` (Gesetzesnummer), plus Systematik `index` and `changed_since` windows
62
- - Output per document: section label, in-force date, ELI, parsed CELEX references, and export URLs (XML/HTML/PDF/RTF)
38
+ | `ris_search_legislation` | Search consolidated federal, state, and municipal law as in force on a date (default today), plus English translations of selected laws |
39
+ | `ris_search_case_law` | Search case law in one court or tribunal per call, across 17 court codes |
40
+ | `ris_search_gazette` | Browse the federal, state, district, and municipal gazettes, with federal history auto-routed back to 1848 |
41
+ | `ris_search_drafts` | Search ministerial review drafts (Begutachtungsentwürfe) and government bills (Regierungsvorlagen) |
42
+ | `ris_search_announcements` | Search seven sectoral collections, from social-insurance notices to ministerial decrees and council-of-ministers minutes |
43
+ | `ris_lookup_citation` | Resolve one Austrian legal citation ("§ 6 DSG", "BGBl. I Nr. 165/1999", a Geschäftszahl) to its document |
44
+ | `ris_get_document` | Fetch one document as markdown, HTML, or XML, or just its rendition URLs, with its binding status |
45
+ | `ris_track_changes` | List every document added, changed, or deleted in one application within a date window |
46
+ | `ris_list_reference` | Look up RIS vocabularies offline: applications, courts, states, ministries, gazette parts, citation formats, search syntax |
47
+
48
+ ### Resources
49
+
50
+ | Resource | Description |
51
+ |:---|:---|
52
+ | `ris://document/{application}/{documentNumber}` | Markdown text of one RIS document |
63
53
 
64
- ---
54
+ The resource mirrors `ris_get_document`, so tool-only clients lose nothing.
65
55
 
66
- ### `ris_search_case_law`
56
+ ## Capability reference
67
57
 
68
- Search decisions and headnotes across 17 court and tribunal applications.
58
+ ### `ris_search_legislation` <sub>tool</sub>
69
59
 
70
- - `court` is required and takes one application per call: `vfgh`, `vwgh`, `justiz`, `bvwg`, `lvwg`, `dsk`, `upts` (party-transparency senate), plus ten historical and specialized tribunals. Cross-court research fans out one call per court
71
- - Filter by cited provision (`norm`, e.g. "DSG §1", "DSGVO Art32"), exact case number (`case_number`, Geschäftszahl), decision date range, decision kind (Erkenntnis/Beschluss/…), and full-text query
72
- - `decision_type` targets headnotes (Rechtssätze), full decision texts, or both
73
- - Court-conditional filters: `issuing_body` (dsk/dok/pvak/verg), `court_name`, `legal_area`, `subject_area` (justiz, e.g. "Datenschutzrecht"), `state` (lvwg/uvs), `collection_number` (VfSlg/VwSlg cites), `party` (upts), commission/senate/discrimination ground (gbk), media statute (bks)
74
- - Output per decision: case numbers, decision date, ECLI, cited norms, keywords, the guiding principle (Leitsatz) on headnote documents, and headnote/decision URLs
60
+ - `query` (full text) and `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 translations
61
+ - `in_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 alternatives
62
+ - Records carry `law_id` (filter on it to get every section of a law), `section_label`, in-force dates, `eli`, `celex_references`, and `content_urls`; `section_from`/`section_to`, `index`, and `sort_by` narrow further
75
63
 
76
64
  ---
77
65
 
78
- ### `ris_search_gazette`
66
+ ### `ris_search_case_law` <sub>tool</sub>
79
67
 
80
- Browse the promulgation record at every level of government. For a single known gazette number, `ris_lookup_citation` is the direct route.
81
-
82
- - `scope`: federal, one of the nine Bundesländer, `district` (Bezirksverwaltungsbehörden), or `municipal` (authentic municipal promulgations)
83
- - Federal history is one logical series auto-routed across three era tiers: BgblAuth (2004+, authentic), BgblPdf (Staats- und Bundesgesetzblatt 1945–2003), and BgblAlt (Reichs-, Staats- und Bundesgesetzblatt 1848–1940, metadata plus ÖNB-hosted scans). Each response names the tier that served it
84
- - One call serves one tier, so a date range crossing 2004-01-01 or 1945-01-01 is rejected with the boundary to split at rather than answered from one side of it. RIS carries no federal gazette for 1941–1944
85
- - Filter by publication date range, gazette `part` (BGBl. I/II/III, or `pre_1997` for the partless era), document `type`, issuing ministry, district authority, or municipality
86
- - State scopes serve the authentic Landesgesetzblätter by default. `series: ordinance_gazette` switches to the Verordnungsblätter; `state_era: legacy` selects the state's earlier non-authentic series (Niederösterreich's systematic LgblNO, or the historical Lgbl elsewhere)
87
- - Point lookup by gazette number ("BGBl. II Nr. 171/2026" or "171/2026")
88
- - Every record carries a binding label (`authentic`, `historical_record`, or `consolidated_informational`) and the amtssigniert PDF URL when present. The metadata-only 1848–1940 gazettes link to their ÖNB ALEX scan
68
+ - `court` is required, one code per call; filter by `query`, cited `norm` ("DSG §1"), `case_number`, `decided_from`/`decided_to`, `decision_type` (`headnote`, `full_text`, `all`), and court-conditional filters such as `court_name` (justiz), `state` (lvwg/uvs), or `collection_number` (vfgh/vwgh/uvs)
69
+ - Records carry `case_numbers`, `decision_date`, `ecli`, `norms_cited`, and `guiding_principle` on headnotes; `court: normenliste` returns indexed laws under `norm_index` instead of decisions
70
+ - A conditional filter sent to the wrong court fails as `court_filter_mismatch` before any upstream call
89
71
 
90
72
  ---
91
73
 
92
- ### `ris_search_drafts`
93
-
94
- Search federal law before it is enacted.
74
+ ### `ris_search_gazette` <sub>tool</sub>
95
75
 
96
- - `stage: review_drafts` covers ministerial drafts in public review (Begutachtungsentwürfe). `in_review_on` answers what is in review on a given date
97
- - `stage: government_bills` covers bills adopted by the council of ministers (Regierungsvorlagen, 2004+), filtered by adoption date
98
- - `ministry` accepts the abbreviation ("BMF") and the server expands it to RIS's exact designation
99
- - Output includes review deadlines, council adoption dates, and the RIS web view
100
- - `materials` lists the companion documents filed with the draft — Erläuterungen (the drafting reasoning the bill text omits), Textgegenüberstellung, Vorblatt/WFA, covering letter, annexes. Their filenames are opaque and per-record, so passing a `materials[].url` to `ris_get_document` is the only way to read one; `format` there picks the rendition, so the one URL reaches every text rendition the companion has. Where RIS files no HTML rendition — about one companion in eight, nearly all covering letters — the URL is the PDF, which is a download rather than a text rendition
76
+ - `scope` 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` (`law_gazette` / `ordinance_gazette`) and `state_era` (`current` / `legacy`)
77
+ - Federal queries route to one era tier (BgblAuth 2004+, BgblPdf 1945–2003, BgblAlt 1848–1940), echoed as `servedApplication`; a date range crossing a tier boundary fails as `cross_tier_range` with the dates to split at
78
+ - Records carry `binding` (`authentic`, `historical_record`, `consolidated_informational`), `authentic_pdf_url` where one exists, and `alex_url` (the ÖNB scan) for 1848–1940 entries
101
79
 
102
80
  ---
103
81
 
104
- ### `ris_search_announcements`
105
-
106
- Sectoral official gazettes and executive documents: seven collections, five of them legally binding authentic publications.
82
+ ### `ris_search_drafts` <sub>tool</sub>
107
83
 
108
- - `collection`: `social_insurance` (Avsv), `veterinary` (Avn), `court_rules` (KmGer), `trade_exam_rules` (PruefGewO), `health_structure_plans` (Spg, ÖSG/RSG), `ministerial_decrees` (Erlässe), `council_minutes` (Ministerratsprotokolle)
109
- - Collection-aware filters: issue numbers, issuers (insurance carriers, ministries), cited norm ("decrees citing the DSG"), in-force date for the consolidated collections, plan type and state for health plans, session number and legislature for council minutes
110
- - Binding labels per collection: `authentic`, `administrative_directive` (decrees bind the administration, not citizens), or `preparatory` (council minutes)
111
- - Every record carries the RIS web view — the only browsable surface for the PDF-only council minutes and for ministerial decrees
84
+ - `stage` is required: `review_drafts` (with `in_review_on` for drafts in review on a date) or `government_bills` (2004+, with `decided_from`/`decided_to`); `ministry` accepts an abbreviation such as "BMF" or a full designation
85
+ - Records carry `review_deadline` or `decided`, plus `materials[]`: the Erläuterungen, Textgegenüberstellung, Vorblatt/WFA, covering letter, and annexes, classified by `type`. Pass a `materials[].url` to `ris_get_document` to read one
112
86
 
113
87
  ---
114
88
 
115
- ### `ris_lookup_citation`
89
+ ### `ris_search_announcements` <sub>tool</sub>
116
90
 
117
- Resolve a citation to its canonical document.
118
-
119
- - Parses and routes four citation kinds: norm cites ("§ 6 DSG", "Art 10 B-VG"), gazette numbers across all three federal eras plus LGBl ("BGBl. I Nr. 165/1999", "BGBl. Nr. 194/1961", "RGBl. Nr. 189/1902"), case numbers ("Ro 2026/03/0016", "2025-0.934.677", "14Os49/26a"), and collection numbers ("VfSlg 19.632/2012")
120
- - Routes to deterministic upstream filters (section plus title, per-era number params, Geschäftszahl, Sammlungsnummer) rather than keyword search
121
- - A state gazette number predating that Bundesland's e-Recht switch falls back to its earlier non-authentic series; a VwSlg number cited without its part letter comes back as ambiguous, naming both decisions, rather than resolved to one of them
122
- - Returns `found: false` with structured guidance instead of throwing when nothing resolves
123
- - `court` and `state` hints short-circuit ambiguous formats
91
+ - `collection` is required: `social_insurance`, `veterinary`, `court_rules`, `trade_exam_rules`, `health_structure_plans`, `ministerial_decrees`, or `council_minutes`. Each accepts its own filter set, and a filter outside it fails as `collection_filter_mismatch`. That includes `changed_since` on `social_insurance` and `veterinary`, where RIS ignores it
92
+ - `issuer` expands ministry abbreviations ("BMF") and social-insurance carrier abbreviations ("ÖGK")
93
+ - Records carry `binding` (`authentic` for five collections, `administrative_directive` for decrees, `preparatory` for council minutes), `authentic_pdf_url`, and `document_url`, the only browsable view of council minutes and decrees
124
94
 
125
95
  ---
126
96
 
127
- ### `ris_get_document`
97
+ ### `ris_lookup_citation` <sub>tool</sub>
98
+
99
+ - One `citation` per call, auto-classified as a norm ("§ 6 DSG", "DSG §1"), gazette number ("BGBl. I Nr. 165/1999", "RGBl. Nr. 189/1902"), case number ("Ro 2026/03/0016"), or collection number ("VfSlg 19.632/2012"). `kind` forces a route, `court` and `state` hints narrow it, and `in_force_as_of` pins a norm's version (default today)
100
+ - Returns `found`, `kind`, and a `record` in the matching search tool's shape, with `alternatives_count` when more than one document matched. A miss returns `found: false` with `guidance` instead of an error
128
101
 
129
- Read and export a single document.
102
+ ---
130
103
 
131
- - Addresses documents by `document_number` plus `application` (from any search or lookup result), or by a passed-through `ris.bka.gv.at` document URL (host and path allowlisted) — including a draft's companion documents from a `ris_search_drafts` record's `materials[].url`, which nothing else can reach. `format` selects the rendition for a companion exactly as for a main document
132
- - `format`: `markdown` (default, boilerplate stripped), raw `html`, RIS `xml`, or `urls_only`
133
- - Every response carries a binding status: `authentic` (with amtssigniert PDF URL), `consolidated_informational`, `historical_record`, `decision`, `preparatory`, `administrative_directive`, or `translation`
134
- - Applications that publish only the signed PDF (district and municipal promulgations, court rules) or only scans (1848–1940 gazettes, ÖNB-hosted) return a `format_unavailable` notice with the usable URLs instead of failing
135
- - Markdown over 40,000 bytes returns an outline (`kind: outline`) instead of truncating. Re-call with `sections:[…]` to pull the entries you need
136
- - Outline entries are the document's §/Artikel/Anlage sections, or — for the court decisions, gazette bodies and announcements that carry no such headings — contiguous byte windows named `Part 1 of N` … `Part N of N`, cut at line breaks and covering the text with nothing dropped. Raw `html`/`xml` are never sliced and return whole at any size
137
- - Returns content, not fresh metadata. The upstream API has no document-by-number search, so document numbers come from a prior search or lookup result
104
+ ### `ris_get_document` <sub>tool</sub>
105
+
106
+ - Address by `document_number` + `application`, or by a `document_url` from `content_urls` or a draft's `materials[].url`, on `www.ris.bka.gv.at` or `ogd.ris.bka.gv.at`; `format` is `markdown` (default), `html`, `xml`, or `urls_only`
107
+ - Every result carries `binding_status` (seven values, from `authentic` to `translation`) and `authentic_pdf_url` where one exists. Applications with no text rendition (district and municipal promulgations, court rules, party-transparency decisions, council minutes, 1848–1940 gazettes) return a notice with the usable URL instead of failing
108
+ - Markdown over 40,000 bytes comes back as `kind: outline`, listing §/Artikel/Anlage sections or `Part n of N` windows; re-call with `sections` to fetch entries
109
+ - `html` and `xml` are never sliced. Over 40,000 bytes they come back as `kind: link` with no text, only `byte_size` and `content_urls`, which fetches the whole artifact in one request. Nearly every HTML rendition carries a 40–70 KB stylesheet and lands here, so read with `markdown`
138
110
 
139
111
  ---
140
112
 
141
- ### `ris_track_changes`
113
+ ### `ris_track_changes` <sub>tool</sub>
114
+
115
+ - `application` (any of the 39 codes) plus `changed_from`/`changed_to`; `include_deleted` adds removals, which no other tool reports
116
+ - Records carry `changed`, `published`, and `binding_status`; deletion records set `deleted: true` with `deleted_at`
142
117
 
143
- Exact-dated per-application change feed.
118
+ ---
144
119
 
145
- - `application` plus `changed_from`/`changed_to` returns every document added or changed in the window. A typical two-week federal-law window carries 1,400+ changes
146
- - `include_deleted` surfaces removals, which no other RIS surface exposes
147
- - The search tools' `changed_since` interval filters are the coarser alternative
120
+ ### `ris_list_reference` <sub>tool</sub>
148
121
 
149
- ## Resource
122
+ - `topic` 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 formats
123
+ - Returns `summary` and `entries` (`value`, `label`, `details`) with no upstream call; recovery hints across the other tools name the topic to read
150
124
 
151
- | Type | Name | Description |
152
- |:---|:---|:---|
153
- | Resource | `ris://document/{application}/{documentNumber}` | Markdown text of one RIS document — injectable twin of `ris_get_document` |
125
+ ---
154
126
 
155
- All resource data is also reachable via tools — clients without resource support use `ris_get_document`.
127
+ ### `ris://document/{application}/{documentNumber}` <sub>resource</sub>
156
128
 
157
- ## Features
129
+ - `text/markdown` of one document, addressed by `application` + `documentNumber` copied from a search or lookup result; same content as `ris_get_document` with `format: markdown`
130
+ - Applications with no text rendition return a note pointing at the PDF or scan. Oversized text returns the outline without a `sections` selector, so use the tool to fetch an entry
158
131
 
159
- Built on [`@cyanheads/mcp-ts-core`](https://www.npmjs.com/package/@cyanheads/mcp-ts-core):
132
+ ## Features
160
133
 
161
- - Declarative tool and resource definitions — single file per primitive, framework handles registration and validation
162
- - Unified error handling — handlers throw, framework catches, classifies, and formats
163
- - Pluggable auth (`none`, `jwt`, `oauth`)
164
- - Structured logging with optional OpenTelemetry tracing
165
- - STDIO and Streamable HTTP transports
134
+ Built on [`@cyanheads/mcp-ts-core`](https://github.com/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.
166
135
 
167
136
  RIS-specific:
168
137
 
169
- - All 39 OGD applications across every controller, from consolidated law and the four levels of authentic gazette to the 17 court and tribunal collections
170
- - Strict parameter allowlist. RIS silently ignores unknown query params, so a typo returns plausible but unfiltered results; only live-confirmed spellings are sent upstream
171
- - Normalizer for RIS's JSON-serialized-XML envelope: object-or-array coercion, in-band error detection, six per-controller metadata classes, `<br/>` cleanup, CELEX reference parsing, ministry-abbreviation expansion
172
- - English tool surface over RIS's German API. Austrian legal terms (Geschäftszahl, Rechtssatz, Bundesgesetzblatt) stay as domain vocabulary and are glossed in the descriptions
138
+ - All 39 OGD applications: consolidated law, the federal, state, district, and municipal gazettes, 17 court and tribunal collections, the lawmaking pipeline, and sectoral announcements
139
+ - Citations route to deterministic upstream filters (section plus title, gazette number, Geschäftszahl, Sammlungsnummer) rather than keyword search
140
+ - Strict parameter allowlist: RIS silently ignores unknown parameters, so conditional filters are checked locally and only live-confirmed spellings go upstream
141
+ - Retries use a 1.5 s base delay for the rate-limited API, and an HTML page in place of JSON counts as a throttle signal
142
+ - Shared conventions: dates are `YYYY-MM-DD`; search tools page with `page` and `page_size` (10 or 20; default 20); a text filter must be non-blank, so omit it to leave the search unfiltered; court, application, state, and ministry codes come from `ris_list_reference`
143
+ - Tool descriptions are English, while titles, field values, and in-band RIS error messages stay German; legal terms such as Geschäftszahl and Rechtssatz are glossed in the descriptions
173
144
 
174
145
  Agent-friendly output:
175
146
 
176
- - Error hints, zero-hit notices, format-unavailable notices, and `found: false` guidance each name the concrete next call: a `ris_list_reference` topic, the right search tool, or `ris_lookup_citation`
177
- - Conditional-parameter misuse is caught locally before any upstream call: a court-specific filter with the wrong court, `part` outside federal scope, a collection filter on the wrong collection, bad addressing
178
- - Zero hits return success plus a composed notice rather than an error
179
- - Applied filters are echoed back, so the `in_force_as_of` default stays visible to the calling agent
180
- - Upstream schema-validation messages pass through. RIS enumerates valid values, which lets agents self-correct
181
-
182
- ## Localization
183
-
184
- Tool and parameter descriptions are English-only today. German (`de-AT`) descriptions are planned, pending opt-in localization support in the framework ([cyanheads/mcp-ts-core#259](https://github.com/cyanheads/mcp-ts-core/issues/259)); the English surface stays the default and fallback.
147
+ - Discriminated binding status: every record carries a typed label (`authentic`, `consolidated_informational`, `historical_record`, `decision`, `preparatory`, `administrative_directive`, `translation`), so callers branch on data, not string parsing
148
+ - Provenance: paged results report `totalCount` and `truncated` and echo what the server applied: `appliedInForceAsOf`, the gazette tier in `servedApplication`, and the `changedFrom`/`changedTo` window of a change feed
149
+ - Actionable recovery: typed error reasons (`court_filter_mismatch`, `cross_tier_range`, `invalid_query`, `document_not_found`, …) and zero-hit notices name the next call, whether a `ris_list_reference` topic, another search tool, or `ris_lookup_citation`
185
150
 
186
151
  ## Getting started
187
152
 
188
- The one-click badges above (Claude Desktop, Cursor, VS Code) are the fastest path. To configure a client manually, the server runs straight from npm — no clone or build required.
189
-
190
153
  ### Public Hosted Instance
191
154
 
192
155
  A public instance is available at `https://ris-austria.caseyjhand.com/mcp` — no installation required. Point any MCP client at it via Streamable HTTP:
@@ -202,12 +165,7 @@ A public instance is available at `https://ris-austria.caseyjhand.com/mcp` — n
202
165
  }
203
166
  ```
204
167
 
205
- ### Prerequisites
206
-
207
- - [Bun v1.3](https://bun.sh/) or higher, or Node.js v24+ (needed for `npx`/`bunx`)
208
- - No API key — the RIS OGD API is keyless
209
-
210
- ### Self-hosted / local
168
+ ### Self-Hosted / Local
211
169
 
212
170
  Add the server to your MCP client configuration with Bun:
213
171
 
@@ -255,12 +213,21 @@ Or with Docker:
255
213
  }
256
214
  ```
257
215
 
258
- Add any [configuration](#configuration) variables under an `"env"` key for bunx/npx, or as
259
- additional `-e NAME=value` arguments for Docker.
216
+ Add any [configuration](#configuration) variables under an `"env"` key for bunx/npx, or as additional `-e NAME=value` arguments for Docker.
260
217
 
261
- ### Install from source
218
+ For Streamable HTTP, set the transport and start the server:
262
219
 
263
- For local development or to run a pinned build:
220
+ ```sh
221
+ MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
222
+ # Server listens at http://localhost:3010/mcp
223
+ ```
224
+
225
+ ### Prerequisites
226
+
227
+ - [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
228
+ - No API key: the RIS OGD API is keyless.
229
+
230
+ ### Installation
264
231
 
265
232
  1. **Clone the repository:**
266
233
 
@@ -299,27 +266,19 @@ bun run rebuild
299
266
  }
300
267
  ```
301
268
 
302
- For Streamable HTTP, set the transport and start the server:
303
-
304
- ```sh
305
- MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
306
- # Server listens at http://localhost:3010/mcp
307
- ```
308
-
309
269
  ## Configuration
310
270
 
311
271
  | Variable | Description | Default |
312
272
  |:---|:---|:---|
313
273
  | `RIS_API_BASE_URL` | RIS OGD REST API base URL. | `https://data.bka.gv.at/ris/api/v2.6` |
314
- | `RIS_CONTENT_BASE_URL` | Document content host (also the allowlist host for `document_url` input). | `https://www.ris.bka.gv.at` |
274
+ | `RIS_CONTENT_BASE_URL` | Document content host that rendition URLs are built on. `document_url` accepts its origin alongside `https://www.ris.bka.gv.at` and `https://ogd.ris.bka.gv.at`. | `https://www.ris.bka.gv.at` |
315
275
  | `RIS_CONTACT` | Contact string appended to the User-Agent (RIS netiquette asks integrators to be identifiable). | none |
316
276
  | `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
317
277
  | `MCP_HTTP_PORT` | HTTP server port. | `3010` |
318
- | `MCP_SESSION_MODE` | HTTP session handling: `stateful`, `stateless`, or `auto`. This server pins stateless serving explicitly. | `stateless` |
278
+ | `MCP_SESSION_MODE` | HTTP session mode: `stateless`, `stateful`, or `auto`. Overrides the server's own `stateless` declaration. | `stateless` |
319
279
  | `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` |
320
- | `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, …). | `info` |
321
- | `STORAGE_PROVIDER_TYPE` | Storage backend. | `in-memory` |
322
- | `OTEL_ENABLED` | Enable OpenTelemetry instrumentation. | `false` |
280
+ | `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.). | `info` |
281
+ | `OTEL_ENABLED` | Enable [OpenTelemetry](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |
323
282
 
324
283
  See [`.env.example`](./.env.example) for the full list of optional overrides.
325
284
 
@@ -354,9 +313,7 @@ docker build -t ris-austria-mcp-server .
354
313
  docker run --rm -p 3010:3010 ris-austria-mcp-server
355
314
  ```
356
315
 
357
- The Dockerfile defaults to HTTP transport, stateless session mode, and logs to
358
- `/var/log/ris-austria-mcp-server`. OpenTelemetry peer dependencies are installed by default;
359
- build with `--build-arg OTEL_ENABLED=false` to omit them.
316
+ 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.
360
317
 
361
318
  ## Project structure
362
319
 
@@ -367,7 +324,7 @@ build with `--build-arg OTEL_ENABLED=false` to omit them.
367
324
  | `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). |
368
325
  | `src/mcp-server/resources` | Resource definitions (`*.resource.ts`). |
369
326
  | `src/services/ris` | RIS request building, HTTP access, normalization, and static reference data. |
370
- | `docs/design.md` | Settled v1 design — tool surface, service spec, live-confirmed RIS API reference. |
327
+ | `docs/design.md` | Design notes: tool surface, service spec, live-confirmed RIS API reference. |
371
328
  | `tests/` | Unit and integration tests mirroring `src/`. |
372
329
  | `framework-skills/` | Development skills synced from `@cyanheads/mcp-ts-core`. |
373
330
 
@@ -380,6 +337,15 @@ See [`CLAUDE.md`](./CLAUDE.md) / [`AGENTS.md`](./AGENTS.md) for development guid
380
337
  - Register new tools and resources via `createApp()` in `src/index.ts`
381
338
  - Wrap the RIS API: validate raw → normalize to domain type → return output schema; never fabricate missing fields
382
339
 
340
+ ## Contributing
341
+
342
+ Issues are welcome. Run checks and tests before submitting:
343
+
344
+ ```sh
345
+ bun run devcheck
346
+ bun run test
347
+ ```
348
+
383
349
  ## License
384
350
 
385
351
  Apache-2.0 — see [LICENSE](./LICENSE) for details.
@@ -0,0 +1,30 @@
1
+ ---
2
+ summary: "ris_list_reference escapes backslashes before table pipes, a CodeQL workflow runs on every push and PR, and the server adopts mcp-ts-core 0.13.6's session-mode and retry-budget APIs."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.4.4 — 2026-09-19
8
+
9
+ ## Added
10
+
11
+ - **CodeQL static analysis workflow** (`.github/workflows/codeql.yml`) runs on push/PR to `main` and weekly, analyzing JavaScript/TypeScript and Actions.
12
+
13
+ ## Changed
14
+
15
+ - **`sessionMode: 'stateless'` is now declared in `src/index.ts`** instead of left to the deployment's `MCP_SESSION_MODE` default — every RIS tool answers from the request it's given, so no handler needs a stateful session.
16
+ - **The search retry budget is `withRetry`'s `deadlineMs`** (mcp-ts-core 0.13.x) — each attempt bounds its own fetch at whatever remains of the 48s budget, and an expiry aborts the in-flight request rather than sleeping past it; a budget-exhausted call now reports `data.reason: 'retry_deadline_exceeded'`.
17
+ - **`lint:packaging` now checks the README `Version-` badge against `package.json`** (mcp-ts-core 0.13.x) — a stale badge fails `devcheck`.
18
+
19
+ ## Fixed
20
+
21
+ - **`ris_list_reference`'s `cell()` escapes backslashes before pipes** — an unescaped backslash preceding table punctuation was silently dropped by markdown rendering, corrupting cell values containing `\|`, `\*`, or a Windows path ([#41](https://github.com/cyanheads/ris-austria-mcp-server/issues/41)).
22
+
23
+ ## Dependencies
24
+
25
+ - `@cyanheads/mcp-ts-core` ^0.13.0 → ^0.13.6
26
+ - `zod` ^4.6.1 → ^4.6.5
27
+ - `@biomejs/biome` 2.5.13 → 2.5.14
28
+ - `@types/node` 26.5.1 → 26.6.1
29
+ - `tsc-alias` ^1.9.4 → ^1.9.5
30
+ - `vitest` ^5.0.0 → ^5.0.1