Skip to content

feat(messages): body-verify Phase 2 on cueapi_send_message (Mike directive 2026-05-11)#35

Merged
mikemolinet merged 1 commit into
mainfrom
feat/messages-body-verify-phase-2
May 12, 2026
Merged

feat(messages): body-verify Phase 2 on cueapi_send_message (Mike directive 2026-05-11)#35
mikemolinet merged 1 commit into
mainfrom
feat/messages-body-verify-phase-2

Conversation

@mikemolinet
Copy link
Copy Markdown
Collaborator

Summary

Parity port of body-verify Phase 2 to the MCP server's cueapi_send_message tool. Closes Backlog row cmp1v39ih.

When cueapi_send_message is invoked, the tool now sends X-CueAPI-Verify-Echo: true by default and compares the substrate-echoed body_received against 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

  • cueapi-cli #51 (Layer 3 force-file mode)
  • cueapi-cli #52 (Phase 2 auto-verify with --no-verify opt-out)
  • cueapi-cli #53 (body_received-is-dict defensive isinstance)
  • cueapi-python #39 (auto_verify kwarg)
  • cueapi-python #40 (body_received-is-dict defensive isinstance)

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> (default true) — opt-out for perf-sensitive flows.

Wire shape

Caller invocation X-CueAPI-Verify-Echo header body-verify check runs
(omitted) "true" yes
auto_verify: true "true" yes
auto_verify: false omitted no

body_received shape handling (defensive)

Substrate emits SDK reads received as Why
body_received: "hello" (string) "hello" Current spec-lock per cueapi/cueapi#798 + cueapi-core #88
body_received: {body: "hello", ...} (dict) "hello" (from .body) Brief wire-shape window between core #86 and #88 — could resurface in a future substrate rev
body_received absent check skipped Server may not echo for various reasons; we don't fail on absence, only on present-but-mismatched

Tests

8 new (dedicated describe block "cueapi_send_message — body-verify Phase 2"):

  • default verify-on sends X-CueAPI-Verify-Echo: true header
  • auto_verify=false opt-out omits X-CueAPI-Verify-Echo header
  • matching body_received passes through silently (STRING shape per #798 spec-lock)
  • matching body_received as dict shape (defensive fallback for pre-#798 substrate) passes
  • mismatched body_received throws with byte-divergence diagnostic
  • mismatch error includes the divergent-byte index
  • mismatch error message names the message_id from the response
  • missing 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 body
  • idempotency_key goes via Idempotency-Key HEADER, not body

Full suite: 142/142 passing (was 134; +8 new).

Parity-impact checklist

  • Server contract (cueapi/cueapi#795 + #797 + #798) reviewed
  • MCP schema addition is additive (default verify-on matches the cli + python default)
  • Defensive isinstance fallback handles both STRING and dict body_received shapes
  • Tests cover all 8 verification cases
  • cueapi-action wires through (deferred — cueapi-action chore(deps): bump actions/setup-node from 4.4.0 to 6.4.0 #16 already forwards Phase 3 body source inputs to cueapi-cli; Phase 2 is wired at the cli layer it shells out to)

Backlog row: cmp1v39ih.

🤖 Generated with Claude Code

…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.
@mikemolinet mikemolinet merged commit dc04bf5 into main May 12, 2026
2 checks passed
@mikemolinet mikemolinet deleted the feat/messages-body-verify-phase-2 branch May 12, 2026 00:30
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant