Skip to content

Toolport v1.15.0

Choose a tag to compare

@github-actions github-actions released this 21 Aug 00:27
· 130 commits to main since this release
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 next release would leave brew install --cask tsouth89/toolport/toolport
    stale again. The snapshot now matches 1.14.0 (sha256s from the published dmgs)
    and the release doc names tsouth89/homebrew-toolport. The live tap is a
    separate repo; bumping it is that step, not this file. (SBS-936)

  • A refused Team Instructions rewrite no longer deletes the last-good org rules.
    apply_instructions_to only recorded a target when write_target returned
    Applied. Error, TooLong (a Devin Desktop char-cap miss) and BlockedOverride then
    hit remove_recorded, which stripped the working v1 block, persisted the new
    content watermark, and left later syncs with nothing to retry — coverage of
    the missing file against too-long v2 is TooLong, not Stale. Last-good now
    stays on disk and in the recorded set when a rewrite is refused; a real
    removal (org cleared, client gone, path moved) still cleans up. (SBS-917)

  • Kimi Shared HTTP Connect writes url, not Qwen's httpUrl. Connect, rescope
    and reset for Kimi went through the generic JSON editor, which remapped remotes
    via Qwen's urlhttpUrl whenever the map key was not VS Code "servers".
    Kimi requires url and rejects httpUrl. The Kimi writer already emitted the
    right shape; Shared HTTP now uses that same formatter. (SBS-921)

  • Agent rules Preview no longer looks like a dead button. The preview card renders
    after the clients list, so on any window too short to reach it, clicking Preview
    scrolled nothing and showed nothing: the card was open the whole time, below the fold.
    Opening a preview now brings the card into view, and its header names the client, which
    the path alone does not settle wherever two clients share one file (Claude Code / VS
    Code, Gemini CLI / Antigravity). Deliberately still an inline card rather than a dialog:
    a modal makes the page behind it inert, and this card is a live panel that clears itself
    when the editor or the view moves under it. The scroll is not animated for anyone whose
    system asks for reduced motion: index.css already zeroes scroll-behavior, but an
    explicit behavior in the options dict beats that CSS property, so the component reads
    the preference itself.

  • docs/agent-rules.md now names every client with no rules file, not two of them.
    The "no rules file Toolport can write" section named only Cursor and Warp, but the
    Clients section builds that list from whatever is detected, so a user with LM Studio,
    Jan, Hermes, Claude Desktop or Continue installed saw names the doc never mentioned.
    All seven are now listed with the reason each one is on it, including the note that
    Claude Desktop there is the chat app - Claude Code inside the desktop app shares
    ~/.claude and is already covered by the Claude Code row. Continue's stale
    "deferred" comment in clients.rs is corrected too: its .continue/rules/ is
    per-project and its user-level rules are a rules: array inside
    ~/.continue/config.yaml, which fits neither of Toolport's strategies, and
    continuedev/continue was archived read-only in June 2026 besides. Continue stays a
    detected MCP client. Docs and a comment only; no behaviour change.

Internal

  • Dead McpSession::upstream_call shim removed. Nothing called it.
    (#805, thanks @forever-ivy)

  • CI apt installs are bounded and retried instead of hanging. apt-get update on the hosted runners intermittently stops responding rather than
    failing, which burned three jobs' entire timeouts on one pull request without
    ever reaching a compiler, and a run stuck in progress also blocks
    gh run rerun --failed. scripts/ci-apt-install.sh now bounds each attempt,
    retries, gives apt a short enough acquire timeout to fail over to its backup
    mirrors on its own, downloads before installing so a throttled mirror is
    interrupted rather than crept through, and installs from cache with
    --no-download so the final step has no network left to stall on.

  • Every CI job now has a timeout, and apt retries instead of hanging. Rust Clippy
    and Linux build + test carried no timeout-minutes, so they inherited GitHub's
    6-hour default; the jobs in audit, docker-publish, release and winget had none
    either. A flaky Ubuntu mirror made that concrete on one pull request, stalling
    apt-get update in three separate jobs without ever reaching a compiler. An unbounded
    hang is the worst shape available: it burns the whole budget, and a run stuck in
    progress also blocks gh run rerun --failed on the jobs that genuinely failed, so
    recovery needs a manual cancel. All 14 jobs across the 6 workflows now carry an
    explicit backstop, sized per job and generous where a tight limit could kill a real
    release. The five apt-get update call sites are now one script,
    scripts/ci-apt-install.sh, which bounds each attempt and retries, so a bad mirror
    costs seconds rather than a job.

  • The headless security smoke no longer fails on a stderr race. expectBindRefusal
    read the gateway's captured stderr as soon as the process emitted exit, but Node fires
    that while piped stdio can still hold undelivered bytes. The Windows runner duly reported
    refusing to bind 0.0.0.0 without the without HTTP authentication tail the assertion
    matches on, failing a run in which the gateway had behaved correctly - and the identical
    assertion passed on the next host, which is the signature of a race, not a defect. The
    capture is now read only after stderr closes, bounded so a stream that never closes
    cannot wedge a security check.

  • A Windows write now retries instead of losing the race. atomic_write published its temp
    file with a single rename. On Windows that fails outright while any other handle holds the
    destination, and something usually does - Defender, the search indexer, a backup agent - for
    a few milliseconds. hooks::tests::preview_text_is_the_bytes_install_actually_writes duly
    failed one Windows run with install_at returning an error while the identical test passed
    everywhere else. This is not only a test problem: users writing settings.json on a machine
    with antivirus hit the same edge. The publish rename now retries with a capped backoff, bounded
    at 8 attempts so a genuinely locked destination still reports its error. Unix is untouched,
    where rename(2) is atomic against open handles and a failure is real.

  • Three more sources of intermittent test failure, found by auditing rather than waiting.
    The Windows Job Object test waited for its pid file to exist and then read it once, but the
    launcher creates the file and fills it in separate operations, so a read in between parses an
    empty string and panics - the Unix sibling had been fixed for exactly this and the fix was
    never mirrored. LockTimeoutOverride saved and restored the lock-timeout variable per guard,
    so with several suites holding it at once the first one out reverted it while the others were
    still relying on it, dropping them to the 5s production default mid-test; it is now refcounted.
    And the HTTP concurrency test asserted an absolute 400ms wall-clock budget after two
    guess-sleeps, which a loaded runner can miss while behaving correctly; it now waits for a real
    signal that the slow call has parked and asserts the property it means - that the fast response
    came back while the slow one was still in flight.

Thanks

Four of the patches in this release came from outside.

  • forever-ivy - the stale connection-test
    verdict, so a test whose connection details changed underneath it stops
    reporting on the old ones (#814), and the removal of a dead
    McpSession::upstream_call shim (#805).
  • YuukiRitoTeng - stopping the HTTP bridge
    now reports a failure instead of a false success, and keeps the child handle so
    a later stop can retry (#788).
  • rohankumardubey - stack loading
    failures surfaced in the catalog and in onboarding, instead of a failed fetch
    rendering as "no stacks" with no way to retry (#817).

Full changelog: https://github.com/tsouth89/toolport/blob/v1.15.0/CHANGELOG.md