Skip to content

docs(design): plan in-sandbox platform-tool MCP (Claude/Codex tools on Daytona, unified with Pi)#5234

Draft
mmabrouk wants to merge 4 commits into
big-agentsfrom
in-sandbox-tool-mcp
Draft

docs(design): plan in-sandbox platform-tool MCP (Claude/Codex tools on Daytona, unified with Pi)#5234
mmabrouk wants to merge 4 commits into
big-agentsfrom
in-sandbox-tool-mcp

Conversation

@mmabrouk

Copy link
Copy Markdown
Member

Context

A Claude Code run on Daytona with any custom tool is refused up front (run-plan.ts:355, REMOTE_TOOLS_UNSUPPORTED_MESSAGE): the internal tool MCP server binds the runner's loopback, unreachable from inside a remote sandbox, and Claude has no in-sandbox writer for the file relay the way Pi does. So the harness we want people to use for serious agents gets zero platform tools in the sandbox we want to be the default.

This PR is design only: a planning workspace at docs/design/agent-workflows/projects/in-sandbox-tool-mcp/ (README, context, research, plan, open-questions, status). No runtime code changes.

Settled decisions the workspace encodes (owner, 2026-07-11)

  • The sandbox talks only to the runner. The API-hosted tool gateway was rejected (warm sandboxes are the priority); gateway logic stays in the runner and credentials never enter the sandbox.
  • User-declared MCP: HTTP transport only, permanently. We never host or run user stdio/npx MCP servers; a user runs theirs and gives us the URL. Auth is an API key in a header for now; OAuth is named future work.
  • Platform tools are delivered by our own MCP server running inside the sandbox, feeding the existing file relay.
  • The design goal that matters most: one gateway-tool logic serving both Pi and the MCP harnesses, in closely shared code.

Recommended design

Revive PR #4873 (closed, pre-rename): a small dependency-free stdio MCP server bundle, uploaded into the sandbox and advertised as an internal McpServerStdio entry named agenta-tools. The harness's ACP adapter spawns it inside the sandbox; tools/call writes the same relay request file Pi's extension writes; the runner executes server-side behind the existing permission guard.

