Skip to content

docs(design): plan exact MCP client-tool continuation#5201

Draft
mmabrouk wants to merge 3 commits into
big-agentsfrom
docs/mcp-client-tool-continuation-plan
Draft

docs(design): plan exact MCP client-tool continuation#5201
mmabrouk wants to merge 3 commits into
big-agentsfrom
docs/mcp-client-tool-continuation-plan

Conversation

@mmabrouk

Copy link
Copy Markdown
Member

Context

When Claude calls an Agenta client tool such as request_connection, the runner currently destroys the internal MCP response to pause the turn. The browser result arrives on a new /run, Claude reissues the tool with a new id, and the runner matches the result by tool name and arguments. PR #5197 improves native session recovery after that teardown, but it does not preserve the original MCP call.

This design adds a progressive local exact path. The runner holds the original MCP response and prompt, returns the browser result to that same JSON-RPC request, and keeps the current cold path for every unsupported or failed case.

Changes

This PR adds a design-only workspace for the change:

  • current behavior and the boundary with PR [feat] Resume agent sessions across the sandbox lifecycle #5197;
  • a gateway-neutral pending-operation, delivery-port, and state-machine contract;
  • six work packages covering timeout measurement, loopback authentication, the neutral kernel, local hold-open wiring, failure and resource hardening, and rollout;
  • unit, HTTP integration, live Claude, security, race, shutdown, and resource verification; and
  • explicit decisions for timeout threshold, authentication, replica routing, concurrency, and delivery acknowledgement.

Before, a browser result can only reach a new model-issued client-tool call through cold matching. After the proposed local path, the same live MCP request receives the result and the original prompt continues without another model decision.

Scope / risk

This PR changes documentation only. It does not implement the hold-open path or change runner behavior.

A real MCP gateway, Daytona exact delivery, cross-replica forwarding, public wire changes, MCP client-tool batches, and multiple pending client tools per session remain out of scope. The design keeps those boundaries explicit and defines an interface that a future gateway can implement.

The main implementation risks are Claude's unmeasured MCP client timeout, unauthenticated loopback access, duplicate result races, held-socket cleanup, and output loss during fallback. Each has an earlier work package or a default-off exit gate before rollout.

Tests / notes

  • Reviewed against big-agents at cb63991eaa7d757c98d7c02a54382403fbe348ff.
  • Refreshed PR [feat] Resume agent sessions across the sandbox lifecycle #5197 at 343d7146935a8eb3ed41a203cd9a3db6ee954eef while drafting.
  • Scanned all eight Markdown files for forbidden punctuation, trailing whitespace, and documentation wording violations.
  • No code tests were run because this PR contains design files only.

How to review

  1. Read context.md for the warm, cold, and PR [feat] Resume agent sessions across the sandbox lifecycle #5197 boundaries.
  2. Check research.md for the current code findings and the old-versus-new risk split.
  3. Review interface.md for identity, scope, routing, protocol, lifecycle, and future-gateway separation.
  4. Review plan.md for work-package order and rollback gates.
  5. Use qa.md and open-questions.md to challenge the enablement bar and the remaining decisions.

@vercel

vercel Bot commented Jul 10, 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 11, 2026 9:20pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

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: f5685b37-13f8-4cf8-ac7e-529e7a8b5b33

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/mcp-client-tool-continuation-plan

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.

@mmabrouk mmabrouk added the needs-review Agent updated; awaiting Mahmoud's review label Jul 10, 2026
@mmabrouk

Copy link
Copy Markdown
Member Author

🤖 The AI agent says:

This is a design-only PR. I need review on three boundaries before any implementation starts:

  1. Is the six-package order right: measure timeout, harden loopback auth, add the neutral kernel, wire local hold-open, close races and resource exits, then canary?
  2. Is local Claude only the right first scope, with Daytona and wrong-replica requests staying on PR [feat] Resume agent sessions across the sandbox lifecycle #5197 plus cold replay until a real gateway or routing layer exists?
  3. Should loopback authentication remain a prerequisite even though that exposure predates hold-open?

🔸 Decision needed: minimum client timeout

  • Option A: require the held request to exceed the full five-minute approval TTL.
  • Option B: require it to exceed 60 seconds, cap the live wait below the measured ceiling, and fall back cold for longer waits.
  • Option C: ship a shorter hold even if it does not cover 60 seconds.

