Repository navigation
router: one MCP server over local and remote box hosts, with compact output - #55
Merged
Merged
Conversation
…output box mcp fronts the hosts in ~/.box/hosts.yaml besides the local box. A host is reached only over ssh: box __ensure-serve starts its own box serve, and one ssh forward per host brings that owner-only socket here, into a 0700 directory. No network listener is opened. A sandbox there is host/name; a bare name is the local one, else the only host with it. Where several remote hosts have it, up and run are placed (where it is up or warm, then the least loaded under max_up); anything acting on one copy's state must name the host. An unreachable host is reported, not fatal, and retried after a pause; after a quiet spell a host is checked before use, since its box serve exits when idle or upgraded. box hosts shows them. The MCP tools spend fewer of the agent's tokens: - command output loses escapes and progress redraws, collapses repeated lines, and is cut to its first and last lines (8 KiB a stream by default, --output-budget); the rest is kept in memory for a new output tool, by line range or regex - read_file, diff and list_sandboxes answer "unchanged" when the fresh result is identical to what the agent already holds; every read still reaches the sandbox, so it is never stale, and full: true shows it again - the sandbox list is one line per sandbox instead of indented JSON Host lists are validated before ssh sees them: no destination ssh could read as an option, no shell metacharacters in the remote binary path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
box mcpcan now front other machines running box, and its tools use fewer of the agent's tokens.Remote hosts
List them in
~/.box/hosts.yamlandbox mcproutes to them alongside the local box. A sandbox there ishost/name. Without the file, nothing changes.box __ensure-serveon the host. That starts the host's ownbox serve(idle limit 30m) and prints its socket. Onessh -Lper host then forwards that owner-only socket into a0700directory here.upandrunare placed: where it is already up or warm, then the least loaded host undermax_up.diff,fork,applyanddiscardmust name the host, since each copy has its own state./v1/hosts,box hosts) rather than failing the list, and is retried after a pause. After a quiet spell a host is pinged before use, because itsbox serveexits when idle or upgraded.-oProxyCommand=…), and no shell metacharacters in the remote binary path. ssh runs in BatchMode, and host-key checking is left to the user's ssh config. SECURITY.md has a new section on this.Fewer tokens
\rprogress redraws removed and repeated lines collapsed. It is then cut to its first and last lines: 8 KiB per stream by default (--output-budget), replacing the 64 KiB head-only clip. The cut is marked with an id, and the newoutputtool returns any slice by line range or regex. Full outputs are kept in memory only, capped at 64 MiB per session.read_file,diffandlist_sandboxesanswerunchangedwhen the fresh result is identical to what the agent already has, including a file it just wrote. Every read still reaches the sandbox, so the answer is never stale, andfull: trueshows it again.Code
internal/router: the router is anhttp.Handlerspeaking the same/v1API, somcp.Newtakes it in place ofserver.NewHandler.hosts.gohas the config and the ssh transport.internal/mcp:compact.go(cleaning, clipping, the spool, theoutputtool) andseen.go.mcp.Newnow takesOptions.internal/cli: newbox hosts, the hiddenbox __ensure-serve, and newbox mcpflags--hosts,--local-only,--output-budget.Checked
ssh(the test binary forwarding the socket in Go), under-race. They cover name resolution, placement andmax_up, forks staying on their host, a down host, a server that exits and comes back, and config validation.sshand a second box home:box hosts, remotebox serveautostart and reuse, qualified listing, bare-name resolution to the remote host, an "unchanged" re-list, and remote error passthrough.kvmrunner is registered upstream (A self-hosted KVM runner for the nightly escape suite #31): https://github.com/KoLDXr00T/bluebox/actions/runs/39482571791. podman/krun not covered (stock Ubuntu crun lacks libkrun).Not checked: real
execoutput through the new compaction (unit tests only; no KVM where this was written) and a real ssh login (stand-in only). Tryingssh: localhoston a host with sshd and KVM would close both.Not in this PR
RuntimeIdentityrequireskrunon PATH whatever the backend, sobox upfails on a firecracker-only host. The isolation-proof cache is also shared across backends, so one backend's proof lets another skip its check. The hosted run above worked around the first with akrun -> crunsymlink.