This flips the earlier claude-daytona-tools recommendation (A1, HTTP on the sandbox loopback) to A2 (harness-spawned stdio). Warm sandbox reuse (#5225) is the reason: a harness-spawned shim inherits the session lifecycle, so park-to-running, park-to-stopped, and tool-set changes between turns are correct by construction, while a runner-started HTTP shim would need port management, readiness polling, health checks on resume, and stale-process replacement. The remote-tools-delivery daemon-spawned candidate collapses into A2 (the daemon already forwards session MCP entries; no sandbox-agent change needed).

Unification story: extract the relay writer (relay-client.ts) and a transport-neutral MCP handler (mcp-handler.ts) as shared modules consumed by the Pi extension path, the local Claude HTTP server, and the new shim, with a golden test pinning byte-identical relay request files. "Pi consumes the MCP server directly" is written up as a later option with honest trade-offs (Pi has no MCP client by design; the payoff is user-MCP-on-Pi, not this feature).

Sequencing: gateway/callback tools first; client tools (request_connection) stay fail-loud on this path until the client-tool continuation work lands (the relay parks them with no response file, so the shim would hang). Cross-referenced with the concurrent event-driven-tool-relay sibling: shared file contract, and the shim's response wait is one swappable function.

What review is needed

A plan review, not a code review. The interview list is open-questions.md; the two decisions that shape implementation most:

  1. Approve the A1-to-A2 transport flip (question 1).
  2. Confirm client tools stay refused on Claude+Daytona until the continuation work (question 2).

https://claude.ai/code/session_01Fr4A5zjs5rsufRaWgWiUNC

@vercel

vercel Bot commented Jul 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jul 12, 2026 9:32am

Request Review

@mmabrouk

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 417ed42d-4002-4c1e-a0ed-1805bc58448c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds design documentation for delivering platform tools through an in-sandbox MCP server and existing file relay, including transport selection, lifecycle behavior, security constraints, implementation slices, open questions, and status tracking. No runtime code changes are included.

Changes

In-sandbox tool MCP design

Layer / File(s) Summary
Architecture context and existing relay contracts
docs/design/agent-workflows/projects/in-sandbox-tool-mcp/context.md, docs/design/agent-workflows/projects/in-sandbox-tool-mcp/research.md
Documents the current remote-tool limitation, existing relay paths, preserved protocol contracts, sandbox reuse behavior, and prior implementation findings.
Stdio MCP relay design
docs/design/agent-workflows/projects/in-sandbox-tool-mcp/plan.md
Defines the harness-spawned agenta-tools server, runner/sandbox interfaces, shared relay modules, and golden request-byte validation.
Lifecycle, gating, and delivery plan
docs/design/agent-workflows/projects/in-sandbox-tool-mcp/plan.md
Specifies lifecycle states, capability gating, security invariants, packaging, coordination, and phased acceptance criteria.
Design navigation and owner decisions
docs/design/agent-workflows/projects/in-sandbox-tool-mcp/README.md, open-questions.md, status.md
Adds the project glossary and navigation, records eight open decisions, and tracks design status and transport decisions.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • Agenta-AI/agenta#4985: Covers related internal MCP delivery and park/abort/resume behavior for Claude client tools.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the design-only in-sandbox MCP planning work and its unification with Pi.
Description check ✅ Passed The description is clearly about the same in-sandbox MCP design workspace and matches the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch in-sandbox-tool-mcp

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2d7eed07-2a85-4109-a6e8-25fb2352e8ee

📥 Commits

Reviewing files that changed from the base of the PR and between b5c4784 and fdeff6b.

📒 Files selected for processing (6)
  • docs/design/agent-workflows/projects/in-sandbox-tool-mcp/README.md
  • docs/design/agent-workflows/projects/in-sandbox-tool-mcp/context.md
  • docs/design/agent-workflows/projects/in-sandbox-tool-mcp/open-questions.md
  • docs/design/agent-workflows/projects/in-sandbox-tool-mcp/plan.md
  • docs/design/agent-workflows/projects/in-sandbox-tool-mcp/research.md
  • docs/design/agent-workflows/projects/in-sandbox-tool-mcp/status.md

Comment on lines +52 to +54
A2 also removes surface rather than adding it: no listener at all (not even loopback), no
port to choose, no readiness poll racing Claude's `tools/list` (the MCP handshake is
synchronous at spawn), and it works under `network: off` since it is stdio plus file I/O.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not claim restricted-network execution is supported yet.

A2 is network-independent at the transport level, but the existing run-plan.ts gate still refuses Daytona runs with restricted networking and executable tools, as documented in research.md:172-178 and this plan’s security section at Lines 228-229. Remove or defer the network-off acceptance cell unless that separate gate is intentionally changed.

Proposed documentation correction
- Network-off cell: gateway tool executes with `network` restricted +
- `best_effort` (relay is file I/O).
+ Network-off behavior remains out of scope until the existing restricted-network
+ run-plan gate is separately changed and validated.

Also applies to: 282-283

Comment on lines +162 to +165
- **Crash mid-turn.** If the shim dies, in-flight `tools/call`s fail in the harness and the
model sees tool errors; the Claude SDK reports the server as failed. The runner relay loop
is unaffected (it just stops seeing requests). No runner-side supervision is needed
because the runner never owned the process.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Define behavior when the shim crashes after writing a request.

If the shim exits after creating <id>.req.json but before consuming <id>.res.json, the runner can still execute a side-effectful tool while Claude reports an MCP failure. “The runner relay loop is unaffected” does not cover this lost-response case. Add a crash-after-write integration test and document the cancellation/idempotency policy before treating crash handling as complete.

@mmabrouk

Copy link
Copy Markdown
Member Author