I recommend Option B. If WP0 cannot hold the real Claude MCP request beyond 60 seconds, the plan stops and keeps the current cold path.

@mmabrouk

Copy link
Copy Markdown
Member Author

Verdict: approve with comments. The plan is sound and unusually careful. I spot-checked the load-bearing factual claims against the working tree and every one held: MCP_PAUSED and the destroy-and-cold path (tool-mcp-http.ts:62, :190), the empty headers: [] advertisement (mcp-bridge.ts:126), the ToolCallCorrelationIndex (client-tools.ts:39), the FIFO cold store keyed by approvedCallKey(name, args) (responder.ts:66, :217), the pool state set busy | idle | awaiting_approval | destroyed (session-pool.ts:439), the batch-abort behavior (tool-mcp-http.ts:316-324), and the Daytona skip (mcp.ts:245-264). The WP0 measurement gate, the auth prerequisite, the neutral kernel before any held request, and the strict cold-fallback invariants are the right order. The blocking asks are not internal to this plan; they are coordination with PR #5234, which was decided a day after this workspace was written and changes the ground under two of its files.

1. Consistency with #5234: you both edit the same handler, and neither plan declares the order. #5234 slice 1 extracts tools/mcp-handler.ts (transport-neutral MCP handler with an optional client-tool pause hook) and tools/relay-client.ts out of tool-mcp-http.ts and dispatch.ts. Your WP1 (bearer auth, client-tool batch rejection) and WP3 (register-before-pause, preserve-server-on-park) modify exactly the code that extraction moves, and #5234's pause hook is exactly WP3's insertion point. Recommend: state in plan.md that WP1/WP3 build on the extracted handler and sequence after #5234 slice 1 (a pure refactor, cheap to land first). One layering rule matters: the WP1 bearer must live in the HTTP transport wrapper, not in the shared handler, or #5234's stdio shim inherits an auth requirement that has no meaning for a harness-spawned child process. Batch rejection can live in the handler; auth cannot.

2. Design gap: interface.md's neutrality claim does not fully survive the A2 stdio transport that #5234 makes the committed Daytona shape. The delivery port is structurally neutral, but two semantics are HTTP-specific. First, cancel("abort without a result") is res.destroy() today; a stdio shim has no per-request abort. Its options are settling the call with a JSON-RPC error (teaches the model the tool broke), never answering (the shim's relay wait times out at 60 s and returns an error anyway, relay.ts:249-252 writes no response file for a paused client tool), or dying (kills every in-flight call). Second, onClosed can never fire for a relay-backed adapter: the runner cannot observe the shim's transport, so the state-machine row "socket closes -> cancelled" silently degrades to TTL-only. Neither breaks the local release, but the doc claims a future adapter can implement the same port; say explicitly that onClosed is best-effort per transport, that the transport enum (currently the single literal "mcp_streamable_http") is open and will grow a relay/stdio value, and that the relay response protocol must first gain a park shape before any such adapter exists.

3. Consistency: nobody owns the Daytona client-tool bridge. This plan scopes Daytona out (correctly). #5234 delivers gateway tools only and says the shim inherits pause semantics "when the continuation work lands," with the relay-side protocol "deliberately not designed here." Net effect after both land: Claude + Daytona + client tool still refuses up front, and no workspace owns the bridge (relay park protocol, a relay implementation of your delivery port, and the race between a held browser wait and the five-minute Daytona auto-stop, which kills the shim and the held call on park-to-stopped). The two fail-loud/local-only positions are mutually consistent; the gap is that the follow-up work is doubly disclaimed. One sentence in plan.md naming it as explicitly unowned future work, needing its own workspace, would prevent the orchestration hole.

4. Nit: context.md will go stale the week #5234 merges. "Non-Pi Daytona runs with Agenta tools are rejected before session creation" becomes true only for client tools and non-Daytona remote providers after #5234 slice 2 narrows the gate. Add a forward reference. Related wording: WP5's "keep Daytona on the cold path" is imprecise for client tools, where Daytona has no cold path, only the up-front refusal; qa.md already words this correctly ("fails or falls back as it does before this project").

