Releases: btsouth/toolport
Release list
Toolport v1.18.0
Toolport 1.18.0 adds a native GTK shell for Arch and other current-GTK Linux
distributions, makes the destructive-tool setting hide what it refuses, and
fixes catalog entries for servers you host yourself. The .deb and AppImage
builds are unchanged and remain the Linux download everywhere else.
Added
-
A native GTK shell for Arch, Omarchy, and other current-GTK distributions. Not a
rewrite of the Linux app for every distribution: this is a second Linux build,
packaged for Arch astoolport, and it needs GTK 4.10 or newer and libadwaita 1.4 or
newer. That rules out Ubuntu 22.04 and Debian 12, which keep the existing.deband
AppImage, as does anywhere the native package is not available. Where it does run it
is GTK4/libadwaita rather than a web view: it follows the active Omarchy palette,
behaves as a regular Wayland window under Hyprland, and leaves tiling and geometry to
the compositor. Everything the cross-platform app does is here - servers, profiles and
secrets, client detection and connection, pending approvals with desktop
notifications, Activity, Catalog and starter stacks, Playground, Rules and project
rules, Teams, agent permissions and the activity recorder, the shared HTTP endpoint,
diagnostics, and first-run setup. Updates come from pacman or the Omarchy update
flow; there is no self-updater in this build.Only one Toolport should run at a time. Both builds read the same
~/.config/Toolport, and only one process can hold the approval broker's endpoint, so
the second to start shows an empty approval queue while prompts go to the first - it
now says so rather than looking idle. On Arch thetoolportpackage conflicts with
toolport-bin, so the two cannot be installed together there. Upgrading from the earlier preview package retires its
autostart entry and URL handler on first run, and carries launch-at-login across
rather than switching it off. -
Agent permissions has a "Never run sudo" preset. The other presets each block one
destructive act; none covered privilege escalation, which is the case where an agent
can perform any of them as root. Deniessudoandsudoedit. -
Activity says which part of a tool definition drifted. Pins now carry per-field
fingerprints alongside the whole-definition one, so a drift row can name the
description, the input schema, or the annotations instead of only reporting that
something changed. The whole-definition fingerprint is still what quarantine trusts. -
Server and client logos. Catalog entries, registry rows and client cards show the
real mark, with a clean fallback where there is no asset.
Changed
-
Blocking destructive tools now hides them from the catalog as well as refusing the
call. The setting has always promised to "hide and reject", and the call was always
refused, but the catalog kept listing the tools because it is served from a cache
written under whatever policy was in force at the time. If you run with that setting
on, expect those tools to disappear from what your agents can see. Blocking behaviour
is unchanged. -
Clients are grouped by whether they are connected. Available to connect sits above
Connected to Toolport, each with its own count, instead of one mixed list.
Fixed
-
Adding a self-hosted catalog entry created a server that could never connect.
n8nandLangfusedescribe an endpoint you host yourself, so the catalog carries a
hint rather than a URL. A one-click Add wrote an HTTP server with neither a command
nor a URL. Both Add paths now open the server editor prefilled, with the hint as the
URL placeholder rather than as text that could be saved as-is, and the underlying add
refuses an entry with no endpoint so no other caller can recreate it. -
A debug build reaped the production install's gateways. The stale-gateway sweep
did not tell a-devdata directory's processes from the real install's, so running a
development build killed gateways that were serving live clients mid-call. -
Agent rules: the "no rules file" line no longer lumps three different situations
together. Cursor and GitHub Copilot CLI have no global rules file but do read project
rules, so the Clients section now points them at the Projects section instead of calling
them unsupported; Claude Desktop is named as the chat app, with Claude Code inside it
covered by the Claude Code row; only clients with no rules file anywhere still say to
paste rules in by hand.
Toolport v1.17.0
Toolport 1.17.0 brings one permission policy to Claude Code and Cursor, makes
agent rules project-aware and safe around hand edits, hardens approval and Teams
trust boundaries, and fixes fresh Codex gateway startup on Omarchy.
Security
-
A process that bound the approval broker's endpoint after the app had gone could
approve gated calls in its place, and be handed the arguments first. The gateway
dialed whateverapproval-endpoint.jsonnamed and believed whatever came back. The
descriptor survives a crash or a force-kill, and nothing authenticated the peer that
answered: the literal bytes"approved"were a complete decision. Because the
request is written before the reply is read, such a peer also received the call's
real arguments, including the rehydrated values behind a PII release. The gateway now
opens every dial with a random challenge that the broker must answer with an
HMAC-SHA256 proof of the shared token, and sends nothing until the proof checks out;
a peer that cannot read the owner-only descriptor cannot produce it, so it sees no
request and its answer is never read. The failure is reported asunreachable, so a
restarted app is still found on the re-read, and it is still fail-closed. On Unix the
broker also listens on a socket file in a0700directory under the data dir, which
a current gateway prefers, so such a peer cannot even connect on that path; the
loopback listener stays (and is all there is on Windows), and the challenge protects
both the same way. A gateway from before this change still reads only the loopback
address, still reaches the broker, and is still answered. (SBS-867)What this does not claim: a process running as the same user as Toolport can read
the descriptor andregistry.jsonalike, and can switch human approval off directly;
that is a sandboxing question (SBS-185), not an authentication one. -
Teams: a member's consent to a review server no longer follows the server's id
when its command changes. Team servers that run a local command or point at a LAN
address arrive off and stay off until the member reviews and enables them; that
enablement was carried across syncs by id alone, so an org config that kept the id
and swapped the command re-enabled the new command with no re-consent, and a public
remote server that had been auto-enabled (no member action at all) became a local
command that ran on the next gateway start. Consent is now bound to what the member
enabled - transport, command, args, env keys, cwd, url - and is carried over only
when the new entry matches; a changed definition arrives off and is counted in the
"needs review" notice, which now counts only the servers that are actually off rather
than every review server including the ones already consented to. A rename or a tool
allow-list change does not re-prompt. (SBS-1017) -
A form elicitation relayed from an MCP server now says which server is asking.
When a server asks the user for input (MCP elicitation in form mode, whether as a
moderninput_requiredresult or a legacy server-initiated request), the client
renders it in the same chrome as Toolport's own approval prompts, so a server-authored
"your session expired, re-enter your token" was indistinguishable from a genuine one.
Toolport now appendsToolport source: the "<server>" MCP server (not Toolport)to the
message, the form-mode counterpart of the verifiedToolport destination:line URL
mode already carried; a server that pre-writes that line to name itself as something
else has it replaced. (SBS-891)
Added
-
Agent permissions: the same rules now reach Cursor, through a guard hook. Cursor has
hooks but no settings-level rule list, so Toolport installs a small guard into
~/.cursor/hooks.jsonthat runstoolport-gateway --toolport-guard cursorbefore a
shell command runs, an MCP tool is called, or a file is read, and answers allow / deny /
ask from the permission rules you already wrote -Bash(...)for shell commands,
Read(...)for file reads,mcp__server__toolfor MCP tools, with Claude Code's
matching semantics (wildcards, the word-boundary trailing*, compound commands
judged per part, wrappers stripped). Three modes: Off (default), Observe (installed,
every call recorded in Agent activity with what the rules would have decided, nothing
blocked) and Enforce (Never and Ask first take effect; Ask uses Cursor's own prompt;
the hook is installed fail-closed). Only Toolport's own entries are ever added or
removed; a Preview shows the bytes first.Edit/WebFetchrules have no Cursor
event and the tab says so. See
docs/agent-permissions.md. (SBS-1059) -
Agent permissions: rules Claude Code enforces on its own native tool calls. A new
Agent permissions tab holds a policy in Claude Code's own rule syntax (Bash(rm -rf *),
Read(./.env),WebFetch(domain:...),mcp__server__tool, each set to never / ask
first / always allow) and writes it into every Claude Code profile'ssettings.json
underpermissions, so Claude Code itself refuses, or asks before, a matching shell
command, file read or edit, web fetch or MCP call - on every call, whatever any hook
says. Off by default and empty by default; presets (never delete recursively, never
force-push, ask before any push, never read.envor SSH keys) are one-click adds.
Only the rule strings Toolport added are ever added or removed, so a rule you already
had in the file stays; a profile that appears later picks the policy up at the next
start. Claude Code only for now, and the tab says so. See
docs/agent-permissions.md. (SBS-1058) -
Agent rules: start a new set from a rules file you already have. The tab opened on an
empty editor, and the first thing it asked of someone with a~/.claude/CLAUDE.mdor a
~/.codex/AGENTS.mdwas to retype it. Start from a file lists the rules files the
detected clients already read (the user's own text: sentinel-block targets such as
AGENTS.mdandGEMINI.md, the other.mdfiles in a rules directory, and Claude Code's
~/.claude/CLAUDE.md), with a file picker for anything else, and creates a new set from the
file's text - not selected for you, since selecting applies. The file is read and never
written, anything Toolport had
written into it is left out and the import says so, and a remainder that still looks like
Toolport's markers is refused up front. See
docs/agent-rules.md. (SBS-1035) -
Agent rules: apply a rule set inside a project folder. A new Projects section
registers a folder you choose (Toolport never scans for repositories) and applies a set to
the files the repo's clients read there: the rootAGENTS.md(Codex, Cursor, Copilot CLI,
Kiro, Roo Code, Cline, Devin, Goose, Pi, Oh My Pi),GEMINI.md(Gemini CLI, Antigravity)
and.claude/rules/toolport-rules.md(Claude Code, VS Code), each offered only when a
detected client reads it and each naming the clients it reaches. Files are switched on per
project and written only by that project's own Apply, never at startup; switching a
file off or removing the project removes what Toolport wrote; your repo's own text is left
byte-for-byte; a per-file Preview shows the exact bytes first. This is what brings Cursor
and GitHub Copilot into agent rules, which have no user-level file Toolport could write.
Zed is left out on purpose (it reads only the first of nine candidate files, so Toolport
could not honestly report Applied). See
docs/agent-rules.md for the per-client
citations. (SBS-1037) -
Agent rules: a block you edited by hand in a client's file is no longer silently put
back. Toolport wrote its block intoAGENTS.md(or its own file in a rules directory),
you tuned it there, and the next startup or the next time you toggled a client Toolport
rewrote it from the set with no warning. Now a block written for the current set revision
whose body differs on disk reads Edited on disk instead of "Not applied yet"; every
automatic apply (startup, saving, switching sets, toggling clients) leaves it alone; and the
row offers View diff, with Pull into set (the file's version becomes your unsaved
draft) and Overwrite this file (the set goes back over that one file; Re-apply does it for every switched-on client).
A change to the set itself is a newer revision and is written as before. Team
instructions are unchanged: org rules stay authoritative over a member's edit.
(SBS-1036)
Fixed
-
A gateway launched by Codex no longer stops before answering its first call.
Codex starts each MCP child as a process-group leader, which made the gateway's
terminal-session detach fail; its login-shell PATH probe could then receive SIGTTIN
as a background group and stop the whole gateway. Normal Unix startup now handles
that launch shape and leaves the client's terminal session before any probe or
downstream spawn. (SBS-1063) -
Linux gateways launched without the desktop session environment can reach the
Secret Service again. Orca and some AI clients start MCP children with only
HOMEandUSER; on Omarchy that left the gateway without a D-Bus address, so
vaulted servers such as Linear appeared in the cached catalog but every call failed
withno route for tool. When the address is absent, the gateway now recovers the
current user's standard systemd session bus after verifying the runtime directory
and Unix socket are owned by that user. Explicit environment values are left alone.
(SBS-1060) -
Team Instructions: losing a write race no longer deletes the winner's block. When
the team changed or was cleared while an apply was writing its files...
Toolport v1.16.0
One Linux download that works on every GPU
Toolport's AppImage opened a grey, empty window on Arch, Omarchy, Manjaro and other rolling distros — but only on Mesa, which is to say AMD and Intel graphics. NVIDIA machines were fine. That split made it look like an AMD bug, and 1.15.0 shipped advice to install a native package instead and pick your download by driver.
It was never about the GPU. 1.16.0 fixes it at the source: one AppImage, running on Omarchy, Arch, Manjaro and EndeavourOS across AMD, Intel and NVIDIA alike. No native package required, no driver-specific advice, nothing to choose.
scripts/install.sh follows suit — Arch just gets the AppImage now. toolport-bin is still published for anyone who would rather have a real package that upgrades through pacman; it's a preference now, not a workaround.
What was actually wrong
The AppImage bundled wayland 1.20. AppRun puts the bundle on LD_LIBRARY_PATH, and the dynamic loader applies that to everything the process opens afterwards — including the host's GPU drivers, which are deliberately not bundled. So the host's own Mesa was resolved against a four-year-old wayland:
/usr/lib/libEGL_mesa.so.0: undefined symbol: wl_fixes_interface
wl_fixes_interface arrived in wayland 1.23. libEGL_mesa therefore never loaded at all, eglGetDisplay returned nothing, and WebKitWebProcess aborted on startup with EGL_BAD_PARAMETER — a window that opens and never paints. NVIDIA's proprietary EGL doesn't link libwayland-client, which is the only reason it escaped.
Earlier releases blamed the bundled WebKitGTK for this. That was wrong — it's current (2.50.4) and was never involved. The AppImage simply stops shipping the wayland libraries, so the host's own are used, and the build fails loudly if any ever sneak back in.
Sign-in on Linux opens the browser again
The same environment leak, pointed outward. Every process Toolport spawned inherited the bundle's library paths, so a host binary loaded Ubuntu 22.04's glib or brotli instead of the system's and died at link time before running any of its own code:
chromium: symbol lookup error: undefined symbol: BrotliDecoderAttachDictionary
In practice: clicking Authenticate on an OAuth server said "opening browser" and nothing happened, ever. The same hazard was latent on any MCP server that is a native binary or pulls a native node/python module — the app's core function, not a cosmetic path.
Anything Toolport launches that isn't its own bundled payload now gets the host's environment: the browser, MCP servers, the file manager, your login shell. Your PATH, HOME, proxy variables and each server's own env block are left exactly as they were.
Also in this release
- Windows: launch at login no longer reports "Off" when it's on. The status now reads the registry
Runentry and its startup-approval state byte instead of a timestamp heuristic. (#830) - The HTTP/OpenAPI endpoint survives a restart. Enablement and port persist, and the bearer token is reused from the OS keychain instead of rotating on every launch, so a client configured against it keeps working. (#829)
- OpenCode
opencode.jsoncconfigs are detected. Reads, writes, gateway install and launch migration share one path picker that prefers an existing.jsoncand preserves its comments and trailing commas. (#827) - Adding a server whose secret failed to save no longer leaves you with a duplicate. (#826)
Thanks
- @rohankumardubey — a partial server add used to strand you: if a secret write failed after the server was created, retrying made a second server. Now every write is attempted, the failed keys are named, and the dialog stays open in edit mode so the retry updates the server you just added. (#826)
Thank you for keeping at these. It's the kind of fix that only turns up from real use.
Install
# macOS or Linux
curl -fsSL https://toolport.app/install.sh | bash
# macOS (Homebrew)
brew install --cask tsouth89/toolport/toolport# Windows
irm https://toolport.app/install.ps1 | iexOn Linux, take the .deb on Debian/Ubuntu and the AppImage everywhere else. Installed copies from 0.3.3+ update themselves.
If you worked around the grey window with the native package, you can stay there — nothing is broken. You just don't have to any more.
Full changelog: https://github.com/tsouth89/toolport/blob/v1.16.0/CHANGELOG.md
Toolport v1.15.0
Security
-
Release job no longer inherits Azure Trusted Signing credentials during
frontend install. They are now step-scoped to the Windows tauri build,
matching TAURI and APPLE. (SBS-925) -
Downstream stderr drain no longer grows without bound on a newline-less write.
Stdout was already capped at 16 MiB per line; stderr still used unbounded
read_lineand only trimmed the kept tail afterwards. A hostile or buggy
stdio server that wrote a multi-GB chunk with no newline could OOM the
gateway and take every HTTP-bridge client with it. The drain now uses the
sametake(MAX_RESPONSE_BYTES)bound as stdout and stops on an unterminated
full-cap line. (SBS-930)
Added
-
Arch Linux package:
toolport-bin(paru -S toolport-bin, or
omarchy pkg aur add toolport-bin, once AUR account registration reopens
upstream; until thenscripts/render-aur.sh <version> ./aur && cd aur && makepkg -sibuilds the identical package with no AUR account). The AppImage
bundles Ubuntu 22.04's
libwebkit2gtk-4.1, which has noWebKitGPUProcessand cannot initialise EGL
against a current Mesa, so on a rolling release the window opens grey and empty
whileWebKitWebProcessaborts every launch. NoWEBKIT_*variable avoids it.
The AUR package repackages the official.debpayload against the host
WebKitGTK, the same thing the.debalready does on Debian/Ubuntu. Published
by a newaur.ymlworkflow that build-tests the PKGBUILD in an Arch container
before pushing.scripts/install.shnow routes Arch users there. The fat
AppImage is unchanged and stays correct on Ubuntu/Debian. -
Agent activity: see what your agents do outside Toolport. Toolport routes every
MCP call, so it sees those; it has never seen what Claude Code does natively, which
is most of what an agent actually does (Bash,Edit,Read,WebFetch). A new
Agent activity tab installs a small recorder into Claude Code's own lifecycle and
keeps one line per event: which tool, in which folder, in which session. Off until
you turn it on, and it removes itself from every file it wrote when you turn it off.Two limits it is built around rather than promises about it. It cannot stop your
agent: the recorder is deliberately not attached to the step that can refuse a tool
call, so no defect in it can block your work. And it does not read your work:
commands, file contents and tool output are dropped before anything is written, and a
row keeps only names, a folder, a session, and a fingerprint that cannot be turned
back into the input.Every Claude Code profile on the machine is covered, not just the one Toolport
resolved:CLAUDE_CONFIG_DIRpicks a profile per shell, so~/.claudeand
~/.claude-workare both real and a recorder in only one of them would quietly
under-report. As with agent rules, your file is never rewritten wholesale: only a
marked block is added, comments and formatting elsewhere survive, and a preview shows
the exact bytes before the first write. (SBS-822) -
Devin Desktop, Devin Local, and Devin CLI support. The legacy
windsurf
integration now uses the current Devin Desktop (Cascade) name and brand while
keeping its compatible~/.codeium/windsurfpaths. A separate Devin Local / CLI
client manages the shared user MCP config and globalAGENTS.mdused by the new
default local agent and the terminal CLI. -
Agent rules: write your instructions once, apply them everywhere. A new Agent
rules tab holds one or more named rule sets and writes the active one into every
AI client's own global rules location (AGENTS.md,GEMINI.md,.goosehints,
Devin Desktop'sglobal_rules.md, and atoolport-rules.mdin the rules directory of
clients that read one), so keeping every supported client in agreement no longer
means hand-editing each file. Your own content is never overwritten:
Toolport either owns its own file in the client's rules directory or owns a
marked block inside a shared file and leaves every other byte exactly as it is.
Each client is off until you turn it on, a per-client preview shows the exact
bytes before the first write, and turning a client off or deleting the set
removes what Toolport wrote and nothing else. Cursor and Warp keep their globals
in their own UI, so the tab names them as clients it cannot write rather than
silently skipping them. Needs no MCP server or gateway. Team instructions are unaffected and
coexist in the same files. See docs/agent-rules.md. -
Official brand marks for 14 more clients. Grok Build, OpenCode, Qwen Code, Kimi
Code, JetBrains Junie, Kilo Code, GitHub Copilot CLI, Amp, Pi, Oh My Pi, Factory Droid,
BoltAI, AnythingLLM and Continue now show their own logo in the Clients view instead of
a letter badge, so 32 of the 35 supported clients now carry their own mark. Crush, Jan
and Witsy keep the badge, since none of them publish a usable vector mark.
Removed
- Toolport Studio is no longer a supported client. The project is discontinued, so
detection, the Connect flow, and its~/.toolport-studio/mcp.jsontarget are gone,
along with the session-scoped restart wording it was the only user of. Toolport now
auto-detects 35 clients. If you had connected it, the gateway entry in that file is
left where it is and can be deleted by hand; nothing else reads it.
Fixed
-
Stopping the HTTP bridge no longer reports a false success. When the child
process survived the stop, the app cleared the handle, port and bearer token
anyway, so the bridge read as stopped while it was still listening and nothing
was left to retry with. The stop failure is reported, and the state is kept
when the child is still running so a later stop can finish the job.
(#736, thanks @YuukiRitoTeng) -
The gateway no longer speaks on stdout before the client has handshaked.
MCP forbids a server sending anything before the client's
notifications/initialized, and the gateway builds its catalog on a
background thread that announces the result whenever it finishes. A client
that spawns the process early and sendsinitializeseconds later (Grok Code
does exactly this) readnotifications/tools/list_changedas the FIRST frame
of the stream, rejected it, and then looked like it had simply never been
answered. It cost 80 of 128 sessions their handshake for most of a day. The
notification is now withheld and replayed once the peer has both spoken past
initializeand been answered at least once, so the first frame a client
reads is always a reply to something it asked. Withheld, not dropped: the
catalog really did change while the client was starting up. (SBS-1019) -
A connection test whose details changed underneath it no longer reports a
verdict for the old details. Editing a server's command or URL while its
test was in flight left the finished result on screen as though it described
what is now in the form. The in-flight test stays visibly busy until it
settles and its result is then discarded, and a superseded test can no longer
overwrite a newer one. (#739, thanks @forever-ivy) -
Catalog and Onboarding tell a failed stack fetch apart from an empty one.
AlistStacks()failure rendered as "no stacks", indistinguishable from
having none, with no way to retry. Both surfaces now show a skeleton while
loading and an inline "Try again" on failure, and the stack region renders
independently of the popular-catalog empty state, so an empty catalog cannot
hide working stacks. (#732, thanks @rohankumardubey) -
The Linux AppImage no longer forces X11 in a way nothing can override.
linuxdeploy-plugin-gtkwritesexport GDK_BACKEND=x11into an AppRun hook
that is sourced AFTER the caller's environment, soGDK_BACKEND=waylandwas
silently ignored ("Trying x11 backend", then a tao panic). On Ubuntu/Debian
that is only opinionated; on a Wayland session whose Xwayland cannot survive
the app (reproduced on Arch/Hyprland in a VMware guest onvmwgfx) it was
fatal and nearly undiagnosable: the first launch printedEGL_BAD_PARAMETER,
showed no window, and killed Xwayland session-wide, breakingxdg-open
for every other app; every launch after that produced no window, no output and
no error, because the process connected to the orphaned X socket and blocked
forever. Release builds now rewrite that line to
export GDK_BACKEND="${GDK_BACKEND:-x11}"- the same default, so nothing
changes for anyone who sets nothing - and the release fails if the line the
patch expects has disappeared. -
The GHCR gateway image rebuilds from the current commit.
docker-publishwas still
caching all ofsrc-tauri/targetkeyed only on Cargo.lock, the same shape that shipped
v0.3.12/v0.3.13 with a stale signed binary. It now uses the same Swatinem/rust-cache
eviction asrelease.yml, so workspace crates always recompile while third-party deps
stay cached. (SBS-926) -
HITL and routine audit rows no longer count as successful tool calls.
Prometheustoolport_tool_calls_total, Activity "calls logged", and team
showbackcallstreated everyaudit.jsonlline as a routed call and a
missingokas success. An approved human-approval gate writes a
kind:approvaldecision (ok:trueon purpose, so a deny stays out of the
error rate) plus the timed exec, so one approval showed as two successful
calls. Advisor / suggestion / candidate lines omitokand were counted as
successes while the Activity list painted them as failures. Aggregators now
requireokto be present and skipkindin {approval, routine, advisor,
suggestion, candidate}. (SBS-932) -
Homebrew cask snapshot was three releases behind.
packaging/homebrew/toolport.rb
still said 1.11.0 after 1.14.0 shipped, anddocs/RELEASING.mdhad no tap-bump
step, so the nex...
Toolport v1.14.0
Agents can now keep what worked. A proven multi-tool orchestration can become a
saved routine that survives sessions and clients, with a human approving the exact
definition every time one is persisted.
The rest of the release is mostly a security pass, and the credential one is the
reason to upgrade rather than wait: the redaction gate in front of the public share
link, the diagnostics bundle and the team config push missed several of the most
ordinary ways a key is spelled, so live tokens could ride out to a public URL. Two
more findings in the same family are closed here, along with a set of local-file
permissions that were wider than intended and an installer that never checked who
signed the build it was about to install.
A run of hardening across the app, on one theme: a check that could not finish used
to look exactly like a check that passed. A reload signal, a vault read, a restart
check, a backup stat each had a failure path that came back looking like good news.
They now report the failure, so what the app shows you is what it actually knows.
Added
- Persistent agent routines. A proven multi-tool Code Mode orchestration can be
promoted into a saved, parameterized routine that outlives the session and works
from any client. Promotion is the only way in:toolport_run_scriptgains an
immutable input mode (inputs schema-validated, deep-frozen in the VM, dropped after
assessment), only immutable runs are promotion-eligible, andtoolport_save_routine
takes arunIdrather than source, so free-typed source can never be persisted.
Every save raises a one-shot desktop approval card showing the business summary,
the calls, the dependencies, the risk class and the content hash, with no
session-wide or always-allow shortcut. Saved routines are advertised as first-class
tools and preflight their arguments against the stored schema and observed
dependency fingerprints, failing closed to code mode on drift. A routine advisor
watches for repeated same-shape calls and puts strong candidates in a passive
Suggested routines queue in Settings, rather than prompting the model in-band.
Writes are off until you turn them on. (#625) - Short install commands.
irm https://toolport.app/install.ps1 | iexon Windows and
curl -fsSL https://toolport.app/install.sh | bashon macOS and Linux, replacing the
86-character raw.githubusercontent URLs. Both redirect to a pinned commit rather than
a branch: these are piped into a shell, so the content behind them should not be able
to change without a reviewed change on both sides. - winget package.
winget install Toolport.Toolporton Windows once the manifest is
published, and each release submits its own update. - The audit log records which client made the call. Records carry the caller's
name alongside the server and tool, so an audit answers "who invoked this?" rather
than only "what ran?". (#722) - Re-approve every drifted tool at once. A lost or unreadable pin baseline used to
leave the catalog hidden with no way back except approving each tool by hand.
The baseline is recoverable, and a bulk Re-approve all clears a whole drift set
in one deliberate action. (#747)
Security
- Credentials no longer ride out through share links, diagnostics or the team push.
One redaction gate sits in front of three egress paths: the public share link, the
diagnostics bundle people paste into issues, and the team config push that reaches
the org control plane and every teammate. It missed the ordinary spellings:
--api-key=sk-...,--header=Authorization: Bearer ...,X-API-Key:,Cookie:,
and the split form--token sk-..., whose value sits in its own argument with
nothing on it to recognise. Redaction now reads the whole argument list rather than
one argument at a time, so the split form is caught, and the flag name stays visible
while its value goes. A shared setup is meant to be readable, not to carry your keys.
(SBS-889) - A server that echoes your API key no longer writes it into the audit log. The
audited error text is the defended body, but "defended" is not "secret-free": the
injection scan looks for instruction overrides and the PII pass matches PII shapes,
and an API key is neither. A server answeringinvalid api key sk-live-...put a
live credential intoaudit.jsonland into every CSV exported from it. No attacker
needed, just a server that echoes its input on failure. A credential pass now runs
over the text before it is stored, and it reads JSON bodies as well as prose while
leaving an ordinary failure message readable. (SBS-890) - Local logs are owner-only from the start. Ten append-mode logs were created with
the process umask, which under the usual 022 means world-readable, and only became
owner-only at their first size-triggered rotation.oauth-debug.lognever rotates
andinspect.jsonlis not touched while capture is off, so those two never got
there at all. This matters most forgateway.log, which records the local approval
broker's bound port: on a shared machine a second OS account could read it. Every
log is now created 0600 and an existing wider file is tightened on next write, so an
upgrade fixes the files you already have. No-op on Windows. (SBS-868) - The macOS installer checks who signed the build.
codesign --verifyonly proves
a bundle satisfies its own embedded requirement, so an artifact tampered with before
upload and re-signed with any other Developer ID would have passed. The installer now
requires Toolport's team identifier and fails closed otherwise. It also stages the
new app and swaps by rename, where it used to delete the installed app before
copying: an interrupted install could leave a machine with no Toolport at all.
(SBS-897) - The installers' cross-OS hints stop routing around the pinned-commit rule.
toolport.app/install.shand/install.ps1redirect to a pinned commit precisely
because they are piped into a shell, and both scripts then told a user on the other
OS to fetch the unpinnedmaincopy. Both hints use the pinned URLs now, and CI
fails on any reference to a movable ref. (SBS-894) - A downstream server cannot forge Toolport's own error envelope. A private field
the gateway uses to report protocol errors was honoured on results coming back from
a server, which let a hostile server author an error attributed to Toolport and skip
the whole content-defense layer for that result. It is now stripped at the transport
boundary, before anything reads it. (SBS-891) - A revoked token can no longer stay live. The generation bump that tells a running
gateway to reload swallowed a failed registry write, so a revoke could remove the
credential from the keychain, never reach the gateway, and still report success. The
failure is now propagated and the partial outcome is stated plainly. (#737) - A locked or unreadable keyring is no longer read as "no secret stored". A failed
vault read used to be indistinguishable from an absent secret, so Toolport could mint
a replacement bearer, report a missing refresh token, or show a stale secret status.
Each of those paths now distinguishes the error from the absence. (SBS-840, SBS-841,
#758) - Disconnect fails loudly when the shared HTTP bearer cannot be revoked. Reporting
a client disconnected while its token still works is the one outcome that matters
here. (SBS-845) - The npx spawn guard covers the eval flags it missed, and Teams stops following
redirects with a member bearer. A redirect would have replayed
Authorization: Bearerto a host of the redirector's choosing; team servers under
review are now an execution gate rather than a label. (#711, #713)
Fixed
- A space no longer smuggles a forged Toolport marker past the voice rewrite.
The gateway-voice matcher folded zero-width, bidi, fullwidth and homoglyph
evasions but compared whitespace literally, so[ Toolport advisor: …]with a
plain space (or a tab, a newline, a no-break space, or a doubled space inside
the brand) reached the model unchanged while the zero-width form was caught.
Whitespace is now folded like the other evasions: ignored before the brand and
collapsed to one space inside it. (SBS-896) - Team Instructions now follow
XDG_CONFIG_HOMEfor Goose and Zed. Connect
already wrote those clients' MCP configs under XDG (#757 / SBS-847); the rules
writer still hardcoded~/.config, so an org push could succeed and never
apply. AbsoluteGOOSE_PATH_ROOTis honoured on both the config and rules
paths (<root>/config/config.yamland<root>/config/.goosehints). A member
who upgrades in place also gets the block moved to the new path: an unchanged
org text used to skip the write entirely, so the new location stayed empty and
coverage reported Stale until someone edited the instructions. (SBS-899) - Untrusted MCP output can no longer speak as Toolport. The external-data
wrapper is Toolport-branded (it still saidconduitafter the rebrand) and
sanitizes the server label, so a quote in a resource URI cannot close the
marker. Downstream text that imitates[Toolport advisor:],[Toolport shaped],
[Toolport:], or[conduit:]is rewritten before it reaches the model,
including tools/list, search (ranked and pinned results), tool and resource
and prompt output, and resource errors, even when content defense is off. The
rewrite covers a whole tool definition (title, annotations, parameter
descriptions, enum values, property names) and a whole result (structured
output, prompt messages in any shape), and it folds the same zero-width,
fullwidth, and homoglyph evasions the injection scanner already folds.
(SBS-896) - A registry that was not really read no longer counts as "no HTTP clients".
--insecure-loopbacktreats an empty `http_clien...
Toolport v1.13.0
Toolport installs two new ways: as an agent plugin any conformant client can pick up,
and on Windows through a one-line command instead of a trip to the Releases page.
Pseudonymization gains the piece it was missing, a way for a human to release one
value to one server, so the workflow it used to dead-end now has an answer.
Most of the rest of this release is one defect wearing different faces: code that
read a failed probe as good news. A failed audit baseline, health check, security
read, or integrity load could each come back looking like "all clear" and let the
app act on it. Each one now fails closed.
Added
- Agent plugin. Toolport now ships as an Agent Plugins 1.0
package (toolport-agent-plugin.zipon each release): one install connects VS Code,
GitHub Copilot CLI, the Copilot app, and other conformant clients (plus Claude Code,
via the bundled dual layout) to the local gateway, with a skill teaching the agent
the search → call workflow. The plugin launches the gateway the desktop app already
installed, so plugin installs share your existing servers, credentials, and profiles.
If the app already manages that client, disconnect it there first or the gateway
connects twice. - Windows one-line install.
irm https://raw.githubusercontent.com/tsouth89/toolport/main/scripts/install.ps1 | iex,
matching the macOS and Linux one-liners. It resolves the release through the GitHub
API, picks the NSIS asset for the machine's architecture, and refuses to install
anything it cannot verify against the per-asset digest (-AllowUnverifiedis the
explicit override). A signature that disagrees with the file is fatal; a missing one
is a warning, since builds before Azure signing shipped unsigned. (#610) - Release one pseudonymized value to one server. Scoping rehydration to the minting
server closed a cross-server exfiltration channel but left a legitimate workflow with
no path at all: read a customer from a CRM, mail them through a different server, and
the call simply failed. Refusal is still the default, with a human decision as the
remedy. The prompt names the destination, each token, and its real value, and grants
that one value to that one server. There is deliberately no blanket per-server grant,
an "always allow" on a tool can never release a later value, and a headless gateway
with nobody to ask refuses exactly as before. Audited by hashing the arguments, so
released values never reach the log. (SBS-696) - Pseudonymization counts in the audit log, CSV export, and Activity. This path
fails open: a full session map or an over-cap result leaves values in the clear, and
a call where redaction quietly did not apply used to be indistinguishable from a clean
one.piiReplacedis absent when redaction was off and present-and-zero when it ran
and matched nothing, andpiiIncompleteis written only when true, so the fail-open
case is greppable. Activity shows "N pseudonymized" with a warning badge when the pass
did not fully apply. Counts only; values never leave the session map. CSV columns are
appended at the end, so positional consumers keep working. (SBS-607) toolport.checkpoint()in code mode. A last-write-wins resume marker a script
sets itself, alongside the automatic call ledger. Unlikeprogress, which is
positional, this holds author-chosen state ({ lastInsertedId: row.id }). Costs
nothing againstmax_calls, capped at 4096 bytes, and surfaces in the failure text
and instructuredContent.toolportScript.checkpoint. (#663)- Server-declared tool icons render in the tool browser.
data:sources only: a
remote icon URL is not a picture but a request the app makes to a server-chosen host
on every paint, reporting when Toolport is open and from what IP with no tool call
involved. (SEP-973) - URL-mode elicitation, so a server that needs you to finish something in a browser
works even with a client that does not support it: Toolport brokers the prompt in the
desktop app instead of letting the call fail. A server-provided link is a phishing and
internal-network primitive, so only credential-free HTTPS URLs resolving exclusively to
public addresses are allowed, under the same host boundary as Toolport's SSRF defenses,
and the origin shown to you is derived from the parsed URL rather than from anything the
server says about itself. (SBS-707)
Fixed
- A failed read no longer looks like success. Onboarding needs a successful audit
baseline before anything counts and stays in checking or unavailable until an
authoritative health result exists, with concurrent probes sharing one promise instead
of returning an empty list. Activity keeps the last known security events across a
failed refresh and never shows Protection active without an authoritative read. A
rejected client-secret probe stays unknown instead of reporting "no secret stored",
which was blocking scope edits for secrets that were already vaulted. Integrity-store
failures fail closed, and interrupted pin updates recover. (SBS-718, SBS-719, SBS-720,
SBS-721, SBS-722, #697) - The HTTP bridge validates
Origin, not justSec-Fetch-Site. Under DNS rebinding
the attacker's domain resolves to loopback, so the browser calls it same-origin and the
old guard waved it through, worst under--insecure-openwhere there is no token.
AbsentOriginstays allowed, so curl and Open WebUI's backend are unaffected, and a
deliberately exposed bridge can still serve its own browser UI by matching the bound
address or listing hosts inTOOLPORT_HTTP_ALLOWED_ORIGINS. No name resolution is
involved, so a domain that merely resolves to the bridge stays foreign. (SBS-452) - Folder scoping keeps working as Roots is deprecated. Roots was the only input
folder-scoped auto-routing ever had, so a client that never sent it, or stopped
mid-session, silently dropped to the unscoped profile and reached more servers than
intended. The project root now resolves fromTOOLPORT_ROOT, then the client's roots
for the whole deprecation window, then the gateway's working directory. (SEP-2577) - Stale gateway and catalog state can no longer become permanent.
CLAUDE_CONFIG_DIR
is honored instead of hardcoding~/.claude.json. A downstream that answers
tools/listwith a truncated catalog no longer becomes cached truth for every gateway
started afterwards. A republish under a content-addressed filename no longer flips a
managed client to Customized, which had pinned it to a superseded gateway permanently
(npx, docker, and wrapper scripts are still Customized). A confirmed rebuild collapse
can now land instead of being held forever. (#698) - Profiles with similar names no longer share state. Per-profile stores were named by
a lossy slug of the display name, so "Work Prod" and "Work/Prod" shared cache, pins,
and quarantine. Files are keyed by a hashed profile id now, unambiguous legacy files
migrate, and a slug collision fails closed rather than merging two profiles. (SBS-715) - A Windows credential being replaced no longer reads as missing. Windows reports the
base credential absent for the instant a replace is in flight, which surfaced as a
spurious "not authenticated" during OAuth refresh, since the app writes these and the
gateway reads them from another process. Absence now only counts once it survives
bounded retries with a real backoff. A mitigation rather than a guarantee: measured
over 9,600 racing reads, 37 torn reads became 4. (SBS-711) - Atlassian OAuth on Windows, plus a longer refresh-lock wait so a slow refresh is
not abandoned. (#685, SBS-705) - Cancellation is honored end to end. The HTTP transport aborts or forwards
cancellation and retry backoff observes it, so abandoned work releases its per-server
slot. The updater refuses to install while gateways are still running and recovers the
HTTP bridge if an install fails after shutdown. (SBS-716, SBS-717) - PII session maps are cleared when sessions end. (SBS-704)
- Requests with no transport fail closed instead of proceeding. (SBS-551)
- Tray and update lifecycle, including approval requests being delivered once rather
than repeatedly. (SBS-146)
Thanks
Two patches this cycle, one of them a code-mode feature listed above:
- Vermitrude -
toolport.checkpoint(), the
script-declared resume marker a code-mode script sets alongside the automatic call
ledger (#689). - rohankumardubey - ran the headless Rust
suite across macOS, Linux and Windows in CI, where the no-desktop build had only been
exercised on one platform (#671).
If we missed you, open an issue.
Full changelog: https://github.com/tsouth89/toolport/blob/v1.13.0/CHANGELOG.md
Toolport v1.12.0
Two features ship for the first time. PII pseudonymization replaces personal data in
tool results with tokens before the model sees them, restoring the real values only
for the server that provided them. OAuth client credentials let a headless server —
one nobody can click a browser sign-in for — get a real token. Both are off or opt-in
by default.
Several things that were only safe within one process are now safe across processes:
rate-limit counters, OAuth token refresh, and the pseudonym map. Each client spawns
its own gateway, so one process was never the real shape.
Upgrading a Teams deployment: the instructions receipt hash changes once in this
release. See Changed below before rolling it out.
Added
- PII pseudonymization. Emails, phone numbers, card numbers, IBANs, IP addresses
and provider-shaped API keys become stable tokens (⟦EMAIL_1⟧) before the model
sees them, and are restored on the way out. The mapping stays in memory. Off by
default, and a reduction rather than a guarantee: a value no detector recognizes
passes through, as does everything once the per-session cap is reached. (SBS-346) - OAuth client credentials for headless servers. Discovers the endpoint,
negotiates the auth method, and reacquires before expiry. Never falls back to a
browser flow, which would be unusable where this is needed. (SBS-524) - Old gateway binaries are cleaned up instead of accumulating (~18 MB a release).
Keeps anything running, named by a client config, known to be relaunching, or
recent enough to still be cached. (SOU-484) - Apps still launching an obsolete gateway are named. An app caches its spawn
command at startup, so stopping the process is not enough. Settings and a launch
notification list which apps need restarting, each entry clears itself, and Settings
now reports processes it could not stop. (SOU-435) - Keyboard shortcuts.
Ctrl/Cmd+1–6switch view,/orCtrl/Cmd+Ffocuses
search,Ctrl/Cmd+Nadds a server,Ctrl/Cmd+Rrefreshes,?lists them.
(SBS-143) - The window reopens where you left it instead of resetting to a fixed centered
geometry on every launch. (SBS-144) npxservers start about four times fewer processes. Toolport resolves the
entry point directly instead of thecmd.exe→npx→ shim → server chain, which
came to roughly 423 processes for 72 servers on one machine. Version ranges and
@lateststill go throughnpx. Because this skips the install step a server stays
on the cached version;TOOLPORT_NO_DIRECT_SPAWN=1restores the old path. (SOU-550)- Modern MCP extensions. Opaque client extension settings pass through to
downstream servers and compatible declarations aggregate inserver/discover;
io.modelcontextprotocol/taskshandles stay bound to the server that created them
and survive router rebuilds; clients can detect Toolport viaapp.toolport/gateway,
which reports discovery, code-mode, agent-control and approval state. (SOU-453) - MCP Apps. Catalog fetches negotiate the HTML MIME type, Apps hosts get UI-linked
tools even under lazy or grouped discovery, andui://resources route through
their owning tool. App HTML stays byte-faithful for host CSP, and app-only tools
stay out of model-facing search. (SOU-453) - Code mode can validate a script without running it, and a script that fails
partway reports the calls it already made instead of discarding them. (SBS-646,
SBS-647) - The gateway binary supports
--helpand--version, and rejects unknown flags.
Changed
- The team-instructions receipt hash now uses SHA-256 instead of Rust's default
hasher, whose algorithm carries no guarantee across compiler releases. This
changes every member's reported hash once. The Teams coverage dashboard will show
one round of drift that is not real drift; it reconverges as members report in on
this version. Same width and format as before. (SBS-460)
Security
- A pseudonym only resolves for the server that produced it. Tool results are
attacker-controlled, so an injected result could otherwise talk the model into
putting a CRM's token into a URL for an unrelated fetch tool, sending the real value
to whoever wrote the injection. Calls carrying another server's token are refused.
The cost is deliberate: passing a record between servers no longer works unattended.
(SBS-605) - The pseudonym map is cleared when a conversation ends and on a fresh handshake,
on every transport. It previously lived for the whole gateway process with no
eviction. (SBS-605) - Retry fields on a resumed request get the same pseudonym handling as the arguments
beside them, so a host answering a prompt from model context no longer relays a
literal token downstream. (SBS-606) - OAuth token refresh is serialized across the app and the gateway. They share one
keychain and could spend the same refresh token twice, which a provider with reuse
detection answers by revoking the whole family. (SBS-479) - Rate-limit counters are safe across concurrent gateways. Each client spawns its
own and they overwrote one another's counts, so org caps under-counted and did not
hold. Overlapping caps sharing a window no longer double-count. (SBS-680, SBS-609) - An empty quarantine store fails closed. A truncated file silently re-exposed
every tool held after high-risk drift or baseline tamper. (SBS-654) - A cleartext-auth refusal no longer echoes URL-embedded credentials into the error,
where they reached the activity view and logs. (SBS-636) - OAuth hardening (SOU-451): authorization responses bind to the validated issuer,
and stored credentials refuse to cross a changed issuer; discovery checks
path-specific protected-resource metadata before the origin fallback and rejects
metadata for a different resource; BearerWWW-Authenticatechallenges are honored
and validated through the existing TLS and SSRF guards; runtimeinsufficient_scope
challenges preserve prior scopes and open a bounded consent step-up; servers
advertising Client ID Metadata Documents use Toolport's stable HTTPS identity, with
Dynamic Client Registration as the fallback.
Fixed
- Reading a resource whose URI contains non-ASCII characters no longer fails. The
template matcher walked the URI by byte, so backtracking through a multi-byte
character crashed the router. (SBS-620) - A code-mode script's return value is no longer silently corrupted: a
Datecame
back as{}and aBigIntasnull, both reported as success. Values that cannot
be represented now error instead. (SBS-631) - Connect, rescope, disconnect and migrate leave a restart reminder in the panel
rather than only a toast that fades, with wording matched to the action. (SBS-336) - The "Stop old gateways" panel no longer reads as though it contradicts itself. It
could report nothing running directly above a list of apps still launching one —
both true, since a client spawns the gateway on its next tool call. It now says so,
and each row shows the process id so near-identical entries can be told apart. - The Activity list no longer comes up short when the audit log holds an unreadable
line, which used to consume a slot in the page instead of being skipped. (SBS-677) - Servers launched through
npx.batkeep their package identity on import and paste;
only.exe,.cmdand.ps1were recognized as package runners. (SBS-664) - A registry change saved in the app could briefly revert on screen when the disk
watcher read the file outside the lock guarding the in-memory copy. (SOU-329) - OAuth loopback callbacks reject an empty authorization code instead of showing a
success page and sending an invalid token exchange. - The onboarding "What is Toolport for Teams?" link opens the explainer page rather
than the app sign-in, the right destination before you have a team. (SBS-461)
Maintenance
cargo clippy --all-targetsis error-free. A deny-by-defaultnever_loopwas the
last hard failure blocking a clippy gate in CI. (SBS-434)- Code mode's real limits are documented and pinned by tests: a script that never
calls a tool is bounded by iteration and recursion counts, not the wall clock. Both
fail closed. (SBS-430) - Tests pin that pseudonyms resolve as the last step before dispatch, an ordering that
had regressed once without anything catching it. (SBS-614) - Team URLs come from one place, CodeRev reviews once per PR, and text files check out
with LF endings on every platform. (SBS-461)
Thanks
Ten people sent patches this cycle, including two new clients, a fix that stops
installs stripping comments out of hand-edited configs, and the CI job that now guards
the gateway's security suite:
- Vermitrude - Factory Droid CLI support, and
--help/--versionon the gateway binary, which previously accepted unknown
flags silently (#579, #627). - slegarraga - removed a dead data-directory
shim and corrected three pieces of CONTRIBUTING that had drifted from the code
(#638, #639, #642). - rohankumardubey - put the headless gateway
security smoke suite into CI, where it had never run, and exposed sidebar and toggle
state to screen readers (#580, #621). - syf2211 - turned raw stack traces into readable
headlines for DNS, TLS, 429 and 5xx connection failures, and made catalog search
include the category so searching a visible heading actually matches (#611, #614). - alexgaribay - Kimi CLI support, and a fix for
duplicate Tauri context creation across startup paths (#658, #662). - **[arimu1](https:...
Toolport v1.11.0
Modern MCP over Streamable HTTP, with sessionless requests, multi-round-trip
approvals, subscription listeners, and the legacy session flow still available
on the same endpoint.
Highlights
MCP 2026-07-28 over Streamable HTTP
Modern clients can call POST /mcp without an initialize request or
Mcp-Session-Id. Each request carries its protocol metadata and routing headers,
and ordinary results identify themselves with resultType: complete.
Nothing moves for existing HTTP clients. The legacy 2025-06-18
initialize/session flow remains on the same URL, and Toolport chooses the path
from the request rather than requiring a server-wide mode switch. (#584, #586)
Multi-round-trip requests and human approval
A modern destructive call no longer has to hold an HTTP request open while a
person decides. Toolport returns input_required with an opaque requestState;
the client supplies inputResponses on a fresh request to deny or resume the
exact bound call. Denial never reaches the downstream server, and approval
executes the accepted call once. (#585)
Modern subscriptions
Modern clients use subscriptions/listen instead of the legacy GET stream.
Listeners choose the notifications they need, receive their subscription id in
each event, and clean up when the POST stream closes. A follow-up fix makes sure
events are flushed rather than waiting in the response buffer. (#583, #590)
Cacheable catalogs and downstream headers
Modern list and discovery results carry ttlMs and cacheScope, with stable
ordering across equivalent requests. Modern HTTP servers receive the required
protocol and routing headers, including values moved from tool arguments by
x-mcp-header; legacy servers continue to receive their established wire
format. (#584, #587)
Four more clients
Toolport now detects and configures 31 AI clients. This release adds:
Their existing config shapes and unrelated application settings are preserved;
an unparseable whole-app config still fails safely instead of being replaced.
Reliability and security
- Registry recovery accepts only a valid backup instead of silently starting
from an empty registry. (#582) - Corrupt integrity pins fail closed. (#581)
- Removing a server also removes its overrides, pins, result budget, content
exemption, and fingerprint-bound approvals. (#509) - Import-time private-host detection matches the OAuth SSRF guard. (#564)
- Unsupported legacy protocol versions return an actionable negotiation error.
(#588)
Search, import review, result formatting, rate-limit matching, corrupt inspect
lines, share-link copy failures, and several misleading error states also got
smaller fixes. See the full changelog for the complete list.
Upgrade notes
- Install 1.11.0 and open Toolport once so the new gateway is installed.
- Restart AI clients that were already open. A client can cache the old
versioned gateway path until the client process exits. - Shared HTTP users keep the same endpoint and bearer configuration. Modern
clients can move to the sessionless flow; legacy clients need no changes. - Modern HITL requires the client's elicitation capability. Clients without it
fail closed rather than running a gated call.
This is the modern Streamable HTTP transport milestone, not a claim that every
optional MCP 2025-11-25 and 2026-07-28 feature is complete. Auth catch-up,
URL-mode elicitation and icons, Tasks/extensions, and the roots replacement are
tracked separately.
Thanks
Thanks to BharadwajKanneveti,
ColumbusLabs,
wenn-id,
rohankumardubey,
Vam-si-krish,
arimu1, and
Vermitrude for patches in this release.
Full changelog
See CHANGELOG.md
and the related PRs: #509, #538, #541, #553, #560, #564, #566-#578, and
#581-#590.
Toolport v1.10.0
Toolport v1.10.0
MCP 2026-07-28 support over stdio in both directions, stale gateways that stop after an upgrade, approvals that re-check against the live gateway, and a batch of transport and code-mode fixes.
Highlights
MCP 2026-07-28 over stdio, both directions, same endpoint
A client on the new revision can talk to Toolport, and Toolport can talk to a server on it. Every existing client and server keeps seeing byte-identical traffic. Both eras run on one stdio endpoint and are detected per connection, so there is nothing to migrate and nothing to configure. (#511)
Three things now work through the gateway that did not before:
- Progress notifications reach your client. A server reporting progress during a long call has it relayed back, routed to the client that asked for it.
- Large results keep their full envelope. Shaping an oversized result preserves
_metaand any fields Toolport does not recognise. - Structured error codes survive the hop, so a client can act on a machine-readable code instead of parsing a message string.
Over Streamable HTTP, Toolport stays on the established revision for now. A modern client gets the response the spec defines as the fall-back signal, so it negotiates down cleanly instead of failing. That half lands with subscriptions/listen.
Old gateway processes stop after an upgrade, on every OS
Upgrading used to leave older versioned gateways (toolport-gateway-1.9.4.exe and friends) running, so security and policy fixes in the new binary never took effect for clients still talking to an old one. Gateway identity is now path-based across Windows, macOS, and Linux. Two platform bugs turned up in the process: on macOS the process listing used an argv Apple's ps rejects, so it saw zero gateways, and on Linux a binary replaced in place was treated as protected rather than obsolete. Settings gains a Stop old gateways action. (SOU-414)
There is one limit, and it decides whether you need to do anything. An AI client caches the gateway command when the client itself starts, so what matters is the path it cached. Where the binary is replaced in place, that path already resolves to the new build and the next spawn picks it up. Where the path is one an upgrade never rewrites, it does not: on Windows the filename carries its version, so an upgrade never has to overwrite a locked file, and on any OS a client can be pinned to an install location you have since moved away from. In those cases, restart the client app. The Stop old gateways description in Settings spells out which case applies. Clients started after the upgrade are unaffected.
An approved tool call is re-checked before it runs
A human approval used to be validated against a snapshot taken before the hold, so a tool that was quarantined, released, or had its definition changed while the approval sat in the queue still executed against the pre-hold view. Approvals now rebind to the live router and fail closed if the definition fingerprint moved or the tool is blocked, with a "this approval is stale" message. (SOU-321, SOU-322)
Vendor auth hints now require an exact domain match. Lookalike apex domains (clerkauth.com, evilgithub.com) could inherit a real vendor's auth hints and token URL through prefix and suffix matching on the second-level label.
Credentials and transport
- A Continue Shared HTTP bearer now reaches the wire. The token was written under
env, which Continue does not forward for remote servers, so a plaintext bearer sat on disk and never authenticated anything. It now goes underrequestOptions.headers, matching Continue's contract. - Client config backups no longer accumulate live bearer tokens. Every config write copied the previous file and nothing pruned them, so a Shared HTTP client's backups built up carrying working credentials. Capped at five generations per file, matching the registry.
- The Shared HTTP bridge comes back after the reaper stops it. Reaping a bridge whose binary had been replaced left HTTP and OpenAPI clients with nothing listening until someone reopened Settings.
- Resource subscriptions clean up when a session is replaced, and a subscriber waiting on another client's open no longer gives up while that open is still succeeding.
- Code mode budget and isolation.
fetchResultshares the call and wall-clock budget instead of paging without limit, async workers reinstall the active session for host calls, and a corrupt registry no longer boots with code mode enabled.
Also in this release
- Pasting a Crush config no longer fails as a malformed OpenCode one. Both use a top-level
mcpkey, so the shape ofcommanddecides which it is. (#497) - Rate-limit counters stay in memory until a data directory is bound, instead of writing a stray counter file into the working directory. (#543)
- The share-link copy button confirms it copied, and says so when it could not. (#549)
- Coverage for the import-review shell and private-host classifiers, and for gateway filtering during client migration. (#547, #510)
fmtMsandfmtDollarsmoved intolib/utilswith tests. (#548)- Error strings, the benchmark write-up, the security notes, and CONTRIBUTING all say what the code actually does. (#539, #545, #546, #540)
Thanks
Patches this cycle came from:
- AnayGarodia - benchmark and security docs, the share-link copy fix,
fmtMs/fmtDollarsextraction, and tests for the import-review classifiers (#545, #546, #547, #548, #549). - Vermitrude - OpenCode/Crush paste disambiguation (#497).
- snowyukitty - keeping unbound rate-limit counters in memory (#543).
- rohankumardubey - test coverage for gateway filtering during client migration (#510).
- cyforkk - normalised the error strings (#539).
- HaimiyaWasn - CONTRIBUTING correction (#540).
If we missed you, open an issue.
Upgrade notes
- Install 1.10.0 and open Toolport once, so the launch pass can stop obsolete gateways.
- If an AI client was already running before the upgrade, restart it. Toolport does not yet tell you which ones need it, so if in doubt, restart the clients you had open.
- Nothing to configure for MCP 2026-07-28. Era detection is per connection, and clients on older revisions are unaffected.
- Shared HTTP users on Continue: reconnect the client once so the bearer is rewritten under
requestOptions.headers.
Full changelog
See CHANGELOG.md and the related PRs: #497, #510, #511, #539, #540, #543, #545-#549, #552, #554, #556.
What's Changed
- chore(release): prepare 1.9.6 by @tsouth89 in #493
- docs(release): full 1.9.6 notes and contributor thanks by @tsouth89 in #494
- docs(release): public 1.9.6 notes cleanup by @tsouth89 in #495
- fix(app): cross-platform path-based stale gateway reaper (SOU-414) by @tsouth89 in #496
- fix(app): reaper macOS ps argv and Linux (deleted) exe basename by @tsouth89 in #498
- fix(clients): Shared HTTP lifecycle — bearer write, scope, revoke by @tsouth89 in #499
- fix(gateway): clean resource subs on session mint + OpenGate timeout (WS1) by @tsouth89 in #500
- fix(gateway): code mode budget, session TLS, kill-switch tests (WS2) by @tsouth89 in #501
- fix(security): exact vendor SLD match; reject lookalike apex domains (WS5) by @tsouth89 in #502
- fix(clients): Continue Shared HTTP bearer under requestOptions.headers (WS3-1) by @tsouth89 in #503
- fix(gateway): assert code-mode kill switch via real dispatch paths (WS2) by @tsouth89 in #504
- fix(gateway): OpenGate wait + Linux reaper (deleted) policy (WS1/WS4) by @tsouth89 in #505
- fix(gateway): rebind live Router after HITL approval (SOU-321/322) by @tsouth89 in #506
- fix(app,gateway): post-1.9.6 review follow-ups (SOU-432/433/434) by @tsouth89 in #507
- chore: 1.9.7-rc.1 for multi-OS smoke testing by @tsouth89 in #508
- fix: disambiguate OpenCode vs Crush mcp shapes in parse_json_snippet (#418) by @Vermitrude in #497
- test(desktop): cover gateway filtering during client migration by @rohankumardubey in #510
- fix: normalize 'Could not' error strings to 'Couldn't' by @cyforkk in #539
- feat(gateway): dual-era MCP 2026-07-28 support on stdio (SOU-443/444/445/446) by @tsouth89 in #511
- docs: update frontend test file naming in CONTRIBUTING by @HaimiyaWasn in #540
- docs(benchmark): align prose with the measured tables by @AnayGarodia in #545
- docs(security): lead with TOOLPORT_* names, keep CONDUIT_* as legacy by @AnayGarodia in #546
- fix(share): give the copy-link button feedback and surface failure by @AnayGarodia in #549
- refactor(utils): move fmtMs and fmtDollars into lib/utils with tests by @AnayGarodia in #548
- test(import-review): cover the runsShell and isPri...
v1.9.6
Toolport v1.9.6
Client ownership that stays put, Shared HTTP as a real connect option, code mode that can run real multi-step scripts, native resource subscriptions, and a pile of gateway hardening.
Highlights
Your client config is yours (unless you ask us to rewrite it)
A report from the community (#487) showed Toolport rewriting a hand-edited Claude Desktop toolport entry on every app launch. That is fixed end to end:
- Launch re-point only touches real gateway binaries (versioned
toolport-gateway*/ legacyconduit-gateway*). Hand-edited commands likenpx mcp-remoteare left alone and logged. (#488) - Ownership is first-class: Managed / Customized / Absent. Integrations shows a custom-configuration badge and Reset to default (confirm before overwrite). Connect and migrate refuse to clobber a customized entry without an explicit force. (#489)
- Shared HTTP on Connect. Point a client at the supervised HTTP bridge instead of spawning its own gateway: native
url+ bearer where the client supports it, or an opt-innpx mcp-remoteform for stdio-only clients (Claude Desktop and friends). Tokens are vaulted; ownership records never store bearers. (#491) - Machine-wide
TOOLPORT_HTTP/CONDUIT_HTTPno longer hijacks client-spawned stdio gateways. When stdin is a pipe, env forms are ignored (with a warning). Prefer--httpfor scripts and services. (#488)
Code mode grows up
- Parallel / async tool calls in
toolport_run_script:callAsync,Promise.all, bounded host parallelism. (#480) - Typed
servers.*stubs so scripts can call scoped tools with real names. (#481) - Full intermediate results and
fetchResulthandoff so multi-step scripts stay useful. (#483) - Promise drains and edge-case hardening. (#482)
- On by default, with Settings as the kill switch. Existing registries that already set
"codeMode": falsestay off. (#485)
Native MCP resources
- Subscribe / unsubscribe and
resources/updatedfanout from downstream servers to clients (stdio + HTTP). (#476–#479) - Resource templates aggregated and completions forwarded. (#475)
- Producer verification before fanout so a notification is not blindly broadcast. (#484)
- Paginated catalogs preserved so large tool lists do not drop pages. (#474)
Gateway performance and correctness
- Search efficiency and lower routed-call audit overhead. (#472, #473)
- HTTP confirm and shaped-result stash keyed by stable client id. (#478)
- Headless
secrets.encload-modify-save is locked against concurrent writers (same idea as registry locking). (#486)
UI and safety polish
- Profile scope panel shows tool-fetch failures and ignores stale errors after a newer load; tool loading state is scoped per server. (#468)
- Vendor auth hints match on domain-label boundaries only - spoofed hosts like
clerk.evil.comno longer skip the live probe. (#417 / #492)
Thanks
Huge thanks to everyone who filed reports, reviewed, or sent patches this cycle:
- DanielRitter75 for the detailed #487 write-up (env, repro, backups). That report drove the ownership and Shared HTTP work.
- Vermitrude for the vendor domain-boundary fix (#492).
- BharadwajKanneveti for profile-scope tool-fetch error handling (#468).
If we missed you, open an issue or ping us - credit should follow the work.
Upgrade notes
- Install 1.9.6 and open Toolport once so launch migration / re-point can run.
- Restart each AI client so it reloads MCP config.
- Existing registries with
"codeMode": falsestay off code mode. - Prefer
toolport-gateway --httpover a machine-wideTOOLPORT_HTTPfor services.
Full changelog
See CHANGELOG.md and the related PRs: #468, #472–#486, #488, #489, #491, #492.