viafrei 1.3.12 → 1.3.15
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.
- package/CHANGELOG.md +308 -0
- package/README.md +173 -47
- package/SOURCES.md +237 -71
- package/dist/config.d.ts +10 -0
- package/dist/config.js +39 -6
- package/dist/failure.d.ts +10 -1
- package/dist/failure.js +51 -3
- package/dist/fetch.d.ts +19 -0
- package/dist/fetch.js +77 -4
- package/package.json +15 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,314 @@
|
|
|
3
3
|
All notable changes to this package are documented here. The format follows Keep
|
|
4
4
|
a Changelog and the versions follow Semantic Versioning.
|
|
5
5
|
|
|
6
|
+
## [1.3.15] - 2026-09-27
|
|
7
|
+
|
|
8
|
+
The first release with features in it since the bridge got code. Three
|
|
9
|
+
capabilities, and the documentation caught up with a service that had quietly
|
|
10
|
+
grown past it.
|
|
11
|
+
|
|
12
|
+
**If you only use `npx -y viafrei`, nothing changes and nothing breaks.** No flag
|
|
13
|
+
was removed, no default moved, no exit code changed meaning. The three additions
|
|
14
|
+
are opt-in or invisible.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **A contributor surface: `CODE_OF_CONDUCT.md`, three issue templates with a
|
|
19
|
+
chooser, and a pull request template.** The Code of Conduct is Contributor
|
|
20
|
+
Covenant 2.1, and a report with no GitHub account goes to the Impressum e-mail
|
|
21
|
+
rather than to the **postal** address printed there — that address is still a
|
|
22
|
+
visible placeholder, and a reporting route has to be one that works. The three
|
|
23
|
+
forms are a bug, an idea, and the one report this service cannot make about
|
|
24
|
+
itself: a tool answered and the answer was useless. Beside them the chooser
|
|
25
|
+
carries the three routes that are not forms — the private security advisory,
|
|
26
|
+
Discussions, and `SOURCES.md` for a data or licence question. None of these
|
|
27
|
+
existed in 1.3.12.
|
|
28
|
+
- **`VIAFREI_MCP_HEADER` sets HTTP headers from the environment.** Many MCP
|
|
29
|
+
clients can only give a command an `env` block, never arguments, which left
|
|
30
|
+
those users no way to send a header at all. It takes the same `Name: value`
|
|
31
|
+
syntax as `--header`, refuses the same transport-owned names, and separates
|
|
32
|
+
several headers with a **newline**, which can never appear in a header name or
|
|
33
|
+
value, so nothing you might need to send is unrepresentable - a comma, a
|
|
34
|
+
semicolon and a space all occur inside real header values. A `--header` of the
|
|
35
|
+
same name wins; one of a different name is added alongside. An error names
|
|
36
|
+
`VIAFREI_MCP_HEADER`, not the flag you did not type.
|
|
37
|
+
- **`Retry-After` is honoured on the single retry, with a cap.** Both legal forms
|
|
38
|
+
are read - a number of seconds and an HTTP-date - and a value that cannot be
|
|
39
|
+
parsed, or names a moment already past, falls back to the fixed delay instead
|
|
40
|
+
of throwing. **The cap is the per-request timeout**: a server answering
|
|
41
|
+
`Retry-After: 3600` does not make `npx viafrei` sit silently for an hour; the
|
|
42
|
+
retry is abandoned and the ordinary one-line failure, which already names the
|
|
43
|
+
URL and the status, stands. `429` is retried now that the delay it asks for is
|
|
44
|
+
respected - and **only when the header gives a usable delay**. A 429 carrying
|
|
45
|
+
nothing readable is not retried at all, because the only thing left would be the
|
|
46
|
+
fixed delay, and a fixed delay is what makes a rate limit worse. For the same
|
|
47
|
+
reason a *thrown* 429 is still not retryable: there the header is gone. A 503 in
|
|
48
|
+
the same state IS retried, and a test pins the difference so it cannot be
|
|
49
|
+
flattened by accident.
|
|
50
|
+
- **A pathless `--url` that gets a 404 is told where the endpoint usually is.**
|
|
51
|
+
`--url http://127.0.0.1:3000` now fails with `... HTTP 404 Not Found - the URL
|
|
52
|
+
has no path; the MCP endpoint is usually /mcp`. **It is a hint and not a
|
|
53
|
+
rewrite**: quietly appending the path would send the request somewhere nobody
|
|
54
|
+
asked for and hide a different mistake later. The hint appears only when the
|
|
55
|
+
path is empty or `/`, never on another status, and a URL that will not parse
|
|
56
|
+
skips the hint rather than making the failure message itself throw.
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- **The README leads with how to connect, and says why to build on it.** The page
|
|
61
|
+
opened by explaining that you probably do not need this package, which is true
|
|
62
|
+
and was the first thing a developer read. The three transports now come first,
|
|
63
|
+
with the address to use, and a section sets out what the service is actually
|
|
64
|
+
offering: one protocol in place of twenty-one sources from sixteen publishers,
|
|
65
|
+
the licence obligations travelling with each answer, machine-readable failures,
|
|
66
|
+
and answers in the language of the question.
|
|
67
|
+
- **The tool count was wrong and is now a capability table.** The page said
|
|
68
|
+
thirteen tools; the server registers **eighteen**. The five geocoding tools had
|
|
69
|
+
shipped on the platform before the previous bridge release was even cut. The
|
|
70
|
+
table groups tools by the question they answer, which is stable, and the page
|
|
71
|
+
still refuses to paste the catalogue itself - a published tarball cannot be
|
|
72
|
+
corrected, so `tools/list` on the running server remains the only source of
|
|
73
|
+
truth.
|
|
74
|
+
- **The one completeness claim the new table introduced was measured before it
|
|
75
|
+
shipped.** The Places row says "nationwide, all sixteen Länder", which no tool
|
|
76
|
+
reports and which would rot at a partial import - the same objection that removed
|
|
77
|
+
the two address figures under **Fixed** ("Two address figures nobody could check
|
|
78
|
+
are gone"). It was kept because it was measured
|
|
79
|
+
rather than inferred, and it now carries the date: on 2026-09-27 the service's own
|
|
80
|
+
per-Land verdict - which reads both OSM tables, names any missing Land and exits
|
|
81
|
+
non-zero on one - reported all sixteen imported with every row scoped to its Land,
|
|
82
|
+
and two address lookups in the two Länder least likely to have been staged —
|
|
83
|
+
Mecklenburg-Vorpommern and Saarland, asked in Rostock and Saarbrücken — both
|
|
84
|
+
answered through the public endpoint with `osm` in `_meta.sources`. `SOURCES.md` carries the measurement, and one caveat that nearly
|
|
85
|
+
misled it: a single address that does not resolve says nothing about its Land.
|
|
86
|
+
- **The README now says that a slow call may be a retried call.** A `429`, `502`,
|
|
87
|
+
`503` or `504` is tried again **once** after the delay the server asked for, as
|
|
88
|
+
is a connection that failed outright; a `429` with no usable `Retry-After`, and
|
|
89
|
+
any delay longer than the per-request timeout, are not retried at all. The page
|
|
90
|
+
also now says what that timeout does and does not bound, because the first draft
|
|
91
|
+
of the paragraph got it wrong in the direction a user would act on: it is attached
|
|
92
|
+
per HTTP request, so the second attempt gets a fresh one and so does each hop of a
|
|
93
|
+
same-origin redirect, and **no setting here caps the whole call**. Two drafts of
|
|
94
|
+
that one clause were wrong, in the same direction and in the sentence a reader
|
|
95
|
+
would act on: the first said a retried call "can never exceed the timeout you gave
|
|
96
|
+
it", and the correction that replaced it said to halve the timeout for a hard
|
|
97
|
+
ceiling — but two attempts plus a delay that may itself be as long as the timeout
|
|
98
|
+
is three times it, not two, and more again if the endpoint redirects. Both were
|
|
99
|
+
caught in review rather than published, which is the argument for rounds that cost
|
|
100
|
+
nothing, and the second is the sharper lesson: a corrected number is still a
|
|
101
|
+
number, and nothing was reading it. That behaviour shipped earlier and the
|
|
102
|
+
page did not mention it, so a user watching a call take longer than expected had
|
|
103
|
+
nothing to read. Nothing about the behaviour changed in this release.
|
|
104
|
+
- **Both examples on the page are now captured output rather than plausible
|
|
105
|
+
output.** The departures block is a real answer from `mcp.viafrei.de` on
|
|
106
|
+
2026-09-27, and the failure line is copied from a run.
|
|
107
|
+
- **The badge hosts are admitted to the leak ruleset as their own group, with
|
|
108
|
+
their one cost written down.** A shields.io badge is the only entry on that
|
|
109
|
+
allow-list that causes a request from somebody else's browser, so the ruleset
|
|
110
|
+
now says that, says the admission test, and says it is the reason to refuse the
|
|
111
|
+
next image host rather than to admit it by precedent. The list itself stays
|
|
112
|
+
sorted alphabetically rather than grouped, and the ruleset now says why: one flat
|
|
113
|
+
sorted list is what makes a duplicate, or a near-miss spelling of a host already
|
|
114
|
+
on it, visible at a glance.
|
|
115
|
+
- **`numbers.allowed` is back to six entries, and the two rules governing it are
|
|
116
|
+
separated.** A seventh was added during this release to accommodate two bare
|
|
117
|
+
millisecond literals, then removed once both were written `1_000`: an entry that
|
|
118
|
+
covers nothing in the tree is a caption pointing at a value, which is the one
|
|
119
|
+
thing the inverse construction exists to avoid. (The entry is not named here,
|
|
120
|
+
because naming it would put the bare form back in the tree and the sweep would
|
|
121
|
+
be right to say so — it said so about the first draft of this very bullet.) The ruleset now distinguishes **the rule** (an
|
|
122
|
+
entry occurs in the tree and is not an internal value — which `3600` and `9110`,
|
|
123
|
+
both prose in a comment, satisfy) from **the preference** (write a numeric
|
|
124
|
+
separator where one is available, so the literal never reaches the list at all).
|
|
125
|
+
Citing the second as if it were the first is what made the list look arbitrary
|
|
126
|
+
for one review round.
|
|
127
|
+
- **Two `allowedHosts` entries covering nothing are gone.** Bare `shields.io` and
|
|
128
|
+
bare `contributor-covenant.org` sat beside `img.shields.io` and
|
|
129
|
+
`www.contributor-covenant.org`, and no URL in the tree uses either; the bare
|
|
130
|
+
spellings occur only in prose about them, and the host extractor reads a
|
|
131
|
+
scheme-prefixed URL and nothing else. That is the same shape as the
|
|
132
|
+
allow-list number this release removed: an entry that permits nothing is a
|
|
133
|
+
permission nobody can audit. The history-residue block is also back to one line
|
|
134
|
+
per entry; reformatting it to five four-line objects changed no meaning and cost
|
|
135
|
+
twenty-five lines of diff in the file a reviewer reads hardest.
|
|
136
|
+
- **A re-wrap put a private name into a public file, and the sweep caught it —
|
|
137
|
+
which is also a demonstration of the one limit it declares loudest.** The gate
|
|
138
|
+
reads **one line at a time**, and says so in its own limits list. A paragraph
|
|
139
|
+
of this release's prose was re-flowed, and prose that had passed every earlier
|
|
140
|
+
run matched a live entry the moment the wrapping changed. Nothing was ever
|
|
141
|
+
published: it reached a working tree and no further. What the offending text
|
|
142
|
+
was is deliberately not recorded — `scripts/rules.json` says to record the
|
|
143
|
+
rule, the lengths and the narrowing and never the subject, because a
|
|
144
|
+
description narrows the candidate space for a live entry further than the hash
|
|
145
|
+
alone does, and this file cannot be unpublished. The lesson is the gate's, not
|
|
146
|
+
the prose's — a line-oriented scanner's verdict depends on where the wrapping
|
|
147
|
+
falls, so its PASS is about this wrapping and not about this text, and a
|
|
148
|
+
re-wrap is a change the sweep has to see again.
|
|
149
|
+
|
|
150
|
+
### Fixed
|
|
151
|
+
|
|
152
|
+
- **`package-lock.json` still said 1.3.12 while `package.json` said 1.3.15, and
|
|
153
|
+
nothing in this repository would have noticed.** Found in the last pre-push check
|
|
154
|
+
of the release, by reading the two files rather than the diff: seven review rounds
|
|
155
|
+
had confirmed the lockfile was *unchanged*, which is true and is not the same as
|
|
156
|
+
correct. The publish workflow asserts that the TAG and `package.json` agree and
|
|
157
|
+
says so in its own step name; no gate compares the lockfile to either, and the
|
|
158
|
+
lockfile is not in the published tarball, so the only thing that reads it is the
|
|
159
|
+
`npm ci` the release build runs. It is now 1.3.15, regenerated with
|
|
160
|
+
`--package-lock-only` so the change is exactly the two version fields and no
|
|
161
|
+
dependency moved. **The missing gate is deliberately not added here**: it belongs
|
|
162
|
+
in `publish.yml`, and an untested edit to the workflow that publishes, inside the
|
|
163
|
+
commit it publishes from, is the wrong trade a day before the tag — it is filed
|
|
164
|
+
for the next release and rehearsed through the `workflow_dispatch` dry run, beside
|
|
165
|
+
the two other workflow items this release also deferred.
|
|
166
|
+
- **SOURCES.md described a service three capabilities smaller than the one that
|
|
167
|
+
is running.** Address lookup, lift and escalator status, and public-transport
|
|
168
|
+
realtime were all documented as not answering on the public service. All three
|
|
169
|
+
answer, and each was re-confirmed by a call whose result named the source. The
|
|
170
|
+
BKG administrative gazetteer was listed as merely licence-read and is in fact
|
|
171
|
+
named by answers from five different tools. Every status in the catalogue was
|
|
172
|
+
re-measured on 2026-09-27 by calling **fifteen of the server's sixteen
|
|
173
|
+
read-only tools** - every one except `find_cheapest_fuel` - once each and
|
|
174
|
+
reading `_meta.sources`. That one exception is **fuel**, which is deliberately not
|
|
175
|
+
re-measured because MTS-K sets a per-station floor and its terms make needless
|
|
176
|
+
querying a risk to the access itself. That row is labelled `not re-measured`
|
|
177
|
+
rather than dressed up as today's measurement.
|
|
178
|
+
- **SOURCES.md contradicted itself, and one of the contradictions was a licence
|
|
179
|
+
statement.** The table was re-measured; three paragraphs beneath it were not,
|
|
180
|
+
and they still told a reader that address lookup, facility status and
|
|
181
|
+
public-transport realtime do not answer. The worst of them sat in the ODbL
|
|
182
|
+
obligations section and said a share-alike obligation *"binds nobody using that
|
|
183
|
+
service right now"* - measurably false, on the one page whose job is to state
|
|
184
|
+
obligations, inside a tarball that cannot be corrected after publication. Every
|
|
185
|
+
such paragraph now states today's measurement and says which of the two
|
|
186
|
+
versions a reader saw. The status legend lost the value no row carries any more,
|
|
187
|
+
rather than leaving a status somebody could still be relying on.
|
|
188
|
+
- **The README promised rail disruptions "on a line or at a stop".** The server
|
|
189
|
+
declares the opposite in its own tool description and in `viafrei://coverage`:
|
|
190
|
+
it never answers whether a named line, trip or stop is on time. The row now says
|
|
191
|
+
region-wide and says never one line or stop.
|
|
192
|
+
- **Two address figures nobody could check are gone.** The Places row quoted a
|
|
193
|
+
count of addresses and POIs that no tool reports and that would rot at the next
|
|
194
|
+
import - the exact argument the paragraph below it makes against pasting a
|
|
195
|
+
catalogue.
|
|
196
|
+
- **The departures example names both of its edits.** It said the only edit was
|
|
197
|
+
shortening two URIs; five of the ten departures were dropped as well.
|
|
198
|
+
- **The OSM attribution line printed in `SOURCES.md` was not the line the server
|
|
199
|
+
emits.** The code fence said `Geokodierung: …`; no answer has carried that prefix
|
|
200
|
+
since `find_poi` and `find_address` began returning an OSM row **as** the answer
|
|
201
|
+
— a name, a brand, a door — rather than only a coordinate resolved from one. The
|
|
202
|
+
correct line is `OSM-Standortdaten: © OpenStreetMap-Mitwirkende, ODbL 1.0`, and
|
|
203
|
+
1.3.12 carried it nowhere: the page was wrong about the one string it exists to
|
|
204
|
+
publish, inside a tarball that cannot be corrected. A draft of this release then
|
|
205
|
+
printed both spellings at once, which is how it was caught. Every other fenced
|
|
206
|
+
attribution line was swept against the server's register; this was the only
|
|
207
|
+
mismatch.
|
|
208
|
+
- **"An answer about a place does not carry the ODbL line" — three words missing,
|
|
209
|
+
and the sentence reverses.** That wording is this release's own: 1.3.12 carried a
|
|
210
|
+
different wrong version of the same sentence (see the bullet below), and this one
|
|
211
|
+
was written while fixing that one and caught in review. The platform's own
|
|
212
|
+
wording is "a result about a
|
|
213
|
+
place **from our own gazetteer**, a station or a motorway is not built from
|
|
214
|
+
OpenStreetMap". Place resolution falls through the gazetteer to `osm_pois` and
|
|
215
|
+
then `osm_addresses`, so **every** tool that takes a `place` can return an
|
|
216
|
+
OSM-derived answer: measured on 2026-09-27, a weather warning for
|
|
217
|
+
`Zeiss-Großplanetarium` named `["dwd","osm"]` and carried the line, and one for
|
|
218
|
+
`Allianz Arena` named `["osm"]` alone. Both places on this page now carry the
|
|
219
|
+
qualifier and state the predicate — **which table answered**, not which tool
|
|
220
|
+
was called. Two wrong versions of this sentence have now been caught, and both
|
|
221
|
+
erred towards telling a reader an obligation did not apply to them. The last
|
|
222
|
+
round found the same error in the two places nobody re-reads after fixing a
|
|
223
|
+
paragraph: the **section heading** and its opening sentence still framed the
|
|
224
|
+
obligation as something that applies "if you get an address or a point of
|
|
225
|
+
interest back", so a reader who scans headings could conclude the section was
|
|
226
|
+
not theirs. Heading, lead and both bodies now state the predicate, and the
|
|
227
|
+
back-reference to the section was moved in the same edit as the heading.
|
|
228
|
+
- **Twenty-one sources, not nineteen — and the table was missing a row it needs
|
|
229
|
+
to be the register it claims to be.** This release added the station car parks
|
|
230
|
+
row and recorded it in its own bullet below, without correcting the number it
|
|
231
|
+
invalidated; the review
|
|
232
|
+
then found the DELFI disruption feed has a licence row in the server's register
|
|
233
|
+
and no row here. It is in the table now, `read`, at **CC BY-SA 4.0**. Re-counted
|
|
234
|
+
by hand: 21 rows, 16 distinct publisher cells. The README's dare — "count them
|
|
235
|
+
in that table" — is a good one and now survives being taken up.
|
|
236
|
+
- **The cleared-but-unused DELFI sentence was wrong in both halves, and it is a
|
|
237
|
+
licence statement.** It said "two more DELFI datasets … both CC BY 4.0 — the
|
|
238
|
+
share-alike is on the realtime feed". There are **three**, and two of them are
|
|
239
|
+
share-alike: the trip updates we serve and the disruption reports we do not.
|
|
240
|
+
Anybody planning for the day the disruption feed appears was being told to plan
|
|
241
|
+
for CC BY.
|
|
242
|
+
- **The station car parks row had no attribution line, on a page that promises one
|
|
243
|
+
per source** — a defect this release created and closed. 1.3.12 has no car-parks
|
|
244
|
+
row anywhere, and its "three products" sentence was correct for what it listed;
|
|
245
|
+
adding the row, in the bullet that raised the source count, made that sentence wrong, so the Deutsche Bahn
|
|
246
|
+
section said "three products · CC BY 4.0" over four products and two licences. Both fixed, with the BahnPark attribution
|
|
247
|
+
line written out — `read`, so no answer carries it today, and it is here so
|
|
248
|
+
nobody has to go looking on the day one does.
|
|
249
|
+
- **"No tool exposes the police traffic events yet" was a claim about the code,
|
|
250
|
+
and the code says otherwise.** That sentence is also this release's own — 1.3.12
|
|
251
|
+
carries the row at `read` and says nothing about reach. The road analysis already reads that feed, and
|
|
252
|
+
whether a source is NAMED is gated on a live catalogue row, so the row could
|
|
253
|
+
start carrying its attribution line with no release at all. The page now says
|
|
254
|
+
what it measured — `check_road_status` on the A40, A3, A1, A57 and A46 named
|
|
255
|
+
only the motorway interface and the BASt roadworks feed — and says out loud
|
|
256
|
+
that this is a statement about answers and not about reach. The row therefore
|
|
257
|
+
reads `in the service` and not `read`, and the page says not to design around
|
|
258
|
+
it.
|
|
259
|
+
- **Two dead intra-document anchors, both created by this release's own
|
|
260
|
+
corrections.** The `no API key` badge — on the npm package page and the
|
|
261
|
+
repository front page — pointed at `#quick-start`, a heading this release
|
|
262
|
+
renamed to "Connect in one line"; and `SOURCES.md`'s back-reference to the
|
|
263
|
+
§ 4.6 offer kept the old slug of a heading the previous review round renamed.
|
|
264
|
+
Every intra-document link in the tree was checked; these were the only two, and
|
|
265
|
+
both were ours. A later round found the shape that sweep could not see: an
|
|
266
|
+
issue **form** renders at `/issues/new?template=idea.yml`, so the relative
|
|
267
|
+
`../../blob/main/SOURCES.md` in it resolved one level short of the repository
|
|
268
|
+
and 404ed. It is an absolute URL now, like the one `config.yml` already used
|
|
269
|
+
for the same document.
|
|
270
|
+
- **The one place where `SOURCES.md` is now NEWER than the server's register is
|
|
271
|
+
named on the page.** BKG is `live` there on five measured answers, while
|
|
272
|
+
`viafrei://attribution` still flags it as planned — "licence read, data not
|
|
273
|
+
ingested yet". The page's own rule is that the resource wins and the page is
|
|
274
|
+
stale; that rule is right in general and wrong for this row today, so both the
|
|
275
|
+
rule and the row now say so. No bridge release can close it: it is tracked where
|
|
276
|
+
the server is developed, and what a reader of the published page can act on is
|
|
277
|
+
the answer itself - `_meta.sources` and the attribution lines it carries.
|
|
278
|
+
- **The charging answer does not count the sites without a status.** The page said
|
|
279
|
+
"a result says how many nearby sites had no status". It marks each one
|
|
280
|
+
`keine Statusdaten` and closes by saying that means unknown and not free —
|
|
281
|
+
measured, asking for Leipzig. What it refuses to do is leave them out or call
|
|
282
|
+
them free, which is the part that matters when you act on the answer.
|
|
283
|
+
- **The test for "which answers carry the ODbL line" was the question, and it
|
|
284
|
+
should have been the answer.** `SOURCES.md` said the line is carried "on every
|
|
285
|
+
answer whose input was an address, and on no other answer". `find_poi` asked for
|
|
286
|
+
a name and a city returns `_meta.sources: ["osm"]` and the line — so a reader
|
|
287
|
+
using that sentence to decide whether a share-alike obligation had arisen would
|
|
288
|
+
have concluded it had not. The rule is now stated as the platform states it, and
|
|
289
|
+
the page says to read `_meta.sources` on the answer rather than infer anything
|
|
290
|
+
from the shape of the question.
|
|
291
|
+
- **Our own § 4.6 offer was described more narrowly than it is.** It covers **both**
|
|
292
|
+
ODbL databases — addresses and points of interest, one file each under a single
|
|
293
|
+
licence notice — and the page named only the addresses. A recipient who derived
|
|
294
|
+
from POI results is entitled to the POI database.
|
|
295
|
+
- **The README promised car parking the public service does not answer.** Asked for
|
|
296
|
+
a `car_park` in Köln, Hamburg and Leipzig on 2026-09-27, every facility returned
|
|
297
|
+
was a lorry park or unclassified, and none carried occupancy. The row now says
|
|
298
|
+
lorry parking and a note says what is missing and why — the same over-claim as
|
|
299
|
+
the rail row, one row up, in the table this release rewrote to stop over-claiming.
|
|
300
|
+
- **The count of unconfirmed rows said two and there are three** — in a draft of
|
|
301
|
+
this release. 1.3.12 said **four**, counting a different set under a legend this
|
|
302
|
+
release replaced: under its own legend five rows said a source does not answer
|
|
303
|
+
today, and four was the number of `read` rows, which that legend defined as
|
|
304
|
+
"nothing uses it yet". The police-events row moved into the unconfirmed state
|
|
305
|
+
here, the changelog recorded the move and the count did not follow it. The station car-park source, in the server's register with no row
|
|
306
|
+
on the page at all, now has its `read` row.
|
|
307
|
+
- **The Code of Conduct sent a reporter without a GitHub account to a postal
|
|
308
|
+
address that does not exist** — in its first draft, in this release. The document
|
|
309
|
+
is new here (see `### Added`), so no published version ever sent anybody
|
|
310
|
+
anywhere. The Impressum's street and city are still visible placeholders; only
|
|
311
|
+
the e-mail address is real, and that is what the document names. Nothing linked
|
|
312
|
+
to it either; the README's Contributing section does.
|
|
313
|
+
|
|
6
314
|
## [1.3.12] - 2026-09-23
|
|
7
315
|
|
|
8
316
|
The bridge's code is unchanged since 0.0.9. This release replaces 1.3.10,
|
package/README.md
CHANGED
|
@@ -1,36 +1,51 @@
|
|
|
1
1
|
# viafrei
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/viafrei)
|
|
4
|
+
[](https://nodejs.org)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](#connect-in-one-line)
|
|
7
|
+
[](https://modelcontextprotocol.io)
|
|
8
|
+
|
|
9
|
+
**Live German traffic, rail, parking, charging, fuel, addresses and weather —
|
|
10
|
+
inside your AI assistant.** Ask in plain German or plain English and the answer
|
|
11
|
+
comes back from official open data, with the attribution the licence requires.
|
|
5
12
|
|
|
6
13
|
```
|
|
7
14
|
npx viafrei
|
|
8
15
|
```
|
|
9
16
|
|
|
17
|
+
**No account, no API key, no sign-up.**
|
|
18
|
+
|
|
10
19
|
## Ask your assistant things like
|
|
11
20
|
|
|
12
21
|
**Welche Züge fahren als Nächstes ab Hamburg Hbf?**
|
|
13
22
|
|
|
14
23
|
```
|
|
15
24
|
Abfahrten ab Hamburg Hbf (nächste 60 Minuten):
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
12:28 RJ 384 → Koebenhavn H, Gleis 5, pünktlich
|
|
26
|
+
12:29 ICE 7 → Karlsruhe Hbf, Gleis 14, pünktlich
|
|
27
|
+
12:33 ICE 774 → Kiel Hbf, Gleis 11, pünktlich
|
|
28
|
+
12:33 ME RB31 → Lüneburg, Gleis 13A-C, pünktlich
|
|
29
|
+
12:34 ICE 601 → München Hbf, Gleis 8A-F, pünktlich
|
|
20
30
|
|
|
21
|
-
Stand
|
|
31
|
+
Stand 12:23 · Quelle: Fahrplandaten: Deutsche Bahn AG, DB API Marketplace,
|
|
22
32
|
CC BY 4.0, bearbeitet (…) · Bahnhofsdaten: Deutsche Bahn AG,
|
|
23
33
|
DB API Marketplace, CC BY 4.0, bearbeitet (…)
|
|
24
34
|
```
|
|
25
35
|
|
|
36
|
+
That is a real answer, not a mock-up: it came back from
|
|
37
|
+
`https://mcp.viafrei.de/mcp` on 2026-09-27 at 12:23 Berlin time. **Two edits, both
|
|
38
|
+
named:** the two source URIs are shortened to `…` for the reason in the next
|
|
39
|
+
paragraph, and only the first five of the ten departures it returned are shown.
|
|
40
|
+
The server sends no "… and five more" line — the truncation is this page's, not
|
|
41
|
+
its.
|
|
42
|
+
|
|
26
43
|
Every answer ends with a line like that last one. It is the licence talking,
|
|
27
44
|
and it is meant to be shown to whoever reads the answer — see
|
|
28
45
|
[Using the data you get back](#using-the-data-you-get-back). **Reproduce the
|
|
29
46
|
line the server sends you, not this one:** it has been shortened here, and the
|
|
30
47
|
real one names each source's URL, which the licence requires you to keep.
|
|
31
48
|
|
|
32
|
-
**No account, no API key, no sign-up — ask and the answer comes back.**
|
|
33
|
-
|
|
34
49
|
ViaFrei is in its stabilisation and testing phase at the time of writing: live
|
|
35
50
|
and free, with some sources thinner than they will be, and the occasional tool
|
|
36
51
|
that answers slowly or not at all. **Tell us when that happens** —
|
|
@@ -48,27 +63,76 @@ the language you asked in, not translated from one house language:
|
|
|
48
63
|
- *Wo kann ich in Leipzig mit Typ 2 laden?*
|
|
49
64
|
- *Gibt es eine Unwetterwarnung für Freiburg?*
|
|
50
65
|
- *Brauche ich in Deutschland eine Umweltplakette?*
|
|
66
|
+
- *Funktioniert der Aufzug am Bahnhof Köln Messe/Deutz?* — lift and escalator
|
|
67
|
+
status, which is the difference between a station being usable and not
|
|
68
|
+
- *Wo ist die nächste Apotheke zum Leipziger Hauptbahnhof?*
|
|
69
|
+
- *What is at 52.5163, 13.3777?* — and the other direction: a street address to
|
|
70
|
+
coordinates
|
|
51
71
|
- *Tell me when the A8 reopens* — the server can watch a situation and tell
|
|
52
72
|
your assistant when it changes, so you need not keep asking. The watch lives
|
|
53
73
|
in the conversation that opened it: it reaches no inbox and no phone, and
|
|
54
74
|
it ends with the session. Three hours by default; 24 is the longest one can
|
|
55
75
|
be asked to run
|
|
56
76
|
|
|
57
|
-
|
|
58
|
-
pasted catalogue goes stale the first time a description changes on the server,
|
|
59
|
-
and this page is frozen inside a published tarball — it cannot be corrected
|
|
60
|
-
without a release. So there is one source of truth and it is the running
|
|
61
|
-
server: connect any MCP client and call `tools/list` for the catalogue as it
|
|
62
|
-
is today. (<https://viafrei.de> is the live national traffic digest, not a
|
|
63
|
-
catalogue.)
|
|
77
|
+
## What it covers
|
|
64
78
|
|
|
65
|
-
|
|
79
|
+
**Eighteen tools** as of this release. Grouped by the question they answer,
|
|
80
|
+
because the grouping is stable and a pasted catalogue is not:
|
|
66
81
|
|
|
67
|
-
|
|
68
|
-
|
|
82
|
+
| | |
|
|
83
|
+
|---|---|
|
|
84
|
+
| **Roads** | live incidents, jams and closures; roadworks ahead on a route; the state of one Autobahn end to end; **lorry** parking on the motorways, with occupancy wherever the operator publishes it — see the note below on car parks |
|
|
85
|
+
| **Rail** | next departures from any station with real-time delays and platforms; how punctual public transport is across a region right now (region-wide — never one line, trip or stop); whether a station's lifts and escalators are working right now |
|
|
86
|
+
| **Energy** | charging points by connector type and power; the cheapest fuel near a place |
|
|
87
|
+
| **Places** | a street address to coordinates and back; points of interest; what is at a coordinate, and what is near it — nationwide, all sixteen Länder (measured on 2026-09-27 — [how](SOURCES.md)) |
|
|
88
|
+
| **Weather** | official DWD severe-weather warnings for a place |
|
|
89
|
+
| **Watches** | ask once and be told when a situation changes, instead of asking again |
|
|
90
|
+
|
|
91
|
+
**One thing the parking row does not yet cover: car parks.** The licence for
|
|
92
|
+
station car parks is read and the loader exists, but nothing on the public service
|
|
93
|
+
answers with one today — asked for a `car_park` in Köln, Hamburg and Leipzig on
|
|
94
|
+
2026-09-27, every facility that came back was a lorry park or an unclassified site,
|
|
95
|
+
and none carried occupancy. So what you get from `find_parking` today is motorway
|
|
96
|
+
lorry parking. The table said "car and lorry parking" until this release; it was
|
|
97
|
+
the same over-claim the Rail row carried about disruptions, and it is corrected here rather
|
|
98
|
+
than left for a user to discover.
|
|
99
|
+
|
|
100
|
+
**No tool names, descriptions or schemas are pasted on this page, on purpose.**
|
|
101
|
+
This file is frozen inside a published tarball and cannot be corrected without a
|
|
102
|
+
release, so a copied catalogue would start rotting the first time a description
|
|
103
|
+
changes on the server. There is one source of truth and it is the running
|
|
104
|
+
server: connect any MCP client and call `tools/list`.
|
|
105
|
+
(<https://viafrei.de> is the live national traffic digest, not a catalogue.)
|
|
106
|
+
|
|
107
|
+
## Connect in one line
|
|
108
|
+
|
|
109
|
+
The product is a **hosted MCP server**. There is nothing to deploy, no key to
|
|
110
|
+
request and no quota to negotiate — point a client at it and the tools appear.
|
|
111
|
+
|
|
112
|
+
**Streamable HTTP — the address to use.** Most clients speak this, and they need
|
|
113
|
+
none of the rest of this page:
|
|
69
114
|
|
|
70
|
-
|
|
71
|
-
|
|
115
|
+
```
|
|
116
|
+
https://mcp.viafrei.de/mcp
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
claude mcp add --transport http viafrei https://mcp.viafrei.de/mcp
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**HTTP+SSE — if your client only speaks the older transport**, it is answered too,
|
|
124
|
+
so nobody meets a locked door. It is deprecated in the specification; prefer the
|
|
125
|
+
address above:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
claude mcp add --transport sse viafrei https://mcp.viafrei.de/sse
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**stdio — this package.** Some clients still speak only stdio, and **this bridge
|
|
132
|
+
is for those, and only those.** Node 22 or newer; nothing to install, because
|
|
133
|
+
`npx` fetches it when your client starts it. The same three lines fit any client
|
|
134
|
+
that takes an stdio MCP server, including the `.mcp.json` an IDE reads and
|
|
135
|
+
Claude Desktop's `claude_desktop_config.json`:
|
|
72
136
|
|
|
73
137
|
```json
|
|
74
138
|
{
|
|
@@ -81,25 +145,35 @@ client that takes an stdio MCP server, including the `.mcp.json` an IDE reads:
|
|
|
81
145
|
}
|
|
82
146
|
```
|
|
83
147
|
|
|
84
|
-
Restart the client and ask it one of the questions above.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
credentials,
|
|
148
|
+
Restart the client and ask it one of the questions above.
|
|
149
|
+
|
|
150
|
+
## Why build on it
|
|
151
|
+
|
|
152
|
+
- **One endpoint instead of a stack of integrations.**
|
|
153
|
+
[SOURCES.md](SOURCES.md) lists **twenty-one sources from sixteen publishers** —
|
|
154
|
+
Autobahn GmbH, the national access point, Deutsche Bahn, DWD, BKG, GeoNames,
|
|
155
|
+
OpenStreetMap, MTS-K and the rest — each with its own format, its own release
|
|
156
|
+
rhythm and its own licence. They arrive here as one protocol and one set of
|
|
157
|
+
tools. (Count them in that table; one of the sixteen is us.)
|
|
158
|
+
- **The licence work is done and it travels with the answer.** Every result
|
|
159
|
+
carries the attribution line its sources require, and the ones with conditions
|
|
160
|
+
attached say so in the result itself — share-alike is flagged, and the MTS-K
|
|
161
|
+
purpose limit arrives as a sentence you are meant to show. You are not left to
|
|
162
|
+
work out what you owe whom.
|
|
163
|
+
- **It answers in the language of the question**, German or English, rather than
|
|
164
|
+
translating out of one house language.
|
|
165
|
+
- **It is a transport shim and nothing more.** The bridge holds no data and no
|
|
166
|
+
credentials, writes no file outside the OS temp directory, and makes no
|
|
167
|
+
decision about any answer. No telemetry, no analytics, no usage counter, no
|
|
168
|
+
update check — see [What it does not do](#what-it-does-not-do).
|
|
169
|
+
- **Failures are machine-readable.** One line on stderr and a distinct exit code
|
|
170
|
+
per cause, so a supervisor can tell "your network is down" from "you typed the
|
|
171
|
+
flag wrong" without parsing English.
|
|
172
|
+
- **Ask once, be told when it changes.** A watch turns polling into a
|
|
173
|
+
notification for as long as the conversation lives.
|
|
174
|
+
|
|
175
|
+
Free while ViaFrei stabilises, and the limits that exist are written down on this
|
|
176
|
+
page rather than discovered in production.
|
|
103
177
|
|
|
104
178
|
## Configuration
|
|
105
179
|
|
|
@@ -110,9 +184,30 @@ credentials, and it makes no decision about any answer.
|
|
|
110
184
|
| `--timeout <ms>` | per-request timeout, default 30000. The event stream is never timed out |
|
|
111
185
|
| `--version`, `--help` | print and exit |
|
|
112
186
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
187
|
+
Many MCP clients can only pass an `env` block, not arguments, so every option
|
|
188
|
+
has an environment variable too:
|
|
189
|
+
|
|
190
|
+
| variable | same as |
|
|
191
|
+
|---|---|
|
|
192
|
+
| `VIAFREI_MCP_URL` | `--url` |
|
|
193
|
+
| `VIAFREI_MCP_HEADER` | `--header`. Several headers are separated by a **newline**, which can never appear in a header name or value, so nothing you might need to send is unrepresentable — a comma, a semicolon and a space all occur inside real header values |
|
|
194
|
+
| `VIAFREI_MCP_TIMEOUT_MS` | `--timeout` |
|
|
195
|
+
|
|
196
|
+
A flag wins over the variable; the variable wins over the built-in default. A
|
|
197
|
+
`--header` of the same name replaces one from the variable, and a `--header` of
|
|
198
|
+
a different name is added alongside it.
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"mcpServers": {
|
|
203
|
+
"viafrei": {
|
|
204
|
+
"command": "npx",
|
|
205
|
+
"args": ["-y", "viafrei"],
|
|
206
|
+
"env": { "VIAFREI_MCP_HEADER": "Authorization: Bearer …" }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
116
211
|
|
|
117
212
|
**There is no self-hosted ViaFrei.** The server is a hosted service, so `--url`
|
|
118
213
|
is not a way to run your own — it is there for a proxy or gateway in front of
|
|
@@ -125,9 +220,13 @@ want.
|
|
|
125
220
|
One line to stderr and an exit code that says what happened. No stack traces:
|
|
126
221
|
|
|
127
222
|
```
|
|
128
|
-
viafrei: cannot reach https://
|
|
223
|
+
viafrei: cannot reach https://example.invalid/mcp: host not found (DNS) (ENOTFOUND) - check your network connection; --url only if you relay through a proxy
|
|
129
224
|
```
|
|
130
225
|
|
|
226
|
+
That line is copied from a run, exit code 3. The same text also goes back to the
|
|
227
|
+
client as a JSON-RPC error, so an assistant can say what went wrong instead of
|
|
228
|
+
going quiet.
|
|
229
|
+
|
|
131
230
|
| exit | meaning |
|
|
132
231
|
|---|---|
|
|
133
232
|
| `0` | clean shutdown (the client closed stdin, or sent SIGINT/SIGTERM) |
|
|
@@ -137,6 +236,27 @@ viafrei: cannot reach https://mcp.viafrei.de/mcp: DNS lookup failed (EAI_AGAIN)
|
|
|
137
236
|
| `4` | the endpoint answered and this cannot continue: it refused (the line names the HTTP status), it forgot the session, it answered with something that is not MCP, or it redirected to another origin |
|
|
138
237
|
| `5` | protocol version mismatch; the line names the version the server speaks |
|
|
139
238
|
|
|
239
|
+
**A slow call may be a retried call.** A `429`, `502`, `503` or `504` is tried
|
|
240
|
+
again **once** — after the delay the server asked for in `Retry-After`, or 250 ms
|
|
241
|
+
when it asked for none. A connection that fails outright rather than answering
|
|
242
|
+
(reset, broken pipe, socket or connect timeout) is likewise tried once more, after
|
|
243
|
+
the fixed 250 ms; there is no header to read on that path. Two cases are
|
|
244
|
+
deliberately not retried, because waiting would be worse than answering: a `429`
|
|
245
|
+
carrying no usable `Retry-After`, and any delay the server asks for that is longer
|
|
246
|
+
than your timeout. Both come straight back as the ordinary failure line with the
|
|
247
|
+
status in it. An event stream is never retried.
|
|
248
|
+
|
|
249
|
+
**Your timeout bounds each request, not the call.** It is attached per HTTP
|
|
250
|
+
request, so the second attempt gets a fresh one, and so does each hop of a
|
|
251
|
+
same-origin redirect. What you can rely on is per request: no single request
|
|
252
|
+
outlives the timeout, and a delay longer than the timeout is never waited out at
|
|
253
|
+
all. What follows from that is the arithmetic: two attempts plus a delay that may
|
|
254
|
+
itself be as long as the timeout is a worst case of about **three times** what you
|
|
255
|
+
set, and more than that if the endpoint redirects, since every hop is bounded
|
|
256
|
+
separately. **So no setting here caps the whole call** — if you need a hard
|
|
257
|
+
ceiling, enforce it on your side and treat the timeout as the per-request bound it
|
|
258
|
+
is.
|
|
259
|
+
|
|
140
260
|
An established session is allowed to wobble — a dropped event stream is a
|
|
141
261
|
warning, not an exit, and the bridge reconnects. It is not allowed to be dead
|
|
142
262
|
in silence: several failures in a row with nothing succeeding in between end
|
|
@@ -186,9 +306,15 @@ This is said plainly so nobody spends an evening looking for the server code.
|
|
|
186
306
|
|
|
187
307
|
## Contributing
|
|
188
308
|
|
|
189
|
-
Yes, please — see [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
190
|
-
|
|
191
|
-
|
|
309
|
+
Yes, please — see [CONTRIBUTING.md](CONTRIBUTING.md) and
|
|
310
|
+
[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Issues and discussions are open. The
|
|
311
|
+
bridge is small and self-contained, which is exactly what makes it a reasonable
|
|
312
|
+
thing to send a first patch to.
|
|
313
|
+
|
|
314
|
+
**One issue template is worth knowing about before you need it:** *the answer was
|
|
315
|
+
wrong or useless*. A tool that fails is something the server sees; a tool that
|
|
316
|
+
answers confidently with the wrong thing is not. That report is the one thing we
|
|
317
|
+
cannot get any other way.
|
|
192
318
|
|
|
193
319
|
## Security
|
|
194
320
|
|