Skip to content

Releases: Byte-Ventures/borg-mcp-client

borgmcp 6.2.0

Choose a tag to compare

@TheodorStorm TheodorStorm released this 28 Oct 16:36
adb6fa9

Package

  • npm: borgmcp@6.2.0
  • Integrity: sha512-JR6IOlNzQY+iuUaGOxD76F2ozvKa4VApylNIgSXJyJFCnnaXc9izfDmgdTwvYrCCeRRAJ4hVdm0vgjSIhwZ7Dw==
  • Published with npm Trusted Publishing.

Source

News and fixes

borgmcp 6.2.0

Borg now supports OpenCode 1 and OpenCode 2.1.2 or newer. Continue using
borg --cli opencode: Borg detects the installed executable on every launch
and selects the matching integration. Borg does not install OpenCode.

OpenCode 2

  • Uses native Code Mode and its generated tool declarations. Explicit
    mcp.servers.borg.codemode=false configuration keeps direct Borg tools.
  • Starts a private OpenCode API process and attaches the normal OpenCode
    interface after verifying the process and version. The first prompt is
    submitted once; wakeup retries reuse the durable message identity.
  • Binds coordination tools and wakeups to the selected drone and worktree.
    Launching from a worktree subdirectory works. Sending a prompt in another
    conversation in that worktree carries the launch permission policy into it.
  • Preserves ordered directory restrictions and exceptions. Launch-only
    directory grants stay within the selected paths, and Borg never
    automatically approves the outer execute tool.
  • Preserves native MCP configuration precedence and environment/file
    substitutions. The native server's API password is excluded from the Borg
    MCP child's configuration.

Persistent sessions and provider connections

Borg uses OpenCode's native sessions in a separate, persistent V2 database.
This keeps V2 database migrations away from the ordinary V1 database. Sessions
created through Borg can be resumed through Borg; --continue and --session
refer to that database. Ordinary OpenCode conversation history is not imported.

Saved provider accounts may need /connect in the OpenCode interface launched
through Borg. Provider configuration and environment credentials remain
available, and connections made in the Borg-launched interface persist for
later launches.

OpenCode 1 compatibility

OpenCode 1 retains its existing API and function plugin. Plugins are installed
per launch so the two generations can share ordinary user configuration without
loading each other's plugin API. Migration preserves user-edited plugin files.

Upgrading

Run borg update. The Node.js minimum remains 22.13.0 and the
shared contract remains pinned to borgmcp-shared 2.2.0. No server or
shared-package release is required for this client change.

borgmcp 6.0.3

Choose a tag to compare

@TheodorStorm TheodorStorm released this 30 Sep 10:24
9be060c

borgmcp 6.0.3

This release updates three runtime dependencies to versions that close published security advisories. It contains no changes to borgmcp's own code or CLI workflows.

All three packages are transitive dependencies of @modelcontextprotocol/sdk; the borgmcp source does not import them directly. These notes make no claim about whether borgmcp reaches the affected code paths.

Updated dependencies

The versions listed below are the ones the repository's lockfile pins and CI tests. The published package contains no npm-shrinkwrap.json, so an npm install, including borg update, resolves each package within its dependents' declared ranges, and the installed versions can differ from the ones listed.

  • hono 4.12.34 → 4.13.11 (through @modelcontextprotocol/sdk and @hono/node-server). Closes:
  • ip-address 10.4.0 → 10.7.2 (through @modelcontextprotocol/sdk and express-rate-limit). Closes:
    • GHSA-rpw4-54j3-4h4q: Address6.isLinkLocal() recognizes fe80::/64 rather than fe80::/10.
    • GHSA-2vr4-cq9g-pvrc: no classifier recognizes the NAT64 local-use range 64:ff9b:1::/48.
    • GHSA-j6r3-76f7-8jcv: isInSubnet() and isHostInSubnet() compare addresses of different families as if they shared an address space.
    • GHSA-h3mg-xc3c-68pw: Address6 builds a parse diagnostic proportional to the input with no length bound.
  • fast-uri 3.1.7 → 3.1.8 (through @modelcontextprotocol/sdk and ajv). Closes:

Upgrading

  1. Run borg update.
  2. The shared contract pin is unchanged: borgmcp-shared 2.2.0.

borgmcp 6.0.2

Choose a tag to compare

@TheodorStorm TheodorStorm released this 30 Sep 08:07
a8de7db

borgmcp 6.0.2

This release fixes the order of borg representative hermes-plugin install and closes gaps in the Claude wake path. 6.0.1 was prepared but never published, so 6.0.2 is the first release after 6.0.0 and carries both changes. Merged since 6.0.0: #601, #602 (the unpublished 6.0.1 preparation), #603 and #605 (CI only: a manual workflow trigger and a public-source scan in PR CI; no installed behaviour changes).

Fixed: hermes-plugin install asks only after everything that can refuse has been checked

On 6.0.0, with two gateway DMs and three prepared worktrees, install asked the operator to pick a DM and confirm it [y/N]. Only after that did it refuse because several worktrees were prepared, and it printed a bare path list with no command to retry.

  • Install now runs as plan, validate, ask, apply.
    • Plan: it lists every candidate worktree and conversation, and builds the exact values it would write.
    • Validate: it checks every candidate's plan with the same checks the writes apply, before any question. These are the value encoding (for example, a path containing a newline), the gateway DM key and the absolute borg path. The same pass also checks, read only, whether plugins, config.yaml, the backup directories and Borg's state path are usable.
    • Ask: only then does it ask, at a terminal.
    • Apply: it writes. After a question, install can stop only on an invalid or declined answer, a Hermes command's reply, or a write that fails.
  • When a selection is needed, install asks at a terminal: for the worktree when several selectable worktrees are prepared, then for the DM, then for the [y/N] confirmation (default no). A --worktree, or a configured worktree that is still prepared, and a --session-key, or a configured valid DM key, are reused without a question, including without a terminal and on borg update.
  • Candidates that would fail are not offered. They are shown as not selectable, with the reason. A single selectable worktree is used without a question.
  • Without a terminal (including borg update), a selection that is still needed is refused with exact retry commands. Ambiguous worktrees get one borg representative hermes-plugin install command per selectable worktree, keeping --hermes-home and a given --session-key. An unconfirmed DM gets one command per candidate with --session-key, keeping --hermes-home and a given --worktree. Every value in these commands is shell-quoted.
  • Control, C1 and bidi characters are removed from: displayed worktree and borg paths, the planned configuration values, and failure and rollback text (which can quote Hermes config values or replies). Other displayed paths, such as the Hermes home and the backup path, are printed as they are.
    • Retry commands are printed separately, with their exact bytes, so they round-trip even when a path contains a newline or other control characters.

Fixed: the Claude wake path

  • One wake-path text for every Claude surface. The SessionStart hook, /clear, the borg launch prompt, the borg_regen wake-path warning and the borg_stream-status self-arm section now render the same wake-path and recovery instructions from one source.
  • Re-arm before draining. A new Monitor starts at the end of the inbox, so after its exit notification, or when none is armed, a drone now re-arms the Monitor first and then drains borg_read-log unread_only=true.
  • A /loop fallback when the Monitor cannot be armed.
    • If a permission prompt blocks the Monitor call, or it times out, gets no classifier verdict or errors: the drone does not stop or wait. It invokes /loop with no arguments, and on each tick re-arms the Monitor, then drains. With a Monitor armed, the loop is only a fallback.
    • If a human denies the Monitor call: the drone never requests it again. It invokes /loop with no arguments, each tick only drains, and it tells the cube's coordinating role once, through borg_log, that the Monitor was denied and the seat is polling.
  • borg-inbox-monitor replays entries written while no Monitor was armed. This applies to the supported --state-root form; the legacy positional form is unchanged.
    • The monitor keeps a replay cursor, <state root>/<hash>.monitor.cursor, mode 0600. It holds a hash of the last handled entry line, that line's timestamp and an arm boundary, and no message text.
    • At arm it announces the entries written after the previous monitor for the same inbox stopped. At most the newest 20 are shown; when more are waiting, one notice line says how many were not shown and to drain borg_read-log unread_only=true.
    • Delivery is at least once, not exactly once: after a crash or kill, an entry can be announced again at the next arm.
    • A state root with no cursor, including the first arm after upgrading, skips history as before.
    • A cursor that is unusable is ignored with a warning. A cursor that cannot be written is reported once and never stops delivery.
    • Orphan inbox cleanup removes the cursor with the monitor's other state files.
  • The SessionStart and /clear orientation stays within its preview. The harness shows about 2 KB of hook output. The orientation now puts every fixed instruction first. The cube, drone and role follow, fitted to the remaining bytes. The Monitor command comes last. When the whole text would reach 2048 bytes, the command is replaced by a pointer to borg_stream-status. The orientation's title is now # Borg drone orientation, and the cube, drone and role lines come after the instructions.
  • borg_stream-status always shows a Claude seat its exact Monitor command, as inbox Monitor command.
  • Codex and OpenCode wake-path instructions are unchanged.
  • The two stream-owner tests that failed when TMPDIR is not a canonical path (as on macOS) now use a canonical test root. This only affects development.

Upgrading

  1. Run borg update.
  2. Restart each Claude seat: exit it and launch it again. On launch, the SessionStart hook loads the new wake-path instructions, the seat's borg MCP server loads 6.0.2, and the seat arms a 6.0.2 inbox Monitor. /clear is not enough: it restarts neither the running Monitor process nor the borg MCP server, and a re-armed monitor yields to the pre-update monitor that still holds the inbox. The first 6.0.2 arm has no cursor yet and skips history; later arms replay what they missed.
  3. If a 6.0.0 borg representative hermes-plugin install stopped before it finished, run it again. A 6.0.0 run that refused on the worktrees had not written Borg's plugin files, Hermes configuration or activation record. Hermes's own startup maintenance, which runs with its config get commands, may still have run.
  4. The shared contract pin is unchanged: borgmcp-shared 2.2.0.

borgmcp 6.0.0

Choose a tag to compare

@TheodorStorm TheodorStorm released this 29 Sep 07:10
e6a7fac

borgmcp 6.0.0

The human representative's host integration is rebuilt as one system. Representative state moves into one SQLite database, and the listener becomes the push engine. The Hermes plugin is now a thin adapter, and one command sets Hermes up. Merged since 5.7.0: #594, #595, #596, #597 and #598.

Breaking

  • Node.js 22.13.0 or later is required (engines.node >=22.13.0, up from >=22.12.0). Representative state uses Node's built-in node:sqlite, which is unflagged from 22.13.0.
  • Representative state is rebuilt in one SQLite database, <Borg config>/representative/state/.
    • Bindings, the request ledger, delivery positions and wake state all live there, under generation directories and a CURRENT pointer that is replaced by atomic rename.
    • Every path is checked on every open: owned by you, 0700 directories, 0600 files, never a symlink.
    • The borgmcp 5.x files are imported once, when the database is first created. Each valid 5.x binding is imported (a second binding of the same generation is skipped), and its delivery start follows the documented start rule. Until that database exists, hermes-plugin install may also read the 5.x bindings, read only and on each run, to find the prepared worktree; it creates nothing. The 5.x files are never written, and never read again once the database exists.
    • The database is created on first use by borg representative prepare, mcp or listen.
    • A database with another schema version refuses with REPRESENTATIVE_STATE_VERSION.
  • The single tools lease is removed. Any number of processes may use the representative tools at once, for example Hermes Desktop, the CLI and the gateway. Each operation takes its own transaction.
  • borg representative listen speaks protocol 2 only.
    • It requires --protocol 2, and stdin must be a pipe or socket owned by the host. Without these it prints refused and exits 2.
    • It prints listening, wake, refused and stopped events, with no message body, and reads {wake_id, accepted} acks of at most 1 KiB each on stdin.
    • EOF on stdin, or SIGTERM/SIGINT, stops it from the first startup step on.
    • --replay-after and the inbox replay are removed.
    • A 5.7.0 plugin, which starts the listener without --protocol 2, is refused and stops. Upgrade the plugin as below.
  • borg representative hermes-plugin install now configures Hermes (see New). --force is removed: a rerun updates an existing install.
  • The Hermes plugin settings mcp_server, reinject_after_s and max_reinjects are gone. Plugin v2 keeps no state of its own and no longer uses <Hermes home>/plugin-data/borg-representative-push/.

