Execution Finality for Agent Tool Calls
A model-generated tool call is a candidate action, not proof that the action is authorized to become effective. This Internet-Draft proposes a host-side gate that binds exact arguments and destination to scoped, single-use authority before dispatch.
Page reviewed 4 Oct 2026 Source and review policy
Status first
Section titled “Status first”draft-das-agentic-tool-binding-03 is an active individual IETF Internet-Draft, not an RFC, IETF standard or IETF-endorsed specification. Revision -03 was published 5 September 2026 and expires 9 March 2027 unless updated or replaced. Its document header says Intended status: Informational; the IETF Datatracker currently lists no RFC stream and no intended RFC status for the submission.
The draft addresses a narrow execution boundary inside agent hosts: a model can emit a syntactically valid tool_use, function/tool-call, MCP tools/call or computer-use action, but generation of that request does not itself establish that the requested real-world effect is authorized.
Its proposed invariant is therefore structural rather than model-behavioral: hold the generated action non-effective until a local enforcement path has bound the exact action, validated authority, and consumed that authority at the dispatch boundary immediately before effectuation.
Handle a timeout after authorized dispatch
Section titled “Handle a timeout after authorized dispatch”A single-use local permit does not answer whether a remote operation completed when its response was lost. Consider an approved file publication: the host validates exact content/destination, consumes its local authority, sends the request, then times out. Issuing a new permit and repeating the request can duplicate the effect.
candidate → bind + authorize → consume local permit → remote request ├─ receipt: settle └─ timeout: reconcileKeep candidate ID, canonical argument digest, destination, authority ID, dispatch attempt and any destination idempotency/object-version token in one receipt. On timeout, query the same destination for the exact operation/object identity where its API permits. Do not infer non-execution from absence of a local success response.
| Evidence | State to expose |
|---|---|
| No authority consumed and no dispatch | Non-effective/refused |
| Authority consumed; remote outcome unknown | Dispatched, indeterminate |
| Destination confirms exact effect | Effect reconciled |
| Destination confirms failure without effect | Failed; a new attempt needs its declared retry policy |
The individual draft remains revision -03, rechecked 4 October, with no RFC stream or IETF standard status. This worked flow is an engineering application of its local atomicity limitation, not an implemented UHP extension. Control-delivery evidence supplies the vocabulary for preserving missing or conflicting observations.
The execution-finality boundary
Section titled “The execution-finality boundary”A conventional host loop often collapses two different events:
- the model generates a tool request; and
- the host invokes the named tool with those arguments.
The draft inserts a gate between them. Its host-loop sequence is broadly:
- parse each generated tool block;
- canonicalize the live arguments and compute an argument digest;
- map the request into an AgentCandidateAct;
- keep the act non-effective;
- validate policy/authority;
- issue act-bound authority only after successful validation;
- at the Finality Sink, re-check the live digest, destination/sink and freshness state, then consume the authority;
- invoke only after those checks succeed.
That sequence is intended to prevent a common category error: treating schema-valid model output, previous trust in a conversation, or possession of a broad credential as permission for this exact consequential act.
What is bound
Section titled “What is bound”The draft’s security proposition depends on binding authority to the action that will actually execute, not merely to a tool name.
| Bound coordinate | Why it matters |
|---|---|
| Candidate-act identity | Keeps one generated act distinct from another or from a replay |
| Tool/function identity | Prevents authority for one operation being silently reused for another |
| Canonical argument digest | Makes post-approval argument substitution detectable |
| Destination / tool endpoint | Makes an otherwise identical call to a substituted backend distinguishable |
| Workload / agent context | Narrows who may present the authority |
| Policy/freshness state | Allows later policy change or expiry to invalidate stale authority |
| Finality Sink | Prevents an authority issued for one enforcement boundary being accepted at another |
For computer-use actions the same principle applies to live state. The draft treats consequential clicks/submissions as separate candidate acts and says authority should bind the state that will actually be submitted, rather than trusting the model’s textual description of that state.
Fail-closed is scoped to the proposal
Section titled “Fail-closed is scoped to the proposal”The draft defines consequence classes including FINANCIAL, PHYSICAL and NETWORK_CONTROL, and requires fail-closed behavior above its configured threshold. Under that proposal, missing or invalid current act-bound authority means the candidate act remains non-effective.
That is a requirement of this draft’s proposed binding, not a claim about how UHP, MCP, Anthropic, OpenAI, or other agent runtimes currently behave.
The draft also couples each allow/deny enforcement decision to a Ledger-Anchored Validation Receipt (LAVR). Within the proposal, that receipt is evidence that the finality boundary evaluated the specific candidate act; it should not be generalized into proof that a later external system completed the requested effect or that the decision was legally correct.
MCP: useful binding idea, but correct the session wording
Section titled “MCP: useful binding idea, but correct the session wording”The draft contains an explicit mapping for MCP tools/call and places the conforming hook at the MCP client dispatcher, so one client-side enforcer can see the MCP servers reachable by the agent. It binds the intended server endpoint as part of the destination and says a denied call must not be sent to a normal server.
There is an important version boundary, however. One section of the draft characterizes MCP authorization in terms of what a server accepts “under the session.” Released MCP 2026-07-28 removed protocol-level sessions and the Mcp-Session-Id header, removed the initialization handshake, and moved protocol version/capabilities to every request. Current MCP therefore has a stateless protocol core, with application state carried through explicit handles when needed.
The durable concept in this draft is not the older session wording. It is the proposed per-act dispatch gate around an exact tools/call request and destination. That gate can be evaluated independently of whether the surrounding MCP revision is sessionful or stateless.
This site does not treat the draft as correcting or extending MCP core.
Alternate paths are the hard production problem
Section titled “Alternate paths are the hard production problem”The draft is unusually explicit about bypasses. A protected Finality Sink does not establish execution-finality if the same effect remains reachable through another route such as raw credentials, direct HTTP, a second SDK, an unwrapped plugin, subprocess execution, another MCP client or browser automation.
Production assurance would therefore require the consequential paths to converge on, or remain subordinate to, the same effectuation boundary. A middleware wrapper that guards only one convenient path is evidence for that guarded path, not proof that the host cannot bypass it elsewhere.
The draft also acknowledges a local atomicity limit: consuming a local single-use authority and causing a remote effect are not automatically one atomic transaction. Idempotency keys, destination-side verification, transactional outbox patterns, protected acknowledgements, effect receipts or application-specific recovery may still be necessary.
Tool-name translation is part of the enforcement boundary
Section titled “Tool-name translation is part of the enforcement boundary”A fresh Qwen Code compatibility fix gives a concrete implementation example of a quieter bypass class: the guard exists, but the policy namespace does not identify the runtime tool that actually executes. Issue #11823 showed that a Claude Code-style PreToolUse matcher such as "Bash" could silently fail to match Qwen’s run_shell_command, even though Qwen’s permission-rule parser already understood Bash. Likewise, "Write|Edit" could match edit while missing write_file.
Merged PR #11826 fixes the mismatch by making tool-event hook matchers accept the same per-tool aliases that Qwen permission rules accept. The mapping remains intentionally exact rather than expanding permission meta-categories: Read maps to read_file, not every read-like tool; Edit does not map to write_file; Bash does not map to monitor. Session hooks and Skill hooks use the same matcher path.
The upstream review reports 591 focused tests passing, zero TypeScript build errors, clean lint/formatting, and mutation tests that fail if the Claude-compatible alias set is removed or broadened incorrectly. Direct validation was Linux unit tests; the PR explicitly did not run the optional built-CLI end-to-end check and did not directly validate macOS or Windows.
This does not make Qwen’s hook system an implementation of the Internet-Draft, nor does it create a portable Claude↔Qwen hook standard. It does establish a durable engineering lesson for execution-finality designs: canonicalization must cover the policy/matcher identity presented by configuration as well as the runtime identity used at dispatch. A fail-closed gate can still fail open in practice if those namespaces diverge and an unmatched rule is treated as “not applicable.”
For portable harness configuration, tool aliases therefore need explicit semantics and adversarial tests. At minimum, test that every security-relevant matcher resolves to exactly the intended runtime tool, that an unknown alias does not broaden to a category accidentally, and that copied cross-harness configurations do not become inert without a visible error or compatibility mapping.
Parallel tool-call identity must survive streaming translation
Section titled “Parallel tool-call identity must survive streaming translation”Hermes PR #111686, merged 15 September 2026 as a55c972e09177e4db3934915e329993858b247d6, fixes a P1 Gemini-native streaming bug at an earlier boundary than authorization: two distinct calls to the same tool could be accumulated as if they were one call. With Gemini 2.5, functionCall.id can be absent and part_index restarts at zero for each event; the second call could therefore reuse the first call’s accumulator slot, concatenate two complete argument objects into invalid JSON and cause a call to be dropped downstream.
The corrected adapter uses the provider’s call id as slot identity when one exists. Without an id, a full argument payload that is neither a prefix-extension nor a resend of an existing slot opens a separate key#N slot, which remains addressable so a later resend can return to the same call. Upstream’s focused regression adds two invariants and reports 33 passing tests in tests/agent/test_gemini_native_adapter.py after the change.
The remaining ambiguity is explicit: two separate no-id calls with byte-identical tool name and arguments are indistinguishable from a resend and can still collapse; a no-id payload that grows without preserving the previous serialized payload as a prefix is treated as a separate slot. Those limits matter because the adapter is reconstructing identity that the provider stream does not always supply directly.
For execution-finality designs, the architectural lesson is upstream of the Finality Sink: candidate-act identity and exact arguments must survive provider-to-harness streaming translation before an authorization layer can bind them reliably. A digest or single-use authority applied after two candidate calls have been merged would be protecting a corrupted representation rather than the original actions. Stream normalization is therefore a prerequisite to act-bound finality, not a substitute for it.
This is bounded implementation evidence. PR #111686 is Hermes Gemini-provider adapter behavior; it does not implement or endorse this Internet-Draft, change MCP or UHP wire semantics, establish native Hermes UHP adoption, or certify every Gemini streaming shape in production.
Persisted tool-block order must survive stream replay
Section titled “Persisted tool-block order must survive stream replay”Hermes PR #115290, merged 18 September 2026 at 20:05:24 UTC as e5af917aaad130d2f3c01529e026ca2bfb70fb56, fixes a Bedrock Converse failure one step later in the same integrity chain: streamed block identity could be reconstructed incorrectly, persisted, and then make every later request in that session fail on replay with an assistant-prefill rejection.
The failure came from assuming that block identity appears only on contentBlockStart. Bedrock can emit text blocks without a start event while still carrying contentBlockIndex on delta/stop events. Hermes therefore assigned text deltas synthetic running slots; a later toolUse start at its real index could overwrite a text fragment and sort the tool into the middle, while index-less text after a tool stop could continue the already-closed tool slot. The resulting persisted sidecar could become a malformed mixed ordering such as [text, toolUse, text…], which the reviewed Claude-on-Bedrock replay path rejects.
The merged adapter now resolves contentBlockIndex on every contentBlockStart, contentBlockDelta and contentBlockStop, keys stream_blocks by that provider identity, and clears the current fallback slot at each stop. Events from proxies or test doubles that omit an index retain a bounded arrival-order fallback: a start opens a new slot, while a delta/stop continues the current slot. The final refactor makes the index helper a pure resolver and leaves current-slot ownership with the start/delta branches.
Upstream’s focused evidence records 79 passing + 2 failing new regressions before the fix and 81 passing after it, plus a captured live Opus 5 event sequence. The reviewed before/after probe changes a corrupted text/tool/text shape into the intended ordered blocks. A separate proposed repair layer in PR #112303 that reconstructs a replayed turn when its persisted sidecar disagrees with content + tool_calls is explicitly not part of #115290.
For harness integrity, the reusable rule is broader than Bedrock: provider stream normalization is persistence logic whenever the normalized structure is later replayed. Block identity and ordering must survive the entire stream → transcript → replay path. An authorization/finality layer cannot safely bind a candidate act if the durable transcript can later reorder or merge the tool block that represented it.
This is post-v0.21.3 Hermes Bedrock-adapter behavior. It does not implement or endorse the Internet-Draft, change UHP, MCP, ACP or A2A wire semantics, establish native Hermes UHP adoption, or prove that HarnessRouter’s released Hermes backend independently revalidates this provider path.
Durable publication needs its own finality gate
Section titled “Durable publication needs its own finality gate”A post-v0.21.3 Hermes fix gives a second implementation example of finality semantics at a different boundary: when downloaded runtime bytes become a durable cache hit. PR #112443, merged 17 September 2026 as 6005aa1fd9aac8b1024ace50fec8cd1c85a04bae, fixes a local-runtime downloader path where an HTTP response could advertise Content-Length, close early, and still leave the partial transfer eligible for promotion from staging into the final archive cache name.
The corrected downloader now keeps a transfer tentative until its publication condition has been checked:
- each transfer stages into its own same-directory
NamedTemporaryFile, so concurrent downloads for the same destination do not share one predictable.partpathname; - received bytes are counted while streaming;
- when
Content-Lengthis present, a byte-count mismatch raisesBinaryResolutionErrorbefore the staged file can replace the durable cache entry; - the final
replace()happens only after that completeness check; - a
finallycleanup removes the transfer’s own staging file on transfer, progress-callback, validation or publication failure; - responses without
Content-Lengthretain the previous behavior rather than pretending that transport completeness was cryptographically established.
That last boundary matters. Equality with an advertised Content-Length proves only that the received byte count matches the server’s declared length. It does not authenticate the publisher, prove the archive’s expected digest, detect same-length malicious substitution, or replace a trusted checksum/signature scheme. A higher-assurance runtime installer still needs an authenticity/integrity mechanism appropriate to its threat model.
Upstream reports 59 focused tests passing on Windows with Python 3.13.7. The new premature-EOF and cleanup regressions fail against the pre-fix baseline 416a8177; Ruff, git diff --check, Windows compatibility and secret scanning also passed. The PR explicitly did not run the full repository suite, native Linux/macOS coverage, or a complete Desktop/GPU installation, so those are not implied by the focused result.
The reusable harness lesson is a finality invariant: staging state is not published state. A downloaded executable/archive should remain non-authoritative until the checks required for publication have passed, and publication should be atomic with respect to readers as far as the local filesystem allows. Once a cache path is treated as authoritative, later consumers may skip the network path entirely; incorrectly publishing incomplete bytes therefore turns a transient transport failure into durable local state.
Reference implementation evidence
Section titled “Reference implementation evidence”The author publishes a v0.1.0 runnable reference implementation covering representative Anthropic-style tool_use, OpenAI-style function/tool calls, MCP tools/call and computer-use actions.
The draft documents a supplied 10-test suite covering cases such as argument mutation, authority replay, MCP endpoint substitution, wrong-sink presentation, parallel candidate acts, injected-content provenance, deterministic JSON digesting, computer-use origin substitution and policy-epoch invalidation. Its recorded archive verification result is 10 passed.
Those are author-supplied synthetic reference tests, not independent production validation. The draft explicitly says the implementation is not a production security product, does not prove universal bypass closure, uses synthetic data, and does not provide production PKI/HSM/TEE guarantees, formal verification, penetration-test certification or Internet-scale performance.
The linked GitHub repository is public and source-available; its repository metadata does not expose an OSI-standard license identifier (NOASSERTION). Do not infer a broader open-source licensing status from public availability alone.
Relation to UHP
Section titled “Relation to UHP”UHP standardizes the external client↔server contract for executing a selected complete harness. A UHP-served harness may internally call tools, MCP servers, browser/computer-use surfaces or other effectful integrations, but those internal dispatch paths are not made execution-finality-safe merely because the outer UHP request is valid.
| Question | UHP | Tool-binding draft |
|---|---|---|
| How does a client invoke a complete harness? | Standardized by UHP | Out of scope |
| Is model-generated tool output itself authority? | No general UHP guarantee | Explicitly no |
| Where is the proposed enforcement point? | Implementation-specific internal runtime concern | Immediately before consequential dispatch / effectuation |
| Does it bind exact live arguments? | Not a general UHP primitive | Yes, through a canonical argument digest |
| Does it define MCP semantics? | No | No; it proposes a host/client-side binding around tools/call |
| Does it prove an external effect completed? | No general inference | No; local gating and effect completion remain distinct |
For UHP implementers, the relevance is architectural: protocol authorization to run a harness and authorization for a particular downstream effect are different decisions. A server can conform to UHP while still needing its own per-tool policy, credential, sandbox and effect-boundary controls.
Relation to control-delivery evidence
Section titled “Relation to control-delivery evidence”Agent Control Delivery Evidence asks what evidence justifies saying that a stop, revoke, cancel or other control was emitted, received, enforced and observed across all required targets.
This execution-finality draft asks a different question: what must happen before a generated consequential act is allowed to cross the dispatch boundary at all?
They can complement each other:
- execution-finality concerns pre-effect gating of a candidate act;
- control-delivery evidence concerns delivery/enforcement/effect reconciliation for controls across runtime boundaries.
Neither document is a UHP extension, and neither substitutes for UHP conformance.
Evidence boundaries
Section titled “Evidence boundaries”- Work in progress. Revision
-03can change, expire or be replaced. - Individual submission. The Datatracker explicitly says the document is not IETF-endorsed and has no formal standing in the standards process.
- Dependency on another draft. The binding maps generated calls into the separate Agent Candidate Act profile; maturity of this draft does not upgrade that dependency.
- Current MCP differs from one draft characterization. MCP
2026-07-28is stateless at the protocol core; session-oriented wording in the binding draft should not be copied into current MCP descriptions. - Reference code is bounded evidence. Synthetic tests demonstrate the supplied guarded path, not production deployment security or closure of every alternate effect path.
- Qwen case-study scope. PR #11826 is evidence that cross-harness tool-name mismatch can make an intended hook matcher inert and that Qwen now aliases its supported Claude Code names. It is not evidence of Internet-Draft adoption, a standardized hook vocabulary, or universal cross-harness portability.
- Hermes Gemini streaming case-study scope. PR #111686 is evidence that provider-stream translation can corrupt candidate-call identity and arguments before policy evaluation, and that Hermes now separates the reviewed Gemini cases. It is not evidence of Internet-Draft adoption, universal Gemini-stream correctness, or a UHP/MCP protocol change.
- Hermes Bedrock replay case-study scope. PR #115290 is evidence that provider block identity/order can be corrupted during stream assembly, persisted, and rejected on later replay, and that Hermes now preserves the reviewed indexed/fallback cases. It is not evidence of Internet-Draft adoption, universal Bedrock-stream correctness, replay repair for already-corrupt sidecars, or a UHP/MCP/ACP/A2A protocol change.
- Hermes cache case-study scope. PR #112443 proves the reviewed local-runtime downloader now gates durable cache publication on advertised-length completeness and uses per-transfer staging/cleanup. It does not prove cryptographic archive integrity, publisher authenticity, all-platform validation, Internet-Draft adoption, or a UHP/MCP/ACP protocol change.
- No UHP adoption claim. No reviewed primary source establishes a standardized UHP mapping or HarnessRouter implementation of this proposal.
Related pages
Section titled “Related pages”Read UHP security for the protocol’s documented trust boundaries, UHP vs MCP for current MCP architecture, Hermes for the upstream harness/runtime boundary, Agent Control Delivery Evidence for control-outcome reconciliation, and HarnessRisk for lifecycle-oriented adversarial harness evaluation.
Primary sources
Section titled “Primary sources”- IETF Datatracker — draft-das-agentic-tool-binding-03
- IETF HTML — draft-das-agentic-tool-binding-03
- Runnable reference implementation
- MCP 2026-07-28 changelog
- MCP 2026-07-28 transport overview
- Qwen Code issue #11823 — Claude Code tool names did not match tool hooks
- Qwen Code PR #11826 — accept Claude Code tool names in tool hook matchers
- Qwen Code merge
e04f2ec5 - Hermes PR #111686 — parallel Gemini calls keep distinct streaming slots
- Hermes merge
a55c972e - Hermes PR #115290 — preserve Bedrock streamed block identity through replay
- Hermes merge
e5af917a - Hermes issue #108200 — Bedrock assistant-prefill session failure
- Hermes PR #112443 — reject incomplete local-runtime downloads before cache publication
- Hermes merge
6005aa1f - Hermes local-runtime downloader at the merged fix