Verdict: approve with comments. The A1-to-A2 flip is well argued and honestly costed: the warm-reuse lifecycle facts check out against the code (pauseSandbox at engines/sandbox_agent.ts:838-843, park-fail-through-to-delete at :851, customTools in the keep-alive fingerprint at session-pool.ts:170), and tying the shim to the harness session genuinely removes the health-check/stale-port/respawn failure modes an A1 process would carry. The unification story (one writer, one handler, golden-pinned bytes) is the right seam, and the slices are independently landable. I verified the cited anchors broadly: the gate (run-plan.ts:355, :64, :281), the relay contract (relay.ts:51-52, :90-96, dispatch.ts:78-82, delete-after-read at dispatch.ts:91-101), the permission-rule coupling (claude_settings.py:60, :175, mcp__agenta-tools__<tool>), and the env pair (pi-assets.ts:71-72). Two research claims are stale or wrong, and two cross-plan seams need one sentence each to be safe to orchestrate.

1. Factual: the orphaned-req.json risk is overstated, and the precision matters because you assign the bug to the sibling. research.md says a reused relay dir "would execute" a crashed turn's orphan on the next turn. The code already clears the relay dir on Daytona during workspace preparation (workspace.ts:60-66, an rm -rf whose comment states exactly this rationale), which #5232's research cites and yours misses. The real residual window is warm continuation: a checked-out keep-alive session skips prepareWorkspace, each turn starts a fresh relay loop with a per-turn seen set, so an orphan from a crashed turn inside one live environment can still be re-executed. That is narrower, warm-reuse-specific, and worth restating precisely, because #5232's plan explicitly disclaims it ("stale request files ... behave exactly as they do today; this project does not change that property"). As written, the bug is flagged to a project whose plan refuses it. Either get it into #5232's scope or name it unowned.

2. Factual: "The relay directory is derived from the durable cwd (run-plan.ts:388-391)" states the opposite of the code's intent. The relay dir is deliberately kept OFF the durable cwd: an ephemeral base (/home/sandbox/agenta/relay or $TMPDIR/agenta/relay) keyed by basename(cwd), precisely so relay I/O never rides the geesefs mount. Your conclusion (same path across turns of one conversation) stands; fix the wording so nobody later "simplifies" relay files onto the mount and reintroduces the ENOTCONN failure the comment at run-plan.ts:384-387 warns about.

3. Consistency with #5232: the shared-writer seam is described asymmetrically. Your coordination section says the sibling "swaps the internals" of relay-client.ts's single wait function. #5232 names a different shape: a wake-race helper in relay.ts plus a watcher object raced inside relayToolCall in dispatch.ts. It never mentions relay-client.ts, and its open question 6 leaves the landing order open. The intents are compatible, but the contract is only written down here, and the file it names does not exist until your slice 1 creates it. Recommend: land slice 1 (extraction + golden) before #5232 slice 1, and get one sentence into #5232 naming the file and the wait-function contract (timeout plus AbortSignal) so both plans describe the same seam. That also answers #5232's open question 2: with your slice 2 and its slice 2 both behind flags, QA both writers on the watch path in one matrix pass.

4. Consistency with #5201: slice 1 refactors the file #5201 edits, and neither plan declares the order. mcp-handler.ts is extracted from tool-mcp-http.ts, the same file #5201's WP1 (bearer auth, client-tool batch rejection) and WP3 (register-before-pause hold-open) modify; the optional pause hook you leave in the handler is exactly WP3's insertion point. Declare the ordering (slice 1 first, it is a pure refactor) and add the layering rule: #5201's bearer belongs in the HTTP transport wrapper, never in the shared handler, so the stdio shim stays credential-free and listener-free as your security section promises.

5. Consistency: "fail loud for client tools on Daytona until continuation lands" is consistent with #5201's local-only scope, but the bridge between them is doubly disclaimed. Slice 4 lists "client tools through the shim (after the continuation work)" while deferring the relay pause protocol; #5201 scopes Daytona out entirely. Nobody owns: the relay park/late-response shape, a relay-backed implementation of #5201's delivery port (whose cancel has no stdio analogue and whose onClosed can never fire runner-side), and the race between a held browser wait and the five-minute auto-stop that kills the shim on park-to-stopped. Name it as unowned future work needing its own workspace, so the orchestration across these three PRs does not silently assume someone is building it.

6. Nits. Anchor drift of a couple of lines in relay.ts (startToolRelay is at :324, the seen set at :334); harmless, sweep before implementation as your own status note says. CodeRabbit's two inline findings (the network-off acceptance cell conflicts with the untouched strict-network gate at run-plan.ts:368; crash-after-write can execute a side-effectful tool while Claude sees an MCP failure) are both real; I will not repeat them, but the second deserves an at-least-once note in the lifecycle section since it is shared with Pi today.


This is an automated cross-consistency review requested by the owner, run alongside his own review of #5232 (event-driven tool relay). It checks this plan against #5201 (MCP client-tool continuation) and #5232 for contradictions, shared seams, and sequencing.

mmabrouk added 2 commits July 11, 2026 22:49
…MCP plan

Correct the relay-dir and orphaned-request research claims, pin the
waitForRelayResponse seam contract and landing order with the
event-driven-tool-relay sibling, state the bearer layering rule and the
ordering with mcp-client-tool-continuation, name the unowned Daytona
client-tool bridge, cover crash-after-write, and clarify the network-off
QA cell (best_effort opt-out).

Claude-Session: https://claude.ai/code/session_01Fr4A5zjs5rsufRaWgWiUNC
@mmabrouk mmabrouk force-pushed the in-sandbox-tool-mcp branch from fdeff6b to 7616d69 Compare July 11, 2026 20:59
mmabrouk added a commit that referenced this pull request Jul 11, 2026
…inuation plan

State the transport-specific limits of cancel and onClosed in the delivery
port, open the transport union, add the future in-sandbox stdio mapping
(PR #5234) with its missing prerequisites, pin the WP1 bearer to the HTTP
transport wrapper, declare that PR #5234 slice 1 lands before WP1 and WP3,
and correct the WP5 Daytona wording.

Claude-Session: https://claude.ai/code/session_01Fr4A5zjs5rsufRaWgWiUNC
@mmabrouk

Copy link
Copy Markdown
Member Author

Follow-up: the review findings above are now addressed in this workspace (owner request).

Addressed in this PR's branch:

  • research.md: the relay-dir claim is corrected (an ephemeral sibling keyed by basename(cwd), deliberately off the durable cwd; the stability-across-turns conclusion stands). The orphaned-request risk is restated precisely: workspace.ts:60-66 already clears the relay dir on cold builds, so the residual window is warm-continued turns that skip prepareWorkspace. The startToolRelay anchor drift is fixed.
  • plan.md U1: the response-wait seam now has an explicit contract (waitForRelayResponse(resPath, { timeoutMs, signal }) in tools/relay-client.ts) and the coordination section declares the landing order: this slice 1 lands before the event-driven-tool-relay slice 1, so the sibling's watch goes into that function.
  • plan.md U1: the layering rule is stated: mcp-handler.ts stays credential-free; docs(design): plan exact MCP client-tool continuation #5201's WP1 bearer lives in the HTTP transport wrapper; docs(design): plan exact MCP client-tool continuation #5201's WP1/WP3 build on the extracted handler after this slice 1.
  • plan.md client tools: the Daytona client-tool bridge is named as owned by neither this project nor docs(design): plan exact MCP client-tool continuation #5201, with its three missing pieces (relay park protocol, stdio abort-without-result analogue, auto-stop race).
  • plan.md lifecycle: crash-after-write is now covered (the relay is at-least-once from the executor's view, shared with Pi today; the orphaned response file is inert; slice 2 tests must cover it). This addresses CodeRabbit's second inline finding.
  • plan.md slice 3: the network-off QA cell now states why best_effort is required (the strict-network refusal at run-plan.ts:368 stays, and only an explicit best_effort opts out). CodeRabbit's first inline finding misread the cell: it already specified best_effort, which is exactly the opt-out the gate honors, so the cell stays with the clarification.
  • status.md: review round logged.

Pending, deliberately not in this PR:

…relay modules, shrink v1

Fold the Codex xhigh review into the in-sandbox tool MCP plan. Gate A2 on a new
slice 0 restart spike (session/load after a VM stop is adapter behavior, not
correct by construction; orphan exit weakened to an expectation). Hand the
relay-client/relay-protocol extraction to PR #5232 slice 0 and consume it here,
reversing the earlier ordering. Cut the standalone transport-neutral mcp-handler
slice and rewrite the unification section honestly (Pi never speaks MCP; the real
sharing is the relay client and file protocol). Decide specs delivery now: a file,
not an unbounded env variable. Move client tools, Codex-on-Daytona, snapshot bake,
Pi-as-MCP-client, watch adoption, and mandatory replay capture to explicit
follow-ups. Add the missing warm-reuse edges (session/load, sanitized-ID collision,
bundle skew, partial visibility via #5232's atomic rename). Make the internal/user
stdio separation structural and fix naming (tool-mcp-stdio.ts,
internal-tool-mcp-handler.ts, helpers under engines/sandbox_agent/, ACP entry
shapes out of mcp-bridge.ts).

Claude-Session: https://claude.ai/code/session_0127AM79khCdvD2b8BG2joZL
mmabrouk added a commit that referenced this pull request Jul 11, 2026
…surement gates

Fold the Codex xhigh review into the client-tool continuation plan. Rescope v1 to
WP0 (expanded with cold-path baseline metrics: first-reissue match rate, argument
drift, added model calls/latency/cost, wrong-replica rate, wait percentiles) plus
WP1 loopback hardening (auth from headers before body parsing, timing-safe
comparison, per-environment token and rotation test, result-size cap separated
from auth). Defer WP2 through WP5 behind two concrete unlock gates: Claude holds
the request past the 60-second idle TTL, and cold replay demonstrably harms users.
Slim the delivery port to deliver -> accepted|unavailable plus dispose; cut cancel
and onClosed (they modeled an HTTP response handle); correctness rests on lease
expiry and teardown. Replace the standalone registry with pool-owned placement
(ParkedClientTool beside ParkedApproval, awaiting_client_tool in session-pool.ts,
McpHttpResultDelivery under tools/). Rename harnessToolCallId to toolCallId with
its provenance as a registration invariant. Restrict any future warm mode to
owner-routed deployments and define the delivery commit point. Drop the dependency
on #5234's handler extraction (cut from that project's v1). The deferral is the
top review question for the owner; it reverses part of a plan he LGTM'd and is
flagged as such in status.md and open-questions.md.

Claude-Session: https://claude.ai/code/session_0127AM79khCdvD2b8BG2joZL
@mmabrouk

Copy link
Copy Markdown
Member Author

Folded the Codex xhigh review into this workspace (commit d2832ea). The owner was asleep; per his standing simplify-aggressively instruction I adopted the cuts rather than defending scope. Everything is reversible at his review. Per finding:

  • P0 restart spike: the plan no longer claims park-to-stopped restart is "correct by construction". A new slice 0 gates A2: create a session with the internal stdio MCP, stop and restart the VM, exercise the real resumeSession/session/load path, verify tools/list succeeds and a new shim PID exists, and record the failure behavior. Documented fallback: force cold createSession for sessions containing the shim; A1 as last resort. The orphan-exit claim is weakened to an expectation (the runner itself warns about PID-1 reparenting).
  • P0 ownership: the relay-client/relay-protocol extraction is out of this project entirely. PR docs(design): plan event-driven tool relay (remove relay polling latency) #5232 owns it as its slice 0 and is now an explicit prerequisite; this project consumes the modules and adds only shim-specific tests. This reverses the ordering yesterday's consistency pass wrote, and docs(design): plan event-driven tool relay (remove relay polling latency) #5232's workspace already reflects the new ownership.
  • P1 unification honesty: the standalone transport-neutral mcp-handler.ts slice is cut. Pi never speaks MCP, so the real unification is the shared relay client and file protocol; any MCP dispatcher is local-HTTP-vs-stdio only. The plan now says: extract a minimal internal-tool-mcp-handler.ts while building the shim only if it clearly reduces duplication, else temporarily duplicate the small protocol switch with parity tests. No pre-designed pause hook.
  • v1 cuts: client tools, Codex-on-Daytona, snapshot bake, Pi-as-MCP-client (U2), watch adoption, and mandatory replay capture all moved to explicit follow-ups. v1 = spike, shim consuming the relay modules, one hardcoded internal agenta-tools entry, narrowed gate, live acceptance (cold start, live reuse, stopped-VM restart) on the pinned adapter.
  • Warm-reuse edges added: session/load after VM stop, sanitized-ID collision, bundle-version skew in a reused sandbox, and partial request visibility (inherited from docs(design): plan event-driven tool relay (remove relay polling latency) #5232's atomic-rename protocol amendment).
  • Specs size, decided now: the shim reads public specs from an uploaded file, not from the unbounded env variable copied through four exec layers. "Fallback later" is gone.
  • Security made structural: dedicated constructor and type for the internal entry, no user process fields can flow in, the reserved agenta-tools name is rejected for user declarations at validation time, and toAcpMcpServers stays incapable of stdio.
  • Naming: tool-mcp-stdio.ts entrypoint, internal-tool-mcp-handler.ts if a handler exists, upload helpers under engines/sandbox_agent/ like pi-assets.ts, ACP entry shapes out of tools/mcp-bridge.ts, agenta-tools unchanged.

Also dropped the cross-writer byte golden in favor of semantic contract tests (the reader is JSON.parse; the record shape is the contract, pinned by #5232's slice 0 tests). The orchestration file in mcp-delivery-architecture/ was reconciled to the new landing order and stays uncommitted for the owner's review.

https://claude.ai/code/session_0127AM79khCdvD2b8BG2joZL

…matrix

Slice 0 restart spike: session/load respawns the stdio MCP shim on the pinned adapter
(claude-agent-acp 0.22.2, snapshot agenta-sandbox-pi); A2 locked, no engine fork needed.
Slice 1 implemented on lane feat-in-sandbox-tool-mcp. Slice 2 matrix: all runnable cells
green (Claude+local execution, Pi+Daytona execution, Claude+Daytona mechanism, client-tool
and reserved-name refusals, no-tools negative); Claude+Daytona execution + warm/restart
cells credit-blocked and recorded as the merge gate in status.md.

Claude-Session: https://claude.ai/code/session_01Fr4A5zjs5rsufRaWgWiUNC
mmabrouk added a commit that referenced this pull request Jul 12, 2026
Harden the internal gateway-tool MCP endpoint that delivers Agenta tools to
local Claude over loopback HTTP. Until now it relied only on 127.0.0.1
reachability, so any other local process could list or call tools through it.

- Mint a per-server bearer token (randomBytes 32, base64url) in
  startInternalToolMcpServer; advertise it as the entry's Authorization header
  in buildToolMcpServers.
- Authenticate before reading or parsing the request body; reject missing/wrong
  tokens with 401 and no dispatch. Timing-safe comparison, length-guarded.
- Reject a JSON-RPC batch containing a client tool before executing any item.
- The bearer lives ONLY in the HTTP transport path; the in-sandbox stdio shim
  (#5234) stays credential-free by construction (different file, different
  transport). It is a local loopback guard, never a provider/control-plane
  credential — the private callRef still never reaches the advertisement.

Adds real-socket HTTP integration tests (auth, malformed/oversized body, batch
client-tool rejection, close) and updates the two layering pins that asserted
headers: [] to expect the loopback guard header.

WP0: checked-in measurement protocol + unlock-gate scorecard for the deferred
warm hold-open path (results marked PENDING LIVE RUN). No runtime behavior
change from WP0.

Claude-Session: https://claude.ai/code/session_0127AM79khCdvD2b8BG2joZL
pull Bot pushed a commit to jasonkneen/agenta that referenced this pull request Jul 14, 2026
…lay writer

Slice 0 of the event-driven tool relay: move the in-sandbox writer (relayToolCall)
out of tools/dispatch.ts into tools/relay-client.ts, and the wire protocol (suffixes,
request/response types, sanitizeRelayId, request serialization, shared timing env
constants) into tools/relay-protocol.ts. Both new modules are bundle-safe (node
builtins only) so the Pi extension bundle and the future in-sandbox MCP shim (Agenta-AI#5234)
can consume them without pulling in server-side runner code. dispatch.ts and relay.ts
re-export every moved symbol, so all call sites compile unchanged.

Zero behavior change; a golden test pins the byte-exact .req.json serialization as
the cross-writer contract. Side effect: the extension bundle drops the server-side
relay loop it previously pulled in through dispatch's constant imports.

Claude-Session: https://claude.ai/code/session_01Fr4A5zjs5rsufRaWgWiUNC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant