Repository navigation
Releases: mgcrea/bastion
Release list
Bastion 1.32.0
Large servers load their tools on demand. In Cursor, VS Code, Codex and LM Studio, Bastion now swaps a big server's tool list for search, describe and call by itself, so each connection costs far fewer tokens. The per-server picker is gone.
Changed
-
Loading tools on demand is on by default, and Bastion decides which servers it applies to. The per-server Default/On/Off picker is gone. Every server goes through the size check that already existed: a listing is replaced by Bastion's search, describe and call tools only when it has at least twice as many tools as they are, and costs at least twice as many tokens. So App Store Connect's listing is replaced and Context7's two tools are not, and a server that grows past the bar starts being fronted with nobody touching anything. The one switch left is in Settings › General.
On a Mac where that switch was never touched, it is now on. Claude Code and Claude Desktop are unaffected, because they load tool schemas on demand themselves and Bastion never fronts them. Cursor, VS Code, Codex and LM Studio are, and the trade is the one the switch has always carried: their per-tool approval rules then see
bastion_call_toolfor a server's reads andbastion_call_write_toolfor its writes, rather than each tool. Turn the switch off in Settings › General, or set a client to "defers schemas" in its own pane, to keep the full listings there. Anybody who had turned the switch off keeps it off.A choice made per server before this release is dropped, on or off. Bastion's own MCP server follows:
list_servers,get_serverandlist_profilesno longer reportlazy_tools,statusreports the switch instead, andupsert_profilerefuses alazy_toolsargument and says where the switch is rather than ignoring it.
Fixed
- A failing server's red mark lines up with the green dots. It sat a few points off the dots in the sidebar rows around it. Every status mark there now centres on one column.
Bastion 1.31.0
A failing remote server now shows red. When calls to a remote server start failing, the sidebar, the menu bar and the Activity pane turn red and say why. Bastion's embedded Node runtime also moves to 24.22.1 for October's security fixes.
Added
- A server on this Mac looks like one. A remote server at
127.0.0.1or::1shows a computer in the sidebar, labelled "Endpoint on this Mac", rather than the cloud of a server somebody else runs.
Fixed
- A remote server whose calls fail no longer shows green. Bastion counted a remote server as running for as long as it held a session with it, so a profile whose refresh token the vendor had revoked read green while every call through it failed. A failed exchange now turns the server's sidebar row, its profile, its Activity row and its dot in the menu bar red, with the reason, and the next call that is answered turns them back.
Security
- The embedded Node runtime is 24.22.1, up from 24.20.0, for Node's October 2026 security release: an out-of-bounds write in the Brotli decoder, three ways around TLS hostname and IP checks, and HTTP keep-alive response confusion, with undici, OpenSSL and the CA bundle updated. Every server Bastion downloads runs on this runtime, holding its profile's credentials. npm moves to 11.19.1.
Bastion 1.30.0
Manage a server from the sidebar. Right-click any server in the sidebar to edit it, switch it off or remove it, without opening its page first.
Added
- A right-click menu on each server in the sidebar, with Edit…, Enable or Disable, and Remove Server…. Each item appears only where the server's own page offers it: Edit… on a server you added yourself, Remove Server… on anything but Bastion's own server. Removing still asks first, and says what goes with the server: its profiles, their credentials in the Keychain and its downloaded code.
Bastion 1.29.0
Rename a profile, and let Shopify write. A profile can take a new name and keep its credentials, sign-ins, workspaces and history, with every wired client following. Shopify can now write behind the writes switch, and Shopify Dev joins the catalog.
Added
-
Rename a profile. Rename… on a profile's row, and
rename_profileon Bastion's own server, give a profile a new name. Its credentials and OAuth sign-in, the state its server keeps (a child's own login, its callback port), its workspaces and its usage history move with it, and every client Bastion configured is rewired to the new endpoint. The client's tool names change with the entry, so permission rules naming the old ones stop matching. The audit log keeps the old name, since its lines are hash-chained. -
Shopify can write, behind the writes switch. From mcp-shopify 0.7.0, a profile with writes on can set and delete metafields, update a product's and its variants' content, and adjust or set inventory. None of these deletes a product.
SHOPIFY_ALLOW_RAW_MUTATIONSaddsshopify_graphql_mutation, which runs any mutation the app's access scopes allow, deletes and refunds included. The switch decides which tools the server offers, not what the credential can do, so grantwrite_*scopes only to an app whose profile is meant to write. A server still on 0.6.0 stays read-only. -
Shopify Dev is in the catalog: the server from Shopify's AI Toolkit that searches shopify.dev and validates generated GraphQL, Liquid and UI code, with no credential. It reports usage to Shopify by default, and its profile offers
OPT_OUT_INSTRUMENTATIONto stop that.
Bastion 1.28.0
Claude Code can be held to Bastion's servers. A switch on each Claude Code profile blocks the MCP servers that plugins and claude.ai connectors add, and keeps Bastion and the ones you set up. Remote servers and client wiring are sturdier too.
Added
- Only Bastion. Each Claude Code client's pane has a switch that writes
disableClaudeAiConnectorsand anallowedMcpServerslist into that profile'ssettings.json: Bastion's endpoint, plus every server already in its.claude.jsonthat Bastion did not write. Servers added by plugins (the ones synced from a claude.ai account included), claude.ai connectors and a repository's.mcp.jsonare then filtered out. Only those two keys change, the rest of the file is kept byte for byte, a backup is written first, and a file with comments or a trailing comma is left alone. Entries you add to the list by hand stay. The list is refreshed when a server is added or the port moves, and lifted when the client is unwired; turning it off removes only what Bastion added, and asks before discarding a hand edit. It is hygiene rather than a boundary: a project's own settings can widen the list.set_client_lockdownon Bastion's own server does the same, andlist_clientsreports each Claude Code row's state.
Changed
- "Measured …" in a remote server's pane dates from when its context figure last changed, as it already did for a server Bastion runs, rather than from the last call.
Fixed
-
A first call can no longer outrun turning writes off. A call racing the edit could start the server from the old profile and keep writes on for up to 30 minutes. Turning a profile's capture off, or a server's Load tools on demand, likewise reaches a server that is already running instead of waiting for its idle stop, so a running server no longer goes on logging arguments and results.
-
Editing a custom server keeps what the form does not show. A server handed over by
bastion://add-serverlost its list of write tools, so those were forwarded with writes off; a server set to load its tools on demand went back to Default; and the edit reached a running server only after a relaunch. The Write gate field is now offered only for a server Bastion runs, since a remote one has nowhere to put it. -
Remote servers recover from an expired session or token on every request. After an upstream session expired, every read on a writes-off profile was refused with "writes are off", and a token revoked before its expiry broke the profile even with a refresh token held. A refresh token is now sent only to the authorization server that issued it, a token whose
expires_inarrives as a string is refreshed before it expires, a lowercasebearerchallenge is read, and an OAuth sign-in no longer times out behind a browser's idle speculative connection. -
Client wiring.
~/.claude.jsonis no longer reformatted, and its backup replaced, on every launch. A workspace-scoped profile's rewire keeps a server you disabled disabled and keeps your timeout, as the global block already did. A repository reached through a symlink is filed under its real path, where Claude Code reads it. Two workspace-scoped profiles that would share a key in a folder are refused rather than taking turns. A string with a CRLF or DEL no longer writes an invalid Codexconfig.toml. -
Moving between two servers or two clients builds a fresh pane. A profile editor open on one server could survive a menu bar reveal of another and save its fields there.
-
A crashed server is no longer counted as running. Opening its profile editor restarted it,
list_serversreported it running, and a failed handshake could hold off an update's relaunch. -
list_profilesno longer returns a value typed before its variable became secret. -
A gateway port outside 0–65535 in Settings is read as unset; it crashed every launch.
-
A latency percentile is never reported above the maximum beside it.
-
Stats and a server's pane say three Bastion tools, or four where a server's writes can be told apart, rather than always four.
Bastion 1.27.0
Other apps can hand their MCP server to Bastion. An app on this Mac can now offer Bastion the MCP server it runs, and Bastion adds it only once you confirm, with its token kept in the Keychain.
Added
- A server another app hands over. An app serving MCP on this Mac can open a
bastion://add-serverlink (swift-mcp-kit 1.2'sBastionLink) to offer Bastion its endpoint and bearer token. Nothing is added until a sheet shows which app asked, the endpoint, the URL it replaces when the server is already listed, and which of its tools change data; you pick the profile (local, or the server's existing one) and whether writes are allowed, off by default. The token goes into the Keychain and is sent asAuthorization: Bearer. Only127.0.0.1and[::1]endpoints are accepted, and a link cannot take the name of a catalog server or Bastion's own, or replace a server that runs a package.
Changed
- Yahoo Finance's writes switch covers portfolios and trades. From 0.5.0 the server opens create, add, remove and delete to manual portfolios and adds tools to record, edit and delete trades, all behind the same switch, which the profile editor described as covering watchlists only. Removing a position or a portfolio that holds lots or transactions is refused unless the call asks for that history to go with it.
Bastion 1.26.0
Yahoo Finance watchlists, behind the writes switch. Yahoo Finance can now change a signed-in account's watchlists, and Bastion keeps those tools hidden until a profile turns writes on. The menu bar panel now gets out of the way of the window it opens.
Fixed
-
Yahoo Finance's watchlist tools are writes. The server's newer versions add four tools that change a signed-in account's watchlists (create, add, remove and delete), and the catalog still described it as having nothing it could change: on 0.3.0 they ran as reads, without confirmation, and from 0.4.0 no profile could turn them on. They now follow the profile's writes switch, and need the cookie of a signed-in finance.yahoo.com tab; the crumb is derived from that cookie, so it rarely needs setting.
-
The menu bar panel closes when it opens a window. Settings, Logs, About, What's New, a server's row and the licence notice's key button could each leave the panel open over the window they had just opened, since opening one of the app's own windows never closes it. The server rows also highlight under the pointer, where they used to give no sign of being clickable until the click.
Bastion 1.25.0
One set of Agent Skills, linked into every client. Bastion links the skills in folders you choose into Claude Code, Codex, Cursor and the other clients that read them, and leaves alone what it did not create. Client wiring and supervision are safer too.
Added
-
Skills. Settings → Skills links Agent Skills from folders you choose into
~/.agents/skills(Codex, Cursor, VS Code Copilot, Gemini CLI and others) and every Claude Code config folder, or, per workspace, into each repository's.claude/skillsand.agents/skills. Bastion only ever creates symlinks and never writes into a source; anything it did not create is left alone and shown, a repository link another tool made into a source included, and broken links it owns are removed. Repository links are kept out of git through.git/info/exclude. A description over the Agent Skills standard's 1,024 characters but within Claude Code's 1,536 is still linked, with a warning that clients reading~/.agents/skillsmay skip it. Claude Desktop's pane lists the account skills synced to this Mac and exports a ZIP for any that are missing. Five new tools on Bastion's own server manage the same. -
Yahoo Finance is in the catalog: prices, fundamentals, financial statements, holders, options, news and analyst ratings, with no account or key, and nothing it can change. Apple Ads is listed too, marked not published until its package is.
Fixed
-
-autoWireClients YESnow turns automatic wiring back on in a Debug build; it was ignored. -
A store file that will not decode is kept, not emptied. The server list, profiles, skills and workspaces each keep a
.unreadablecopy and refuse to save over it, and client wiring stands down until it reads again, so one bad edit no longer costs the whole list. -
A rewire changes only what Bastion owns. It replaces the keys Bastion writes in its own entries and leaves the rest of each entry alone, writes through a symlinked config instead of replacing the link, reads a config that is empty mid-write again, and never replaces a client token the Keychain failed to read. A Codex
config.tomlshape the splice would corrupt is refused rather than rewritten.-trustProfilesForStaleEntries YESis now read. -
Supervised servers. A child that fails its handshake, or is stopped, is sent SIGKILL if it outlives a grace period, so none is left running; a server that keeps crashing is restarted with a growing backoff after one immediate retry; frames from concurrent clients no longer interleave on a child's stdin; a client's cancel reaches the child under the id the child was given; stderr is redacted line by line; and a streamed reply sends no progress after its result.
-
Writes off holds on a fresh server. A
tools/callthat arrives before the server's tool list was ever fetched is judged against the catalog, and refused when the catalog cannot be read. -
Remote servers refuse a reply whose peer address was never observed, and one that was stopped no longer starts a request.
-
Bastion's own server.
add_custom_serverrefuses a catalog id, or an id profiles already hold credentials for, andserver_statstotals count only the servers asked about. -
The audit log verifies as truncated, not tampered, once retention has pruned its oldest segments. Retention applies at launch and when the setting changes, a torn last record no longer breaks the chain, a reply that arrives late is still recorded, queued records are written before quit, and the export is the text that was hashed.
-
Skills. Repository links are kept out of git in submodules and worktrees too, a link to a skill that became invalid is kept and shown instead of removed, a link another tool re-pointed while Bastion was applying a change is put back rather than replaced, and a skill's ZIP leaves
.git,.envand other hidden entries out. -
A licence key a mail client wrapped across lines is accepted, an OAuth sign-in keeps waiting when a stray request reaches its callback, the Log pane follows new entries, the chat drops whole turns when it trims its history, a stuck update check clears, and relaunching to update stops the gateway first.
Bastion 1.24.0
Added
-
Xcode is in the catalog. Xcode 26.3 and later ship their own MCP server,
xcrun mcpbridge: read and edit a project, build, run and test it, render previews, and drive a simulator or device. Bastion runs and supervises it like any other server, with nothing to download. Xcode asks you to approve the agent the first time it opens a project, and behind Bastion that agent is Bastion itself, so one approval covers every client. With a profile's writes off, the tools that change a project on disk or run arbitrary code (RunCodeSnippet,InvokeDebuggerCommand) are hidden; building, running and testing stay available. -
A third kind of server: a command that ships with the Mac. Catalog entries can now name a program directly under
/usr/bin, with fixed plain-word arguments. Only the catalog can: a custom server is still an npm package or a URL, so nothing a person types and nothing a client sends can name a command line.
Fixed
- The website no longer lists Playwright, Supabase, Netlify and Apify as read-only. They gate writes by tool name, which the site's copy of the rule did not count. The server detail pane now describes that gate for them too, where it used to only for remote servers.
Bastion 1.23.0
Added
- Workspaces scope profiles to folders. A workspace is a set of folders and the profiles that belong there. A profile in one is left out of every client's global list and written into Claude Code's per-folder project blocks instead, so a session in an
rgisrepository sees thergisservers and nothing from another account. A folder inside a git repository means the whole repository, its subfolders and its worktrees, which is how Claude Code itself files project blocks; a parent folder is expanded to every repository below it. Managed in Settings → Workspaces, or with the newlist_workspaces,upsert_workspaceandremove_workspacetools. Claude Code only for now.
Fixed
- Usage stats count what you asked for, not what the client's SDK sent on its own. The handshake and listing methods a client sends by itself on every connect (
initialize,tools/listand the other listings,ping,logging/setLevel) were recorded as calls, sotools/listtopped every ranking, the call totals measured how often clients reconnected, and the median latency was mostly that of a cached listing. They are no longer recorded, and days already on disk are filtered when read. The per-client totals have no method to filter on, so they keep counting old handshakes until those days age out of the window.