5. Answers to the questions in the PR description, within the settled decisions: the six-package order is right once the #5234 slice-1 dependency above is added; local Claude first is the right scope and matches #5234's fail-loud posture on Daytona; loopback auth as a prerequisite is correct (hold-open extends the endpoint's lifetime, and the bearer fits the existing AGENTA_RUNNER_* config family). On the timeout decision, Option B is the right gate: it claims only what WP0 can measure and keeps cold fallback for longer waits.


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 #5234 (in-sandbox tool MCP) and #5232 for contradictions, shared seams, and sequencing.

ceiling, and use cold fallback for longer waits.
- Option C: ship any measurable hold, even below 60 seconds.

Recommendation: Option B. It provides a useful fast path without claiming that the runner controls

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think it should be the same as the warm path no? it a different env var but per default it should be the same number

- Option A: allow multiple operations per session and support client-tool JSON-RPC batches.
- Option B: support one pending operation and reject client-tool batches before execution.

Recommendation: Option B. The current pause latch and session resume path are single-gate. Multiple

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

same as now we should hvae. which is we block things when we have more nicely for ht euser

…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 mmabrouk force-pushed the docs/mcp-client-tool-continuation-plan branch from 411537d to 85ae348 Compare July 11, 2026 21:00
@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:

Pending, deliberately not in this PR:

…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 3fdac7c). The headline decision, awaiting the owner: the warm hold-open path is deferred. He LGTM'd the fuller plan before this review; the deferral is a deliberate conservative rescope adopted while he slept, under his standing simplify-aggressively instruction, and it is reversible from git history. It is the top item in open-questions.md. Per finding:

  • Rescope: v1 is now WP0 plus WP1. WP0 expanded beyond the timeout measurement with cold-path baseline metrics: first-reissue match rate, argument-drift rate causing repeat browser interactions, added model calls/latency/token cost, wrong-replica resumption rate, and user wait percentiles. WP2 through WP5 are deferred behind two concrete unlock gates: Claude demonstrably holds the MCP request past the 60-second idle TTL, and the cold path demonstrably harms users (proposed thresholds in plan.md for the owner to confirm). If the request does not survive 60 seconds the warm path is cut, not deferred.
  • Delivery port slimmed: deliver(output) -> accepted | unavailable(reason) plus dispose(reason) meaning only "release adapter-owned resources". cancel and onClosed are cut from the neutral contract; they modeled an HTTP response handle. Transport liveness is an optional adapter-owned closed signal, and registry correctness rests on lease expiry and environment teardown, never on that signal.
  • Registry placement: ClientToolContinuationRegistry is marked premature. If the warm path unlocks: McpHttpResultDelivery under tools/ as transport code, ParkedClientTool beside ParkedApproval in the session model, awaiting_client_tool plus its checkout in session-pool.ts (checkout behaves as a lease with idempotent finalization), exact result extraction beside the responder extractors. A durable registry only at a future gateway boundary. harnessToolCallId renamed to toolCallId with its provenance stated as a registration invariant: warm registration requires a proven harness-correlated id; the best-effort fallback id is cold-only.
  • Wrong-replica gap: any future warm mode is restricted to owner-routed deployments (single replica, verified affinity, or an owner-routing token), because a wrong-replica cold start can race a live owner. The delivery commit point is defined: before accepted, preserve the inbound result for cold; after accepted, never start a cold continuation because the original prompt later fails.
  • Dependency drop: WP1 no longer waits on docs(design): plan in-sandbox platform-tool MCP (Claude/Codex tools on Daytona, unified with Pi) #5234's handler extraction (cut from that project's v1). The batch rejection lands in tool-mcp-http.ts and moves only if a shared dispatcher is extracted later. WP1 also gained the review's hardening details: auth from headers before body parsing, timing-safe comparison, per-environment token with a rotation test, result-size cap treated separately from auth.

status.md and open-questions.md flag the deferral provenance explicitly. The orchestration file in mcp-delivery-architecture/ was reconciled and stays uncommitted for the owner's review.

https://claude.ai/code/session_0127AM79khCdvD2b8BG2joZL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review Agent updated; awaiting Mahmoud's review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant