Bastion 1.2.1
Version 1.2.1 ·
Fixed
A modern client could handshake cleanly and then register no tools at all. The 2026-07-28 revision has every list result declare how long it may be cached and by whom, and a client validates the whole result against that schema — so an absent
ttlMsis not weaker caching, it is a discarded tool list. Claude Code 2.1.251 reports it asInvalid result for tools/list: ttlMs expected number, received undefined. Modern list results now carry both fields; legacy ones still carry neither, because handing a 2025-11-25 client a field from a later revision is the same mistake pointing the other way.The scope is
privaterather thanpublicbecause a Bastion listing is per profile, not per server:allowWritesdecides which tools come back, so a shared cache would be free to serve the read-only profile's answer to the writing one.The same symptom from the other end: a child's
listChanged: truewas passed straight through. Bastion cannot honour it, and never could — one supervised instance serves several clients, so there is no single client alist_changedbelongs to, andSupervisor.receiveddrops those notifications on purpose. A modern client that believed the advertisement openedsubscriptions/listen, got-32601, and dropped the whole connection rather than that one subscription. It is now advertised asfalse, andresources.subscribeis withdrawn for the same reason. An honestfalsecosts a notification nobody was going to receive; the hopefultruecost every tool on the server.The sixty-second TTL above is what stands in for it: the client re-lists on its own schedule rather than being told when to.
Typing a profile name allocated a callback port for every prefix of it.
ProfileEditorcalled the function that decides the port from its view body, once per keystroke, against a half-typed name — so typingolouvleft a profile directory and a burnt port behind foro,ol,oloandolou. Worse, the first keystroke claimed the server's documented default, which pushed the profile the user actually meant onto a port their upstream app had never heard of, and OAuth logins came backinvalid redirect_uri parameter. Reading and deciding are now separate calls: a view asks, and only a spawn decides.