Problem
Partner-admin write tools in src/partner/tools.ts (steam_uploadStoreImage, steam_uploadTrailer) refuse an unconfirmed live call by returning:
{ \"ok\": false, \"error\": \"confirm must be true when dry_run is false. No request was sent.\" }
That payload is a normal MCP text result. It does not set isError: true and it is not prefixed with [CONFIRM_REQUIRED].
The shared helper refusal() in src/utils/confirm.ts (used by the five default-bin write tools) returns:
isError: true
- text starting with
[CONFIRM_REQUIRED]
Why it matters
Clients that branch on isError (or scan for [CONFIRM_REQUIRED]) will treat partner-admin refusals as success-shaped data. The gate still blocks the network call; the response contract is just inconsistent.
Scope
Deliberately deferred out of PR #21 so that PR stays scoped to the security fix (confirm/dry-run on default-bin mutations, plus untrusted-content labels). Do not bundle this refactor into #21.
Suggested follow-up
Use refusal() from src/utils/confirm.ts in the partner-admin unconfirmed path without changing dry-run or live-upload behavior.
Problem
Partner-admin write tools in
src/partner/tools.ts(steam_uploadStoreImage,steam_uploadTrailer) refuse an unconfirmed live call by returning:{ \"ok\": false, \"error\": \"confirm must be true when dry_run is false. No request was sent.\" }That payload is a normal MCP text result. It does not set
isError: trueand it is not prefixed with[CONFIRM_REQUIRED].The shared helper
refusal()insrc/utils/confirm.ts(used by the five default-bin write tools) returns:isError: true[CONFIRM_REQUIRED]Why it matters
Clients that branch on
isError(or scan for[CONFIRM_REQUIRED]) will treat partner-admin refusals as success-shaped data. The gate still blocks the network call; the response contract is just inconsistent.Scope
Deliberately deferred out of PR #21 so that PR stays scoped to the security fix (confirm/dry-run on default-bin mutations, plus untrusted-content labels). Do not bundle this refactor into #21.
Suggested follow-up
Use
refusal()fromsrc/utils/confirm.tsin the partner-admin unconfirmed path without changing dry-run or live-upload behavior.