feat(messages): body-verify Phase 2 on cueapi_send_message (Mike directive 2026-05-11)#35
Merged
Merged
Conversation
…ctive 2026-05-11)
Parity port of cueapi-cli #51/#52/#53 (Layer 3 force-file + Phase 2
auto-verify + body_received-is-dict isinstance fix) and cueapi-python
#39/#40 (auto_verify kwarg + body_received-is-dict isinstance fix).
The MCP `cueapi_send_message` tool now sends `X-CueAPI-Verify-Echo: true`
by default, then compares the substrate-echoed `body_received` against
the body the caller passed. On mismatch, the handler throws an MCP-
visible error with a byte-level divergence index + message_id so callers
can quickly locate caller-side body corruption (typically shell expansion
of $(...) / `...` / ${VAR} before the MCP tool received the arg).
Schema additions:
- `auto_verify: optional<boolean>` (default true) — opt-out for
perf-sensitive flows. Set false to skip the header + the check.
Handler additions:
- When verify-on, set `X-CueAPI-Verify-Echo: true` extraHeader.
- After response, extract `body_received` defensively: STRING per the
spec-lock (cueapi/cueapi#798 + cueapi-core #88), but fall back to
`body_received.body` if the substrate emits the older dict shape (the
brief wire-shape window between cueapi-core #86 and #88). Matches the
defensive isinstance pattern in cueapi-cli #53 and cueapi-python #40.
- Throw on mismatch with: byte-divergence index + message_id + diagnostic
pointing to caller-side shell-expansion as the typical cause.
Tests (8 new, in a dedicated describe block):
- default-on sends the header
- auto_verify=false opt-out omits the header
- matching STRING shape passes silently
- matching dict-shape (defensive fallback) passes silently
- mismatch throws with body-verify-mismatch message
- error includes byte-divergence index
- error names message_id
- missing body_received (substrate didn't echo) is silently OK
(only present-but-mismatched fails — keeps the check forgiving on
substrate changes that drop the echo entirely)
Plus 2 existing tests updated (assert extraHeaders shape includes the
new default `X-CueAPI-Verify-Echo: true`):
- "sends `from` via X-Cueapi-From-Agent HEADER, NOT in the body" (×2)
- "idempotency_key goes via Idempotency-Key HEADER, not body" (×2)
Backlog row: cmp1v39ih.
Merged
4 tasks
mikemolinet
added a commit
that referenced
this pull request
May 12, 2026
…ueapi-python #41 parity) (#36) Parity port of cueapi-cli #55 and cueapi-python #41 — body-verify Phase 2 on cueapi_fire_cue tool, OPT-IN (default false). ## Design rationale (verbatim from primary's #41) Substrate /v1/cues/{id}/fire echoes a pydantic-after-parse body that may include server-side default-population, causing spurious diff vs the tool's JSON.stringify(body). So auto_verify is opt-in — caller flips it on when they know substrate echo semantics match their serialization. Diverges from cueapi_send_message (cueapi-mcp #35) which is default- on: that endpoint echoes the raw STRING body field per the #798 spec- lock — no parsed-defaulted shape concern. ## Implementation - Schema: auto_verify: optional<boolean> (default false). Help text documents the opt-in rationale + the substrate-echo-shape concern. - Handler: when auto_verify=true, sends X-CueAPI-Verify-Echo: true header + captures sentBodyStr = JSON.stringify(body). After response, compares against body_received (defensive isinstance: STRING per spec-lock OR dict pre-#798 wire shape → JSON.stringify for compare). - Throws on mismatch with byte-divergence index + execution id + diagnostic pointing to caller-side payload mutation as typical cause. ## Notable trade-off vs cueapi-cli/python Skips the SHA256 constant-cost fast-path that python #41 and cli #55 both have. Reason: Web Crypto SubtleCrypto.digest() is async; embedding it in this synchronous-shape handler would force the whole tool async- contortion. Accepts the trade-off of string-compare-only for the MCP TS port. JSON.stringify deterministic serialization on Node makes the string compare reliable for non-trivial payloads. ## Tests (7 new) - default (no auto_verify) omits X-CueAPI-Verify-Echo header - auto_verify=true sends the header - matching body_received (STRING shape post-#798) passes silently - matching body_received (dict shape pre-#798) passes via JSON.stringify-equivalence - mismatched body_received throws with byte-divergence diagnostic - mismatch error includes execution id from response - missing body_received (substrate didn't echo) is silently OK Full suite: 149/149 passing (was 142 + 7 new). Backlog row: cmp1wj2a6.
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.
Summary
Parity port of body-verify Phase 2 to the MCP server's
cueapi_send_messagetool. Closes Backlog rowcmp1v39ih.When
cueapi_send_messageis invoked, the tool now sendsX-CueAPI-Verify-Echo: trueby default and compares the substrate-echoedbody_receivedagainst the body the MCP caller passed. On mismatch, the handler throws an MCP-visible error with a byte-level divergence index + message_id so callers can locate caller-side body corruption (typically shell expansion of$(...), backticks, or${VAR}before the MCP tool received the arg).Parity references
--no-verifyopt-out)This PR mirrors that defense across the MCP surface so messages sent via Claude Desktop / Cursor / any MCP host get the same corruption-detection guarantee.
Schema addition
auto_verify: optional<boolean>(defaulttrue) — opt-out for perf-sensitive flows.Wire shape
X-CueAPI-Verify-Echoheader"true"auto_verify: true"true"auto_verify: falsebody_received shape handling (defensive)
receivedasbody_received: "hello"(string)"hello"body_received: {body: "hello", ...}(dict)"hello"(from.body)body_receivedabsentTests
8 new (dedicated describe block "cueapi_send_message — body-verify Phase 2"):
default verify-on sends X-CueAPI-Verify-Echo: true headerauto_verify=false opt-out omits X-CueAPI-Verify-Echo headermatching body_received passes through silently (STRING shape per #798 spec-lock)matching body_received as dict shape (defensive fallback for pre-#798 substrate) passesmismatched body_received throws with byte-divergence diagnosticmismatch error includes the divergent-byte indexmismatch error message names the message_id from the responsemissing body_received in response is silently OK (substrate didn't echo)2 existing tests updated (assert extraHeaders shape includes the new default
X-CueAPI-Verify-Echo: true):sends from via X-Cueapi-From-Agent HEADER, NOT in the bodyidempotency_key goes via Idempotency-Key HEADER, not bodyFull suite: 142/142 passing (was 134; +8 new).
Parity-impact checklist
Backlog row:
cmp1v39ih.🤖 Generated with Claude Code