New

  • The listener is the push engine.
    • Wake state is durable, and one wake batch is outstanding at a time, with a 2-second debounce.
    • The ack deadline is 60 seconds, measured absolutely.
    • An undelivered reply is woken again after 10 minutes, 1 hour and 6 hours, then every 24 hours.
    • A refused wake backs off from 30 seconds, doubling to 30 minutes.
    • On start, a bounded startup cohort ends in one startup wake for replies that are still undelivered.
    • Delivery is observed from any process; Borg stops waking for a reply once it is delivered.
    • Invalid wake state is rebuilt from the delivered checkpoint instead of blocking the listener.
  • Hermes plugin v2 (borg-representative-push 2.0.0).
    • It has three settings: session_key, worktree and borg_command.
    • It starts the listener only inside the messaging gateway and owns the listener's stdin pipe, so the listener exits when the gateway does.
    • Each wake becomes one fixed, body-free injected message.
    • A listener that does not announce protocol 2 first is never driven: it is closed, then sent SIGTERM, then SIGKILL, and reaped.
  • borg representative hermes-plugin install sets Hermes up in one command.
    • It installs the plugin files and writes the settings, allow_gateway_injection and the borg-representative MCP entry (lazy: true, the absolute borg path) through Hermes's own CLI, reading every value back.
    • It enables the plugin.
    • It restarts the gateway only when it runs as a launchd or systemd service, and confirms the restart by a new PID. For a hand-started gateway it prints the command instead.
    • It stops and restarts nothing in Hermes Desktop. It prints "new chats get the Borg tools" for an added MCP entry, and the /reload-mcp-or-restart line for a changed one.
    • It never picks the conversation to wake without your confirmation. A --session-key, or an already configured session_key that is a valid gateway DM key, is used without a prompt and without a refusal. Otherwise, at a terminal it lists every gateway DM candidate and asks [y/N]; without a terminal it refuses and prints the exact --session-key command, keeping --hermes-home and --worktree.
    • config.yaml is backed up first. On failure Borg reverses only its own keys.
    • Nothing is written before its checks pass, and the installer never creates representative state.
    • It reports whether a gateway allow-all switch is set (gateway.allow_all_users, allow_all_users, GATEWAY_ALLOW_ALL_USERS or <PLATFORM>_ALLOW_ALL_USERS). It does not report other gateway access such as allowlists or pairing.
    • --dry-run only reads, and --no-restart leaves the activation pending, so a rerun finishes it.
  • borg representative hermes-plugin uninstall removes only Borg's entries and files. The unload stays pending until the gateway is confirmed to have dropped the plugin.
  • borg update activates an installed plugin.
    • It refreshes the files, settings and MCP entry, and never asks a question.
    • An activation that fails or throws produces a warning naming borg representative hermes-plugin install. It never changes the update's result. An activation left pending on a hand-started gateway is not a failure: it prints the command to restart that gateway.
  • borg representative status reports the listener, its wake state and, when the plugin is installed, hermes_plugin: its activation state, a pending Desktop reload, the conversation and the open-gateway report.
  • borg representative reset-state is disaster recovery for a corrupt state database. It refuses a healthy database and reports exactly what was kept and what was lost.

Fixed

  • The Hermes plugin guide's refused-wake wording was qualified to match the 5.7.0 release notes (#594). Plugin v2 in this release replaces that whole mechanism.
  • The test suite refuses writes into the real home directory at the file-system layer; only isolated test roots are allowed. This only affects development.
  • A failing cross-process state test can no longer hang the suite: its child processes wait with a deadline, and every child still running is killed and reaped after each test (7583e50). This only affects development.

