Repository navigation
Releases: Byte-Ventures/borg-mcp-client
Release list
borgmcp 6.2.0
Package
- npm: borgmcp@6.2.0
- Integrity:
sha512-JR6IOlNzQY+iuUaGOxD76F2ozvKa4VApylNIgSXJyJFCnnaXc9izfDmgdTwvYrCCeRRAJ4hVdm0vgjSIhwZ7Dw== - Published with npm Trusted Publishing.
Source
- Tag: v6.2.0
- Commit:
adb6fa911598abedf8f633384c29eda57e8ca4a8
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=falseconfiguration 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 outerexecutetool. - 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
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.
hono4.12.34 → 4.13.11 (through@modelcontextprotocol/sdkand@hono/node-server). Closes:- GHSA-gqvv-2mrq-wpjv: incomplete fix for CVE-2026-39408;
toSSG()can still write files outside the output directory. - GHSA-g6gw-c38x-mqfc: unbounded dot-notation nesting in
parseBody()can cause memory exhaustion. - GHSA-crvj-82cr-hjcx: the query parser reads parameters after the URL fragment.
- GHSA-gqvv-2mrq-wpjv: incomplete fix for CVE-2026-39408;
ip-address10.4.0 → 10.7.2 (through@modelcontextprotocol/sdkandexpress-rate-limit). Closes:- GHSA-rpw4-54j3-4h4q:
Address6.isLinkLocal()recognizesfe80::/64rather thanfe80::/10. - GHSA-2vr4-cq9g-pvrc: no classifier recognizes the NAT64 local-use range
64:ff9b:1::/48. - GHSA-j6r3-76f7-8jcv:
isInSubnet()andisHostInSubnet()compare addresses of different families as if they shared an address space. - GHSA-h3mg-xc3c-68pw:
Address6builds a parse diagnostic proportional to the input with no length bound.
- GHSA-rpw4-54j3-4h4q:
fast-uri3.1.7 → 3.1.8 (through@modelcontextprotocol/sdkandajv). Closes:- GHSA-hrr3-gc8f-f4qj: inconsistent host case normalization via percent-encoded octets.
Upgrading
- Run
borg update. - The shared contract pin is unchanged:
borgmcp-shared2.2.0.
borgmcp 6.0.2
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
borgpath. The same pass also checks, read only, whetherplugins,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 onborg 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 oneborg representative hermes-plugin installcommand per selectable worktree, keeping--hermes-homeand a given--session-key. An unconfirmed DM gets one command per candidate with--session-key, keeping--hermes-homeand a given--worktree. Every value in these commands is shell-quoted. - Control, C1 and bidi characters are removed from: displayed worktree and
borgpaths, 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, theborg launchprompt, theborg_regenwake-path warning and theborg_stream-statusself-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
/loopfallback 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
/loopwith 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
/loopwith no arguments, each tick only drains, and it tells the cube's coordinating role once, throughborg_log, that the Monitor was denied and the seat is polling.
- 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
borg-inbox-monitorreplays entries written while no Monitor was armed. This applies to the supported--state-rootform; the legacy positional form is unchanged.- The monitor keeps a replay cursor,
<state root>/<hash>.monitor.cursor, mode0600. 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 monitor keeps a replay cursor,
- The SessionStart and
/clearorientation 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 toborg_stream-status. The orientation's title is now# Borg drone orientation, and the cube, drone and role lines come after the instructions. borg_stream-statusalways shows a Claude seat its exact Monitor command, asinbox Monitor command.- Codex and OpenCode wake-path instructions are unchanged.
- The two stream-owner tests that failed when
TMPDIRis not a canonical path (as on macOS) now use a canonical test root. This only affects development.
Upgrading
- Run
borg update. - 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.
/clearis 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. - If a 6.0.0
borg representative hermes-plugin installstopped 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 itsconfig getcommands, may still have run. - The shared contract pin is unchanged:
borgmcp-shared2.2.0.
borgmcp 6.0.0
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-innode: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
CURRENTpointer 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 installmay 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,mcporlisten. - A database with another schema version refuses with
REPRESENTATIVE_STATE_VERSION.
- Bindings, the request ledger, delivery positions and wake state all live there, under generation directories and a
- 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 listenspeaks protocol 2 only.- It requires
--protocol 2, and stdin must be a pipe or socket owned by the host. Without these it printsrefusedand exits 2. - It prints
listening,wake,refusedandstoppedevents, 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-afterand 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.
- It requires
borg representative hermes-plugin installnow configures Hermes (see New).--forceis removed: a rerun updates an existing install.- The Hermes plugin settings
mcp_server,reinject_after_sandmax_reinjectsare 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
startupwake 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-push2.0.0).- It has three settings:
session_key,worktreeandborg_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.
- It has three settings:
borg representative hermes-plugin installsets Hermes up in one command.- It installs the plugin files and writes the settings,
allow_gateway_injectionand theborg-representativeMCP entry (lazy: true, the absoluteborgpath) 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 configuredsession_keythat 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-keycommand, keeping--hermes-homeand--worktree. config.yamlis 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_USERSor<PLATFORM>_ALLOW_ALL_USERS). It does not report other gateway access such as allowlists or pairing. --dry-runonly reads, and--no-restartleaves the activation pending, so a rerun finishes it.
- It installs the plugin files and writes the settings,
borg representative hermes-plugin uninstallremoves only Borg's entries and files. The unload stays pending until the gateway is confirmed to have dropped the plugin.borg updateactivates 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 statusreports 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-stateis 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
- Make sure Node.js is 22.13.0 or later.
- Run
borg update. With a 5.7.0 plugin installed, it upgrades the plugin, its settings and the MCP entry. It keeps a configuredsession_keywhen that key is a gateway DM key. - Run
borg representative hermes-plugin installwhenborg updatewarns that the activation is incomplete, or to install the plugin for the first time.- An already configured
session_keythat 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.
- An already configured
- If Hermes Desktop was open, run
/reload-mcpin its open chats, or restart it, when the install says the Borg MCP entry changed. - The shared contract pin is unchanged:
borgmcp-shared2.2.0.
borgmcp 5.7.0
borgmcp 5.7.0
- Adds a Borg-owned Hermes user plugin,
borg-representative-push, andborg 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.
- The command copies exactly the plugin's two files into
- When the bound Coordinator replies, the plugin wakes the one Hermes conversation named by its
session_keysetting.- It supervises
borg representative listeninside 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, thendeliver. No message body, sender or document passes through the plugin. - Gateway injection requires the Hermes permission
allow_gateway_injection: truefor the plugin.
- It supervises
- 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-representativetoolset disabled on every other platform, Desktop and CLI included. - Each undelivered reply wakes the conversation at most
1 + max_reinjectstimes (defaults:reinject_after_s600,max_reinjects3).- 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-delivercovers 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
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 withREPRESENTATIVE_LISTENER_SERVER_UNREACHABLE, and storage failures withREPRESENTATIVE_LISTENER_STORAGE_REFUSED. Hints carry no message text; content is still fetched withread. - Makes
borg_representative-readreplayable. 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 newborg_representative-delivertool after persisting them.deliveraccepts 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, andmax_bytes(4096 to 60000, default 32768) is the requested byte budget. The serialized result never exceedsmax(max_bytes, 16384)bytes;statusreports the constant floor 16384 asenvelope_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 withREPRESENTATIVE_READ_OVERSIZEand nothing moves. - Adds
binding_fingerprinttostatus,read,send,deliverand the listener'slisteningevent.prepare --rebindnow 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;statusreports 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
statuswithREPRESENTATIVE_CHECKPOINT_INVALIDuntil it is removed. Deduplicate replayed replies byentry_id. - Fixes stream-ownership status reads that could briefly report no owner while a live owner refreshed its lease. This affected
borg_stream-statusand the representative ownership status.
borgmcp 5.5.0
Package
- npm: borgmcp@5.5.0
- Integrity:
sha512-HwvYZ6WDPZWgK4Gk17itN3ILpVCuTBvdXhfYSxuSWVjqNLU7oTK+GKXmqTJCDkhDqE/xHmpj9ODX0ulHkus8EQ== - Published with npm Trusted Publishing.
Source
- Tag: v5.5.0
- Commit:
a207be435505ed848144abc7e393ff6b8bdc5085
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 prepareprepares and binds the connection without launching a coding agent;borg representative statusinspects it;borg representative mcpserves the connection over stdio. - Exposes exactly four MCP tools:
borg_representative-status,borg_representative-send,borg_representative-readandborg_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-authorizedandmodel-adviceare 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 originalrequest_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
Package
- npm: borgmcp@5.4.1
- Integrity:
sha512-TcJlewax+E/nuvPwXDC/cA1O6jrcvJJkLq9JMI3SYGpZ5JMA5ySBMYjpT4hqISoj7ouWEhXlljLzsncXSjMRLw== - Published with npm Trusted Publishing.
Source
- Tag: v5.4.1
- Commit:
2e438cf22bc1ae1df0fc835e15ff7107ea8cbf03
News and fixes
borgmcp 5.4.1
- Pins
borgmcp-shared2.2.0 so the published client/server pair is compatible again:borgmcp-server4.2.0 pins 2.2.0, and withborgmcp5.4.0 still on 2.1.0 the update preflight refused the mismatched pair (borg updatereported "published pair is incompatible" and attempted no mutation). No other changes.
borgmcp 5.4.0
Package
- npm: borgmcp@5.4.0
- Integrity:
sha512-3EGPwjgwS4XGsM2mDyCoZTEOgJQdnVsSwCKXf1Ejs2+5n87/tCqYErDRceuoWIy437bjQwQlJOE/yhvTHj7j6Q== - Published with npm Trusted Publishing.
Source
- Tag: v5.4.0
- Commit:
08c4e1346a1928dbba295758e0bf411d9b63fe15
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-serveras 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-statusreports the identity source, id, and observation age. The identity is never used for authorization, stream ownership, or liveness.
borgmcp 5.3.0
Package
- npm: borgmcp@5.3.0
- Integrity:
sha512-482PopJ7J1vatqrywCxqZnLLxdWaGO4MEHdxAvvBXYDEKPzIiR3xiiEZxJUaPdSisNmGuBtw8lt3iPYEAuLqxA== - Published with npm Trusted Publishing.
Source
- Tag: v5.3.0
- Commit:
7f940f8c62f1830863ae1bf6ed1419817b3a443c
News and fixes
borgmcp 5.3.0
- A Codex launch now runs only against a Borg-owned
codex app-serverover--remote. When the app-server does not become ready within a wall-clock budget after one retry,borgandborg assimilaterefuse the launch with the reason and exit 1 instead of starting a session without remote wake. A passthrough--remoteargument is refused. - Wake delivery resolves the Codex thread through the socket pinned into the MCP child at launch. The launch-recorded
codex-wake-targets.jsonis no longer written or read, and theborg_stream-statusCodex 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.