Skip to content

Releases: btsouth/toolport

Toolport v1.18.0

Choose a tag to compare

@github-actions github-actions released this 31 Aug 00:22

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 as toolport, 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 .deb and
    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 the toolport package 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. Denies sudo and sudoedit.

  • 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.
    n8n and Langfuse describe 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 -dev data 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

Choose a tag to compare

@github-actions github-actions released this 23 Aug 22:32
845fc57

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 whatever approval-endpoint.json named 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 as unreachable, 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 a 0700 directory 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 and registry.json alike, 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
    modern input_required result 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 appends Toolport source: the "<server>" MCP server (not Toolport) to the
    message, the form-mode counterpart of the verified Toolport 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.json that runs toolport-gateway --toolport-guard cursor before 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__tool for 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 / WebFetch rules 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's settings.json
    under permissions, 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 .env or 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.md or a
    ~/.codex/AGENTS.md was 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.md and GEMINI.md, the other .md files 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 root AGENTS.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 into AGENTS.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
    HOME and USER; 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
    with no 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...

Read more

Toolport v1.16.0

Choose a tag to compare

@github-actions github-actions released this 22 Aug 19:26

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 Run entry 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.jsonc configs are detected. Reads, writes, gateway install and launch migration share one path picker that prefers an existing .jsonc and 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 | iex

On 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

Choose a tag to compare

