Skip to content

Toolport v1.12.0

Choose a tag to compare

@github-actions github-actions released this 09 Aug 20:43
· 413 commits to main since this release

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 - installing the gateway no longer strips
    comments out of JSONC client configs (Zed, VS Code, Kilo Code) (#592).
  • adity982 - Crush MCP client support (#407).
  • georgeatparallel - Parallel Search in the
    curated catalog (#615).
  • aryansk - documented the gateway's public
    environment overrides (#640).
  • AshSgDe29071999 - documented curated stacks
    and replaced a hardcoded count that broke whenever one was added (#616).

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