Upgrading

  1. Make sure Node.js is 22.13.0 or later.
  2. Run borg update. With a 5.7.0 plugin installed, it upgrades the plugin, its settings and the MCP entry. It keeps a configured session_key when that key is a gateway DM key.
  3. Run borg representative hermes-plugin install when borg update warns that the activation is incomplete, or to install the plugin for the first time.
    • An already configured session_key that is a valid gateway DM key is kept without a prompt. Otherwise, at a terminal, confirm the gateway DM to wake, or pass --session-key.
  4. If Hermes Desktop was open, run /reload-mcp in its open chats, or restart it, when the install says the Borg MCP entry changed.
  5. The shared contract pin is unchanged: borgmcp-shared 2.2.0.

borgmcp 5.7.0

Choose a tag to compare

@TheodorStorm TheodorStorm released this 28 Sep 11:24
e322e2c

borgmcp 5.7.0

  • Adds a Borg-owned Hermes user plugin, borg-representative-push, and borg representative hermes-plugin install [--hermes-home <path>] [--force] to place it.
    • The command copies exactly the plugin's two files into <Hermes home>/plugins/borg-representative-push/ and prints the configuration to add.
    • It refuses to replace an existing install without --force, refuses a symbolic-link target and a missing Hermes home, never edits Hermes config and never starts or restarts Hermes.
  • When the bound Coordinator replies, the plugin wakes the one Hermes conversation named by its session_key setting.
    • It supervises borg representative listen inside the Hermes messaging gateway only. The listener starts when that platform connects and never in the CLI, Desktop or worker processes.
    • Only a messaging-gateway conversation (Telegram, Discord, Slack and the other gateway platforms) can be woken. A Hermes Desktop chat cannot be woken: Hermes injects plugin messages only into gateway conversations.
    • The injected message is one fixed text that tells the conversation to call read, persist and relay, then deliver. No message body, sender or document passes through the plugin.
    • Gateway injection requires the Hermes permission allow_gateway_injection: true for the plugin.
  • Only one process may use the representative tools. The woken conversation runs in the gateway, so the gateway must own the tools lease. The guide shows how to scope it: a lazy MCP entry, and the mcp-borg-representative toolset disabled on every other platform, Desktop and CLI included.
  • Each undelivered reply wakes the conversation at most 1 + max_reinjects times (defaults: reinject_after_s 600, max_reinjects 3).
    • Every wake is counted in the plugin's state before Hermes is asked to start the turn, so a replayed hint or a gateway restart does not renew the count. A refused wake's reservation is rolled back when the state update succeeds; if that rollback fails, the wake is conservatively counted as spent.
    • A record is removed only when an observed borg_representative-deliver covers that reply.
    • A terminal listener stop (a binding refusal, or eviction, rebind, revocation or trust change) ends every wake, including queued and repeat wakes, until the gateway restarts.
  • Wakes fail closed on a state write error. If the wake count cannot be written, the plugin does not wake the conversation and logs why.
  • Plugin state lives in <Hermes home>/plugin-data/borg-representative-push/.
    • The plugin refuses that directory if it is a symbolic link and sets it to mode 0700.
    • Its files are created with mode 0600, and it never reads or writes through a symbolic link planted there.

borgmcp 5.6.0

Choose a tag to compare

@TheodorStorm TheodorStorm released this 28 Sep 06:50
491114b