@github-actions github-actions released this 21 Aug 00:27
510fe9c

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_line and 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
    same take(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 then scripts/render-aur.sh <version> ./aur && cd aur && makepkg -si builds the identical package with no AUR account). The AppImage
    bundles Ubuntu 22.04's
    libwebkit2gtk-4.1, which has no WebKitGPUProcess and cannot initialise EGL
    against a current Mesa, so on a rolling release the window opens grey and empty
    while WebKitWebProcess aborts every launch. No WEBKIT_* variable avoids it.
    The AUR package repackages the official .deb payload against the host
    WebKitGTK, the same thing the .deb already does on Debian/Ubuntu. Published
    by a new aur.yml workflow that build-tests the PKGBUILD in an Arch container
    before pushing. scripts/install.sh now 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_DIR picks a profile per shell, so ~/.claude and
    ~/.claude-work are 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/windsurf paths. A separate Devin Local / CLI
    client manages the shared user MCP config and global AGENTS.md used 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's global_rules.md, and a toolport-rules.md in 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.json target 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 sends initialize seconds later (Grok Code
    does exactly this) read notifications/tools/list_changed as 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
    initialize and 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.
    A listStacks() 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-gtk writes export GDK_BACKEND=x11 into an AppRun hook
    that is sourced AFTER the caller's environment, so GDK_BACKEND=wayland was
    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 on vmwgfx) it was
    fatal and nearly undiagnosable: the first launch printed EGL_BAD_PARAMETER,
    showed no window, and killed Xwayland session-wide, breaking xdg-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-publish was still
    caching all of src-tauri/target keyed 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 as release.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.
    Prometheus toolport_tool_calls_total, Activity "calls logged", and team
    showback calls treated every audit.jsonl line as a routed call and a
    missing ok as success. An approved human-approval gate writes a
    kind:approval decision (ok:true on 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 omit ok and were counted as
    successes while the Activity list painted them as failures. Aggregators now
    require ok to be present and skip kind in {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, and docs/RELEASING.md had no tap-bump
    step, so the nex...

Read more

Toolport v1.14.0

Choose a tag to compare

@github-actions github-actions released this 16 Aug 18:45
3bbab40

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_script gains an
    immutable input mode (inputs schema-validated, deep-frozen in the VM, dropped after
    assessment), only immutable runs are promotion-eligible, and toolport_save_routine
    takes a runId rather 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 | iex on Windows and
    curl -fsSL https://toolport.app/install.sh | bash on 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.Toolport on 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 answering invalid api key sk-live-... put a
    live credential into audit.jsonl and 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.log never rotates
    and inspect.jsonl is not touched while capture is off, so those two never got
    there at all. This matters most for gateway.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 --verify only 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.sh and /install.ps1 redirect 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 unpinned main copy. 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: Bearer to 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_HOME for 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. Absolute GOOSE_PATH_ROOT is honoured on both the config and rules
    paths (<root>/config/config.yaml and <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 said conduit after 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-loopback treats an empty `http_clien...
Read more

Toolport v1.13.0

Choose a tag to compare

@github-actions github-actions released this 13 Aug 23:05
1ed50ff

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.zip on 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 (-AllowUnverified is 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. piiReplaced is absent when redaction was off and present-and-zero when it ran
    and matched nothing, and piiIncomplete is 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. Unlike progress, which is
    positional, this holds author-chosen state ({ lastInsertedId: row.id }). Costs
    nothing against max_calls, capped at 4096 bytes, and surfaces in the failure text
    and in structuredContent.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 just Sec-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-open where there is no token.
    Absent Origin stays 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 in TOOLPORT_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 from TOOLPORT_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/list with 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

Choose a tag to compare

@github-actions github-actions released this 09 Aug 20:43

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+16 switch view, / or Ctrl/Cmd+F focuses
    search, Ctrl/Cmd+N adds a server, Ctrl/Cmd+R refreshes, ? lists them.
    (SBS-143)
  • The window reopens where you left it instead of resetting to a fixed centered
    geometry on every launch. (SBS-144)
  • npx servers start about four times fewer processes. Toolport resolves the
    entry point directly instead of the cmd.exenpx → shim → server chain, which
    came to roughly 423 processes for 72 servers on one machine. Version ranges and
    @latest still go through npx. Because this skips the install step a server stays
    on the cached version; TOOLPORT_NO_DIRECT_SPAWN=1 restores the old path. (SOU-550)
  • Modern MCP extensions. Opaque client extension settings pass through to
    downstream servers and compatible declarations aggregate in server/discover;
    io.modelcontextprotocol/tasks handles stay bound to the server that created them
    and survive router rebuilds; clients can detect Toolport via app.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, and ui:// 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 --help and --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; Bearer WWW-Authenticate challenges are honored
    and validated through the existing TLS and SSRF guards; runtime insufficient_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 Date came
    back as {} and a BigInt as null, 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.bat keep their package identity on import and paste;
    only .exe, .cmd and .ps1 were 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-targets is error-free. A deny-by-default never_loop was 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 / --version on 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:...
Read more

Toolport v1.11.0

Choose a tag to compare

@github-actions github-actions released this 01 Aug 12:14
2684952

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

  1. Install 1.11.0 and open Toolport once so the new gateway is installed.
  2. Restart AI clients that were already open. A client can cache the old
    versioned gateway path until the client process exits.
  3. Shared HTTP users keep the same endpoint and bearer configuration. Modern
    clients can move to the sessionless flow; legacy clients need no changes.
  4. 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

Choose a tag to compare

@github-actions github-actions released this 30 Jul 01:28
475ded9

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 _meta and 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 under requestOptions.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. fetchResult shares 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 mcp key, so the shape of command decides 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)
  • fmtMs and fmtDollars moved into lib/utils with 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:

If we missed you, open an issue.

Upgrade notes

  1. Install 1.10.0 and open Toolport once, so the launch pass can stop obsolete gateways.
  2. 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.
  3. Nothing to configure for MCP 2026-07-28. Era detection is per connection, and clients on older revisions are unaffected.
  4. 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...
Read more

v1.9.6

Choose a tag to compare

@btsouth btsouth released this 27 Jul 01:40

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* / legacy conduit-gateway*). Hand-edited commands like npx mcp-remote are 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-in npx mcp-remote form for stdio-only clients (Claude Desktop and friends). Tokens are vaulted; ownership records never store bearers. (#491)
  • Machine-wide TOOLPORT_HTTP / CONDUIT_HTTP no longer hijacks client-spawned stdio gateways. When stdin is a pipe, env forms are ignored (with a warning). Prefer --http for 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 fetchResult handoff 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": false stay off. (#485)

Native MCP resources

  • Subscribe / unsubscribe and resources/updated fanout 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.enc load-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.com no longer skip the live probe. (#417 / #492)

Thanks

Huge thanks to everyone who filed reports, reviewed, or sent patches this cycle:

If we missed you, open an issue or ping us - credit should follow the work.

Upgrade notes

  1. Install 1.9.6 and open Toolport once so launch migration / re-point can run.
  2. Restart each AI client so it reloads MCP config.
  3. Existing registries with "codeMode": false stay off code mode.
  4. Prefer toolport-gateway --http over a machine-wide TOOLPORT_HTTP for services.

Full changelog

See CHANGELOG.md and the related PRs: #468, #472#486, #488, #489, #491, #492.