viafrei 1.4.9 → 1.5.5

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 (4) hide show
  1. package/API.md +32 -8
  2. package/CHANGELOG.md +205 -0
  3. package/SOURCES.md +25 -18
  4. package/package.json +5 -1
package/API.md CHANGED
@@ -9,7 +9,7 @@ the server's own text, reproduced verbatim, because that text is what an
9
9
  assistant reads when it decides which tool to call; paraphrasing it here would
10
10
  document a different server.
11
11
 
12
- **It is a dated snapshot, taken on 2026-10-01.** Generating this file makes
12
+ **It is a dated snapshot, taken on 2026-10-02.** Generating this file makes
13
13
  it impossible for the document and the snapshot to disagree — CI regenerates and
14
14
  compares — but it cannot keep the snapshot from ageing against the live server,
15
15
  because a capture is a point in time. **The source of truth is the running
@@ -17,12 +17,12 @@ server:** connect any MCP client and call `tools/list`.
17
17
 
18
18
  | | |
19
19
  | --- | --- |
20
- | Server | `viafrei` 1.4.9 |
20
+ | Server | `viafrei` 1.5.5 |
21
21
  | MCP protocol | `2025-06-18` |
22
22
  | Streamable HTTP | https://mcp.viafrei.de/mcp |
23
23
  | Legacy HTTP+SSE | https://mcp.viafrei.de/sse |
24
- | Captured from | `https://mcp.viafrei.de/mcp` on 2026-10-01 |
25
- | Surface | 19 tools, 10 resources, 2 resource templates, 9 prompts |
24
+ | Captured from | `https://mcp.viafrei.de/mcp` on 2026-10-02 |
25
+ | Surface | 20 tools, 10 resources, 2 resource templates, 9 prompts |
26
26
  | Parameter schemas | JSON Schema draft-07 |
27
27
  | Capabilities | `tools`, `resources`, `prompts`, `logging` |
28
28
 
@@ -31,7 +31,7 @@ No API key. No account. No sign-up.
31
31
  ## Contents
32
32
 
33
33
  - [How to read a result](#how-to-read-a-result)
34
- - [Tools](#tools) — 19
34
+ - [Tools](#tools) — 20
35
35
  - [Resources](#resources) — 10
36
36
  - [Resource templates](#resource-templates) — 2
37
37
  - [Prompts](#prompts) — 9
@@ -410,7 +410,7 @@ The server's own instructions to a connecting client, verbatim:
410
410
 
411
411
  **Read-only** — it changes nothing. Reaches a third-party source (open world). Idempotent: true. Destructive: false.
412
412
 
413
- > Return the next departures from a German railway station: time, line, destination, platform, delay and cancellations. Use when someone asks when their train, S-Bahn or ICE leaves, whether it is late, or what is leaving a station now — give the station name as the person said it ("Hamburg Hbf", "Munich Central"); an ambiguous name comes back as a list. Do NOT use for buses or trams (punctuality: check_transit_disruption), for tickets, fares or journey planning, or for motorway traffic — call check_autobahn_traffic. At most 15 departures, window 120 min. Results carry their attribution line.
413
+ > Next departures from a German railway station, with platform, delay and cancellations. Use when asked when a train, S-Bahn or ICE leaves a named station, or whether THAT departure is late; vague later-today wording ("heute Abend") stays here. Whether ONE line is punctual ("ist die S1 pünktlich?") is NOT this tool, though it is rail and about delay — call check_transit_disruption. Do NOT use for buses, trams, a non-railway stop, another day or a time over 2 h away — get_departures. No destination filter: read the board. Max 15 departures, window 120 min. Results carry their attribution line.
414
414
 
415
415
  | parameter | type | required | default | constraints |
416
416
  | --- | --- | --- | --- | --- |
@@ -425,9 +425,33 @@ The server's own instructions to a connecting client, verbatim:
425
425
  - **`eva_no`** — The station's EVA number (6–8 digits, e.g. 8002549 for Hamburg Hbf), when a previous result gave you one. It skips the name lookup and is exact — use it to answer a follow-up about a station this tool has already named.
426
426
  - **`language`** — Set this on every call to the language the person is writing in: "en" if they wrote English, "de" if they wrote German. Do not leave it out because it has a default — the default is only the fallback when the language is genuinely unclear, and an English question answered in German is a wrong answer. Place names, station names and road numbers are never translated in either language; in English the German term is kept in parentheses so the person recognises it on signs and in local apps.
427
427
  - **`limit`** — How many departures to return, earliest first (1–15, default 10). More than 15 is refused — that is a board a person can read, not a dataset.
428
- - **`station`** — The railway station, as the person says it: "Hamburg Hbf", "Köln Hbf", "Munich Central", "Frankfurt (Main) Hbf". Pass their words — English names and "central station" are understood. If the name fits several stations the result lists them and asks which; do not guess one yourself. Give either station OR eva_no, never both.
428
+ - **`station`** — The railway station, as the person says it: "Hamburg Hbf", "Köln Hbf", "Munich Central", "Frankfurt (Main) Hbf". Pass their words — English names and "central station" are understood. If the name fits several stations (a bare "Hauptbahnhof"), call anyway: the result lists them and asks which; do not guess one yourself. Give either station OR eva_no, never both.
429
429
  - **`when`** — Start of the window as an ISO-8601 instant with an offset ("2026-09-20T18:30:00+02:00"). Leave it out for "now", which is what almost every question means. Times in the answer are Europe/Berlin whatever you pass.
430
430
 
431
+ ### `get_departures` — Scheduled departures (bus, tram, train)
432
+
433
+ **Read-only** — it changes nothing. Answers from data this service already holds (closed world). Idempotent: true. Destructive: false.
434
+
435
+ > Scheduled departures from any German public-transport stop — bus, tram, U-Bahn, S-Bahn, train, ferry — with line, destination, platform. Use when someone asks when a bus, tram, U-Bahn or ferry goes, or for another DAY or a clock time over 2 h away: "Wann fährt der nächste Bus ab Fulda Bahnhof?" Do NOT use for a railway station's trains now or later today — get_train_departures. Planned times only: for "is my bus late?" give the plan and say so; regional punctuality check_transit_disruption. No destination filter: read the board. Window 48 h, 15 per call. Results carry their attribution line.
436
+
437
+ | parameter | type | required | default | constraints |
438
+ | --- | --- | --- | --- | --- |
439
+ | `duration_min` | integer | no | `60` | min 5; max 1440 |
440
+ | `language` | string | no | `"de"` | one of `"de"`, `"en"` |
441
+ | `limit` | integer | no | `10` | min 1; max 15 |
442
+ | `modes` | array of string | no | — | min 1 item(s); each item: one of `"rail"`, `"subway"`, `"tram"`, `"bus"`, `"ferry"` |
443
+ | `stop` | string | no | — | min length 2 |
444
+ | `stop_id` | string | no | — | min length 3 |
445
+ | `when` | string | no | — | format `date-time`; pattern (288 characters — see the description; the `format` above is the short answer) |
446
+
447
+ - **`duration_min`** — How far past that moment to look, in minutes (5–1440, default 60). Small for "what goes now", a few hours for an evening. To reach the far end of the 48 h timetable, move `when` instead of widening this: a window of a whole day returns at most 15 rows and would answer about the wrong half of it.
448
+ - **`language`** — Set this on every call to the language the person is writing in: "en" if they wrote English, "de" if they wrote German. Do not leave it out because it has a default — the default is only the fallback when the language is genuinely unclear, and an English question answered in German is a wrong answer. Place names, station names and road numbers are never translated in either language; in English the German term is kept in parentheses so the person recognises it on signs and in local apps.
449
+ - **`limit`** — How many departures to return, earliest first (1–15, default 10). The result always says how many more were in the window.
450
+ - **`modes`** — Keep only these kinds of service: "bus", "tram", "subway" (U-Bahn), "rail" (every train, including S-Bahn and regional) or "ferry". Omit it unless the person named a kind — "nur Busse", "welche Tram". Several are allowed, which is what "die Busse und Bahnen vor dem Hbf" means. An S-Bahn is "rail": the feed does not always distinguish it, and the line name ("S 6") says which it is.
451
+ - **`stop`** — The stop, as the person says it: "Fulda, Bahnhof", "München, Marienplatz", "Köln, Hbf", "Hamburg, Rathausmarkt" (town first). Include the town when the person did — half the names in Germany exist in twenty towns, and a bare "Bahnhof" or "Hauptbahnhof" comes back as a list of candidates to choose from, so call it and let the result ask. Pass their words; do not guess an id. Give either stop OR the stop id, never both.
452
+ - **`stop_id`** — The stop's timetable id, exactly as a previous result of this tool gave it ("de:06631:1234"). It skips the name lookup and is exact — use it for a follow-up about a stop this tool has already named, and for one the person picked out of a candidate list.
453
+ - **`when`** — Start of the window as an ISO-8601 instant with an offset ("2026-10-02T07:30:00+02:00"). Leave it out for "now". Convert the person's words yourself — "morgen früh", "tonight" — and pass the instant; the answer is always rendered in Europe/Berlin.
454
+
431
455
  ### `check_station_facilities` — Station lifts and escalators
432
456
 
433
457
  **Read-only** — it changes nothing. Reaches a third-party source (open world). Idempotent: true. Destructive: false.
@@ -655,5 +679,5 @@ single call.
655
679
  ---
656
680
 
657
681
  Generated from `catalogue.json` by `scripts/gen-api-doc.mjs`. The snapshot was
658
- read from `https://mcp.viafrei.de/mcp` on 2026-10-01; no tool was invoked to
682
+ read from `https://mcp.viafrei.de/mcp` on 2026-10-02; no tool was invoked to
659
683
  produce it, so no data provider was contacted.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,209 @@ a Changelog and the versions follow Semantic Versioning.
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [1.5.5] - 2026-10-02
9
+
10
+ **Mirrors the server.** The bridge is versioned to match the ViaFrei MCP server it
11
+ relays to. The running server reports 1.5.5 while the registry's latest is 1.5.4, so
12
+ this release moves the package to the server's number and carries whatever had been
13
+ waiting under `[Unreleased]`. Prepared by the `Version sync` workflow: the shipped
14
+ reference was re-captured from the running server, and the probe reported the surface
15
+ **unchanged** — only the version string and the capture date moved.
16
+
17
+ ## [1.5.4] - 2026-10-02
18
+
19
+ **Mirrors the server.** The bridge is versioned to match the ViaFrei MCP server it
20
+ relays to. The running server reports 1.5.4 while the registry's latest is 1.4.9, so
21
+ this release moves the package to the server's number and carries whatever had been
22
+ waiting under `[Unreleased]`. Prepared by the `Version sync` workflow: the shipped
23
+ reference was re-captured from the running server, and the probe reported the surface
24
+ **CHANGED** — the automation knows what moved, not what it means:
25
+
26
+ - tools: the server has get_departures, the snapshot does not
27
+ - tools: get_train_departures differs between the server and the snapshot
28
+
29
+ What that means, written by a person after reading the diff and asking the server:
30
+
31
+ - **`get_departures` is new**: scheduled departures from any German public-transport
32
+ stop — bus, tram, U-Bahn, S-Bahn, train, ferry — with line, destination and
33
+ platform, planned times only, a 48 h window and 15 per call, answering from the
34
+ DELFI static timetable (CC BY 4.0). It is the tool for "when does the next bus go",
35
+ for another day, or for a clock time more than two hours away. Asked on 2026-10-02,
36
+ prod answered that the timetable is **not loaded yet** and said so plainly instead
37
+ of inventing a board; `SOURCES.md` records that on the static-GTFS row rather than
38
+ promoting the source to *live*.
39
+ - **`get_train_departures` was re-described, not re-shaped**: its description now
40
+ draws the line against the new tool (buses, trams, a non-railway stop, another day
41
+ or a time over two hours away go to `get_departures`), keeps vague later-today
42
+ wording for itself, and sends "is the S1 punctual?" to `check_transit_disruption`
43
+ even though it is rail and about delay. The `station` argument's text adds that a
44
+ bare "Hauptbahnhof" should be sent as-is because the server lists the candidates.
45
+ No argument was added, removed or re-typed.
46
+ - The catalogue is **20 tools, 18 read-only**; `SOURCES.md` moves from nineteen /
47
+ seventeen to those numbers, and the live calls a re-measurement would cost from
48
+ fifteen to sixteen, because the tool gained is read-only and not fuel.
49
+
50
+ ### Added
51
+
52
+ - **A scheduled freshness probe: is the service alive, not merely answering?**
53
+ (`scripts/probe-freshness.mjs`, `npm run probe:freshness`, and the `Freshness`
54
+ workflow every six hours.)
55
+
56
+ The gap it closes was real and nothing here could see it. A server can return
57
+ `200` with a correctly shaped body for days while an ingest worker is dead, and
58
+ every existing check survives that: the stub tests prove the bridge's
59
+ **transport**, `check:docs` proves the document matches the **snapshot**, and the
60
+ cut-time probe proves the **surface** still matches. None of them reads the age
61
+ of the data in an answer.
62
+
63
+ This asks the running server and judges `_meta.asOf`, which every real-time tool
64
+ carries, against a limit **per tool**. That is not a detail: measured against
65
+ prod on 2026-10-01, autobahn, transit, weather and departures all answered within
66
+ a minute while `check_road_status` was **7.5 h** old, because it blends the BASt
67
+ roadworks feed, which the Mobilithek catalogue declares as **twice daily**. One
68
+ global limit would be either useless for the fast feeds or permanently red for the
69
+ slow one — and a check that is permanently red is a check that gets switched off.
70
+
71
+ Each limit sits about an order of magnitude above the source's own **declared
72
+ cadence** rather than above the single reading this was written against, so it
73
+ catches a dead worker and cannot fire on normal variation. 72 h for roadworks is
74
+ roughly six times a twice-daily interval and clears a weekend; 90 min for the
75
+ other four is many times their providers' own floors.
76
+
77
+ **The callable set is an allow-list, not a deny-rule**, and the direction is the
78
+ point: with a deny-rule a new entry runs unless it matches, and here the failure
79
+ mode is a licence breach against a provider that can revoke access. Fail-closed is
80
+ the right default on that path, so a tool nobody named is refused.
81
+
82
+ **No fuel tool is ever called**, stated separately because it carries the reason
83
+ the allow-list does not: `find_cheapest_fuel` and `find_fuel_station` answer from
84
+ MTS-K / Tankerkönig, which sets a minimum interval per station and limits use to
85
+ answering a consumer's question. A monitoring query is not that. Both guards
86
+ **refuse before any request**, and the fuel arm is not redundant — a self-test
87
+ case adds a fuel tool to **both** lists, which is the realistic way the exclusion
88
+ would be lost, and it is still refused on licence grounds.
89
+
90
+ **Scheduled, not on push**, because CI here reaches nothing by design: a
91
+ freshness failure is news about the service, not about the commit, and reddening
92
+ a contributor's push for it would teach people to ignore red.
93
+
94
+ **The instrument is proved before it is trusted**, and the workflow runs the two
95
+ steps in that order. The self-test is hermetic — every case against a local stub
96
+ on loopback — and it is the only place the staleness verdict is ever exercised,
97
+ because a healthy endpoint cannot produce a stale payload. So the self-test runs
98
+ **first**: if it fails the instrument is broken, and if the live step fails the
99
+ service is. A monitor whose verdict is never proved reports success about a dead
100
+ service exactly as convincingly as about a live one.
101
+
102
+ Three exit codes, because "could not check" and "checked and it is wrong" are
103
+ different answers: `0` every feed inside its limit, `1` a real defect (stale, or
104
+ no `asOf`, or no attribution, or `isError`, or an `asOf` in the future — a clock
105
+ fault must not read as very fresh), and `2` could not check (endpoint, session,
106
+ or a response shape nobody recognises). An unparsable body is deliberately `2`
107
+ rather than `1`.
108
+
109
+ Found by its own self-test and fixed: `--json` printed the human summary to
110
+ stdout after the document, so the stream did not parse. Both streams are now
111
+ pinned by a case.
112
+
113
+ **Found in review, and it is the case the first draft had no test for.** A
114
+ JSON-RPC *error* — what the server returns for a retired tool or a renamed
115
+ argument, and this watch list hard-codes seven argument names — fell through to
116
+ the no-result branch: **exit 1, every feed printed `STALE`, on a healthy
117
+ service**, and `error.message`, the entire diagnosis, discarded. It is now exit
118
+ **2** with the server's code and message printed, a distinct `?????` label
119
+ because `STALE` is a claim about the *data* rather than about our ability to
120
+ measure it, and a refusal sentence that no longer says the body failed to parse —
121
+ it parsed perfectly. Four assertions cover it, and removing the guard turns all
122
+ four red.
123
+
124
+ - **The registry is compared with the server every six hours, and a release that
125
+ would close the gap is PREPARED — never performed** (`scripts/propose-release.mjs`,
126
+ `npm run propose:release`, and the `Version sync` workflow).
127
+
128
+ The bridge is versioned to match the server it relays to, and until now the only
129
+ thing that noticed the registry falling behind was a person checking by hand: 1.4.9
130
+ sat on prod while npm said 1.4.8 until somebody asked. Every step of catching up
131
+ was mechanical and identical each time — `npm version`, the probe's `--write`,
132
+ `docs:api`, the `## [X.Y.Z]` block, the gates — so the workflow does them and
133
+ pushes the result as `release/X.Y.Z` with a pull request.
134
+
135
+ **What it will not do, by design.** It does not merge, tag or publish, for three
136
+ reasons each sufficient alone: the merge needs a reviewer verdict covering HEAD,
137
+ which a bot merging through the API would bypass; an npm version is immutable, so
138
+ a wrong one is forever; and when the server's **surface** changed rather than its
139
+ number, the release note needs a sentence about what the change means, which
140
+ nothing here can write — the 1.4.9 cut carried a licence-relevant fix for exactly
141
+ that case. So the verdict, the merge and the tag stay with a person, and the tag
142
+ publishes as it always has.
143
+
144
+ **Four states, each named in the output**, because the workflow branches on them:
145
+ `in-sync`, `awaiting-tag` (main already carries the server's version; the tag is
146
+ the missing step), `drift` (the one that is prepared), and `behind` — the server
147
+ BEHIND the registry, which is exit 1 and proposes nothing, because a downgrade is
148
+ a decision about whether prod rolled back or a publish was premature.
149
+
150
+ **Every version string read from the network is checked against `X.Y.Z` before it
151
+ is used anywhere**, since it ends up in a branch name, a commit and an `npm
152
+ version` argument; a prerelease on either side is a refusal. A mutant with the
153
+ guard removed is part of the self-test, so the guard is proved live rather than
154
+ present. Detect **writes nothing**, asserted by hashing the five files it may
155
+ later touch. The server is read through the catalogue probe — one reader of that
156
+ endpoint, `initialize` and the four list calls, no tool invoked — and the probe's
157
+ own WRONG/DATED verdict decides the lead of the CHANGELOG block: DATED says the
158
+ surface is unchanged; WRONG lists what moved and says **a person must describe it
159
+ before this merges**, and the pull request is opened as a **draft**. A failing
160
+ offline gate is also a draft rather than a lost run: the gate's output goes into
161
+ the pull request, where the person who has to act on it will read it.
162
+
163
+ A proposal is idempotent across runs — a `release/X.Y.Z` branch already on origin
164
+ is left alone, so a pull request waiting for its review is not joined by a twin
165
+ every six hours — and a CHANGELOG already carrying the block is a refusal. The
166
+ workflow dispatches CI on the branch explicitly, because a push or a pull request
167
+ made with the workflow token starts no workflow by GitHub's rule. **Two
168
+ preconditions are asserted before anything is read**, because each failure would
169
+ otherwise conceal itself: the run must be on the default branch (a dispatch from
170
+ another ref would branch off it and open a pull request carrying its commits), and
171
+ the repository must allow Actions to open pull requests — a setting that is OFF by
172
+ default (and was, here, until 2026-10-02), without which `gh pr create` fails after
173
+ the branch is pushed and the orphan branch then silences every later run. That
174
+ read is administration-class and the workflow token may not be able to make it,
175
+ so it has **three outcomes**: a successful `false` refuses, a failed read is named
176
+ and the run continues, because if the pull request still cannot be opened the
177
+ branch just pushed is deleted again for the same reason.
178
+
179
+ The MCP stub the catalogue probe's self-test ran on moved to `scripts/mcp-stub.mjs`,
180
+ and the pass/fail counter both self-tests print through to `scripts/check-harness.mjs`,
181
+ so this self-test shares them rather than carrying copies; the probe's own case
182
+ count is unchanged.
183
+
184
+ ### Fixed
185
+
186
+ - **The `Version sync` workflow runs the leak sweep on the tree it prepares.** Its
187
+ first real run was this release, and the proposal it pushed carried two bare
188
+ numbers from the new tool's schema text — a minutes-per-day maximum and an example
189
+ stop id — that the public-repo sweep refuses. Nothing in the workflow had asked:
190
+ its three gates compare the shipped files with each other, not whether the
191
+ snapshot may be published. What caught it was the CI run the workflow itself
192
+ dispatched — the pull request's own `pull_request` run never executed, it sat at
193
+ `action_required` — and the step that failed was the publish-hygiene gate on the
194
+ **built tarball**, because `API.md` ships in the package: the numbers were on their
195
+ way into the published artefact, not only into the repository. The sweep now runs
196
+ after the five files are staged, and a finding makes the proposal a **draft** with
197
+ the findings in the pull-request body. That job builds nothing, so it cannot run
198
+ the tarball gate itself: it covers this class of finding, not the exact gate that
199
+ fired. The two numbers are on `numbers.allowed` as the harmless values they are;
200
+ both exist only in text the server controls, so a re-wording upstream makes the
201
+ sweep refuse them as unused — loudly, which is the right direction.
202
+ - **`check-sources`' self-test reads its counts off the check's own summary line**
203
+ instead of carrying them: three of its cases said "seventeen" and "fifteen", and
204
+ when the page correctly moved to eighteen and sixteen they went inert — one passed
205
+ without mutating anything — or asserted the previous release's numbers. A mutation
206
+ that changes nothing, or that would change one of two occurrences, is now a
207
+ refusal. The check's own pattern for the re-run sentence accepts "would now mean"
208
+ beside "would still mean", because the count did move this time and the page says
209
+ so.
210
+
8
211
  ## [1.4.9] - 2026-10-01
9
212
 
10
213
  **Mirrors the server.** The bridge is versioned to match the ViaFrei MCP server
@@ -1451,6 +1654,8 @@ for it, so the number is free; the bridge will use it when the platform does.
1451
1654
  commits, and a squash makes them unreachable from `main` - which would turn
1452
1655
  the check red on `main` for everybody, for something no contributor did.
1453
1656
 
1657
+ [1.5.5]: https://github.com/mavrovde/viafrei-bridge/releases/tag/v1.5.5
1658
+ [1.5.4]: https://github.com/mavrovde/viafrei-bridge/releases/tag/v1.5.4
1454
1659
  [1.4.9]: https://github.com/mavrovde/viafrei-bridge/releases/tag/v1.4.9
1455
1660
  [1.3.22]: https://github.com/mavrovde/viafrei-bridge/releases/tag/v1.3.22
1456
1661
  [1.3.16]: https://github.com/mavrovde/viafrei-bridge/releases/tag/v1.3.16
package/SOURCES.md CHANGED
@@ -143,28 +143,35 @@ is deliberately not re-measured, and the count says so rather than absorbing it:
143
143
  status this page cannot stand behind is worse than an honest gap.
144
144
 
145
145
  **The server has grown since that measurement, and this page has not re-run it.**
146
- As of the 2026-09-29 capture shipped alongside this page it exposes nineteen tools,
147
- seventeen of them read-only, including a SECOND fuel tool, `find_fuel_station`.
148
- That one is excluded from any spot check for exactly the same reason
149
- `find_cheapest_fuel` is, and the reason is a licence condition rather
150
- than a convenience: MTS-K sets a minimum interval per station and its terms make
151
- needless querying a real risk to the access itself. **Both fuel tools are excluded,
152
- not one.**
146
+ As of the 2026-10-02 capture shipped alongside this page it exposes twenty tools,
147
+ eighteen of them read-only. Between that measurement and the 2026-10-02 capture it
148
+ gained two tools, and both matter here. One is a SECOND fuel tool,
149
+ `find_fuel_station`, excluded from any spot check for exactly the same reason
150
+ `find_cheapest_fuel` is, and the reason is a licence condition rather than a
151
+ convenience: MTS-K sets a minimum interval per station and its terms make needless
152
+ querying a real risk to the access itself. **Both fuel tools are excluded, not one.**
153
+ The other is `get_departures` (server 1.5.4): scheduled departures from any
154
+ public-transport stop, answering from the DELFI static timetable — a source this
155
+ page had listed as *read* with nothing using it. Something uses it now, and on
156
+ 2026-10-02 the server's own answer was that the timetable is not loaded yet; the row
157
+ below says exactly that rather than promoting it.
153
158
 
154
159
  The sentence above therefore describes what was measured on 2026-09-27 and not what
155
- the server offers today. Re-running it would still mean **fifteen** live calls
156
- against real providers — the same fifteen, because the one read-only tool the server
157
- gained is the second fuel tool, and that one is excluded. Sixteen read-only minus one
158
- fuel tool was fifteen; seventeen minus two is fifteen again. Those calls would
159
- re-confirm statuses this page already knows, so it is dated on purpose rather than
160
- refreshed on a schedule — and dated is said out loud, because a measurement silently
161
- carried forward under a present-tense sentence is the failure this section exists to
162
- avoid.
160
+ the server offers today. Re-running it would now mean **sixteen** live calls against
161
+ real providers: the read-only tools minus the two excluded fuel tools. The count moved
162
+ by one because `get_departures` is read-only and not fuel, so a re-run would call
163
+ it. Those calls would mostly re-confirm statuses this page already knows, so it is
164
+ dated on purpose rather than refreshed on a schedule — and dated is said out loud,
165
+ because a measurement silently carried forward under a present-tense sentence is the
166
+ failure this section exists to avoid.
163
167
 
164
168
  - **live** — an answer came back naming it when this page was checked;
165
169
  - **in the service** — licensed and loaded, and the spot check produced no
166
170
  answer that named it, so it is reported as unconfirmed rather than as live;
167
- - **read** — the licence is read and cleared, and nothing uses it yet.
171
+ - **read** — the licence is read and cleared, and no answer has been seen from it:
172
+ because nothing asks it, because what asks it is told the data is not loaded, or
173
+ because what asks it has not been seen to get an answer either way. Where it is not
174
+ simply that nothing asks it, the row says so.
168
175
 
169
176
  There used to be a fourth value, **not on the public service today**, and no row
170
177
  carries it any more: the three rows that did now answer. It is removed from this
@@ -188,8 +195,8 @@ status somebody could still be relying on.
188
195
  | Public-transport realtime (GTFS-RT Trip Updates) | DELFI e.V., via the national access point (Mobilithek) | Germany-wide departure and arrival forecasts | real time | **CC BY-SA (version unstated)** | live |
189
196
  | FaSta — Facility Status | Deutsche Bahn AG (DB API Marketplace) | Live state of lifts and escalators at stations | live status | CC BY 4.0 | live |
190
197
  | Geocoding (addresses and points of interest) | OpenStreetMap contributors | Street and house-number points and mapped points of interest in Germany | refreshed from the OSM extract | **ODbL 1.0** | live |
191
- | Timetable data (static GTFS) | DELFI e.V. | Germany-wide scheduled public transport | weekly release | CC BY 4.0 | read |
192
- | Stop directory (zHV) | DELFI e.V. | Every public-transport stop in Germany with its identifier and coordinates | weekly release | CC BY 4.0 | read |
198
+ | Timetable data (static GTFS) | DELFI e.V. | Germany-wide scheduled public transport | weekly release | CC BY 4.0 | read — asked by `get_departures` since server 1.5.4; on 2026-10-02 the server answered that the timetable is not loaded yet, and said so rather than inventing a board |
199
+ | Stop directory (zHV) | DELFI e.V. | Every public-transport stop in Germany with its identifier and coordinates | weekly release | CC BY 4.0 | read — `get_departures` resolves stop names against it since server 1.5.4; the 2026-10-02 answer named no stop and said only that the timetable is not loaded, so whether the directory answered cannot be read off it |
193
200
  | Disruption reports (Störungsmeldungen) | DELFI e.V., via the national access point (Mobilithek) | Germany-wide public-transport disruption messages | real time | **CC BY-SA 4.0** | read |
194
201
  | Station car parks (DB BahnPark) | Deutsche Bahn AG (DB API Marketplace) | Car parks at railway stations, with their operator and access details | continuous | **dl-de/by-2-0** | read |
195
202
  | Administrative units and place names | Bundesamt für Kartographie und Geodäsie (BKG), product GN250 | Länder, Regierungsbezirke, Kreise, Gemeinden with their official keys and names | yearly release | **dl-de/by-2-0** | live |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viafrei",
3
- "version": "1.4.9",
3
+ "version": "1.5.5",
4
4
  "description": "German traffic, rail, parking, charging, fuel, address and weather data in your AI assistant - MCP, no API key. stdio bridge to the hosted ViaFrei MCP server.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -79,6 +79,10 @@
79
79
  "test:sources": "node scripts/check-sources.test.mjs",
80
80
  "probe:catalogue": "node scripts/probe-catalogue.mjs",
81
81
  "test:probe": "node scripts/probe-catalogue.test.mjs",
82
+ "probe:freshness": "node scripts/probe-freshness.mjs",
83
+ "test:freshness": "node scripts/probe-freshness.test.mjs",
84
+ "propose:release": "node scripts/propose-release.mjs",
85
+ "test:propose": "node scripts/propose-release.test.mjs",
82
86
  "rules:show": "node scripts/show-rules.mjs"
83
87
  },
84
88
  "dependencies": {