borgmcp 5.6.0

  • Adds borg representative listen --worktree <path> [--replay-after <entry_id>], a separate supervised process that emits body-free JSON wake hints for new Coordinator replies on stdout. It keeps a private, bounded inbox (above 1024 lines it trims to 512) and can replay retained hints after the host's own queue checkpoint. It holds its own exclusive listener lease, independent of the MCP tools lease. It reconnects with backoff after transport failures and stops with a typed reason on eviction, rebind, revocation, trust change or lost lease. A server that cannot be reached at startup is refused with REPRESENTATIVE_LISTENER_SERVER_UNREACHABLE, and storage failures with REPRESENTATIVE_LISTENER_STORAGE_REFUSED. Hints carry no message text; content is still fetched with read.
  • Makes borg_representative-read replayable. It returns the bound Coordinator's replies after a per-binding delivered checkpoint, oldest first, and changes nothing. The same replies come back until the host calls the new borg_representative-deliver tool after persisting them. deliver accepts only a reply that a read actually returned since the checkpoint last moved; the same or an older id is a no-op. After a crash at any point, the next read returns every reply not yet delivered.
  • Bounds each read: limit (1 to 50, default 10) is a hard cap on returned replies, and max_bytes (4096 to 60000, default 32768) is the requested byte budget. The serialized result never exceeds max(max_bytes, 16384) bytes; status reports the constant floor 16384 as envelope_floor. Replies are whole or omitted and message text is never cut. An oversize reply is returned alone, with its citations reduced to ids if needed. If it still cannot fit, the read is refused with REPRESENTATIVE_READ_OVERSIZE and nothing moves.
  • Adds binding_fingerprint to status, read, send, deliver and the listener's listening event. prepare --rebind now always starts a new binding generation, even for an unchanged selection. A running MCP adapter or listener refuses or stops after any rebind until it is restarted; status reports the pinned and the current fingerprint.
  • Upgrading from 5.5.0: the first read or deliver starts the checkpoint where the old destructive read left the representative's unread cursor, once per representative drone and server. The legacy cursor is imported only from a genuine private file; otherwise every addressed reply is returned. The checkpoint is stored privately; a corrupt checkpoint file stops every tool except status with REPRESENTATIVE_CHECKPOINT_INVALID until it is removed. Deduplicate replayed replies by entry_id.
  • Fixes stream-ownership status reads that could briefly report no owner while a live owner refreshed its lease. This affected borg_stream-status and the representative ownership status.

borgmcp 5.5.0

Choose a tag to compare

@TheodorStorm TheodorStorm released this 27 Sep 08:14
a207be4

Package

  • npm: borgmcp@5.5.0
  • Integrity: sha512-HwvYZ6WDPZWgK4Gk17itN3ILpVCuTBvdXhfYSxuSWVjqNLU7oTK+GKXmqTJCDkhDqE/xHmpj9ODX0ulHkus8EQ==
  • Published with npm Trusted Publishing.

Source

News and fixes

borgmcp 5.5.0

  • Adds borg representative, a client-side interface for a standard MCP host to relay a human's requests to one existing Coordinator (the agent responsible for coordinating the work) and read its replies. borg representative prepare prepares and binds the connection without launching a coding agent; borg representative status inspects it; borg representative mcp serves the connection over stdio.
  • Exposes exactly four MCP tools: borg_representative-status, borg_representative-send, borg_representative-read and borg_representative-ack. Sends go only to the bound Coordinator. This narrow interface is enforced by the client, not a new server authorization boundary. Relay messages identify themselves as automated; user-authorized and model-advice are unverified assertions, not proof of human authorization.
  • Enforces exclusive host-process ownership on the first send, read or acknowledgement. Other processes receive an ownership refusal; status remains read-only. A dead owner or expired lease permits takeover, and a process that loses ownership must restart. Private lease storage rejects unsafe paths and permissions; a private lease directory that is not 0700 is refused with manual recovery steps.
  • Delivery remains explicit: there is no background wake or push into the host. Reads consume everything they fetch, and an acknowledgement is a receipt, not a delivery checkpoint. The host must persist read results, map request IDs to conversations, route replies by in_reply_to, and hold unmatched replies for the human. A local lease cannot cancel an in-flight request and is not distributed fencing; retry an ambiguous send with its original request_id.
  • Fixes a credential-store lock turnover race: concurrent clients retry acquisition when the lock changes during inspection, while data-file identity changes still fail closed.

borgmcp 5.4.1

Choose a tag to compare

@TheodorStorm TheodorStorm released this 06 Sep 15:52
2e438cf

Package

  • npm: borgmcp@5.4.1
  • Integrity: sha512-TcJlewax+E/nuvPwXDC/cA1O6jrcvJJkLq9JMI3SYGpZ5JMA5ySBMYjpT4hqISoj7ouWEhXlljLzsncXSjMRLw==
  • Published with npm Trusted Publishing.

Source

News and fixes

borgmcp 5.4.1

  • Pins borgmcp-shared 2.2.0 so the published client/server pair is compatible again: borgmcp-server 4.2.0 pins 2.2.0, and with borgmcp 5.4.0 still on 2.1.0 the update preflight refused the mismatched pair (borg update reported "published pair is incompatible" and attempted no mutation). No other changes.

borgmcp 5.4.0

Choose a tag to compare

@TheodorStorm TheodorStorm released this 05 Sep 13:20
08c4e13

Package

  • npm: borgmcp@5.4.0
  • Integrity: sha512-3EGPwjgwS4XGsM2mDyCoZTEOgJQdnVsSwCKXf1Ejs2+5n87/tCqYErDRceuoWIy437bjQwQlJOE/yhvTHj7j6Q==
  • Published with npm Trusted Publishing.

Source

News and fixes

borgmcp 5.4.0

  • Adds a real-process wake-path regression suite that runs in CI on the current Node line: it starts the pinned borgmcp-server as a real process, enrolls ten independently credentialed seats, runs ten MCP children as the three agent kinds against simulated agent endpoints, and asserts broadcast, direct, and ack wake targeting, paging of a large backlog, lossless reconnect replay, degraded status for a stopped monitor or dead Codex bridge, exactly-once unread drain, and that every Codex wake targets the user thread. Vendor CLIs are simulated; the OpenCode session-lifecycle matrix is not covered.
  • Gives the MCP child a stable agent session identity per harness, or an explicit unknown: Claude through a per-launch correlation matched against the SessionStart hook's recorded session id, Codex through the single user thread in the Borg-owned app-server, OpenCode through its launch-bound session. ARRIVAL announcements are deduplicated per conversation, so a repeat is suppressed across an MCP reconnect and announced after a new conversation or when the identity is unknown. A Claude resume whose SessionStart hook failed can leave the previous identity in place; that residual is documented.
  • Serializes lifecycle-state updates across processes so concurrent seats no longer overwrite each other's recorded arrivals.
  • borg_stream-status reports the identity source, id, and observation age. The identity is never used for authorization, stream ownership, or liveness.

borgmcp 5.3.0

Choose a tag to compare

@TheodorStorm TheodorStorm released this 05 Sep 09:37
7f940f8

Package

  • npm: borgmcp@5.3.0
  • Integrity: sha512-482PopJ7J1vatqrywCxqZnLLxdWaGO4MEHdxAvvBXYDEKPzIiR3xiiEZxJUaPdSisNmGuBtw8lt3iPYEAuLqxA==
  • Published with npm Trusted Publishing.

Source

News and fixes

borgmcp 5.3.0

  • A Codex launch now runs only against a Borg-owned codex app-server over --remote. When the app-server does not become ready within a wall-clock budget after one retry, borg and borg assimilate refuse the launch with the reason and exit 1 instead of starting a session without remote wake. A passthrough --remote argument is refused.
  • Wake delivery resolves the Codex thread through the socket pinned into the MCP child at launch. The launch-recorded codex-wake-targets.json is no longer written or read, and the borg_stream-status Codex bridge probe reads that live socket.
  • Wake targeting never selects a Codex system, ephemeral, or subagent thread. Codex CLI 0.153.4 keeps a system ephemeral thread loaded next to the TUI thread for a period after launch (observed present 12 minutes after launch and gone 63 minutes after launch on four sessions), and it previously captured every wake in that window.
  • A wake that finds no loaded thread enters the retry drain instead of waiting for the next entry or the heartbeat, and a server re-ping for an entry already on disk wakes Codex.