Skip to content

Toolport v1.17.0

Choose a tag to compare

@github-actions github-actions released this 23 Aug 22:32
· 54 commits to main since this release
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, the apply that
    lost the compare-and-set removed every file it had just written. If the apply that won
    had written its own block to those same paths (same markers), the loser stripped it;
    if the winner's write had failed, the loser stripped the only good block while the UI
    reported the new config. The lost apply now hands its written paths to whichever team
    is connected, whose next reconcile sees them as stale and rewrites them; only when no
    team remains at all (a disconnect won) is the block removed, which is what disconnect
    does to everything it has on record anyway. The other half of this ticket - a path
    whose cleanup failed being dropped from the record and stranded - was already fixed
    alongside SBS-917. (SBS-914)


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