Problem
The compliance storyboard runner (in `@adcp/client`) injects `idempotency_key` on every MCP tool call, including read-only tools like `get_products` and `get_adcp_capabilities` that have no idempotent semantics.
Sellers using Python frameworks with strict defaults (FastMCP, Pydantic `extra='forbid'`) hit `ValidationError` before their handler runs, even though AdCP schemas explicitly allow `additionalProperties: true` and the handler would otherwise just ignore the field.
Originally surfaced as #4399. Closed PR #4405 attempted to fix this via Python-side docs guidance, but that treats the symptom rather than the cause — every Python implementer would have to absorb the workaround independently.
Why the storyboards aren't at fault
Storyboard YAML `sample_request` blocks for read tools do not include `idempotency_key`. The injection happens at runner dispatch time:
```
storyboard YAML → runner builds payload → injects idempotency_key → MCP tool/call
^^^^^^^^^^^^^^^^^^^^^^^^
should be conditional on tool semantics
```
Proposed fix
The runner should not inject `idempotency_key` on read tools. Concretely:
- In `@adcp/client`: when dispatching a tool call, check whether the tool is read-only (heuristic: tool name starts with `get_` / `list_`, OR check the tool's declared schema for absence of `idempotency_key` in `properties`). Skip injection on read tools.
- Spec clarification: codify "`idempotency_key` SHOULD NOT appear on read-tool requests; if it does, handlers MUST ignore it." in the idempotency docs so we have a normative anchor.
Out of scope
Refs
— Filed during triage 2026-05-13
Problem
The compliance storyboard runner (in `@adcp/client`) injects `idempotency_key` on every MCP tool call, including read-only tools like `get_products` and `get_adcp_capabilities` that have no idempotent semantics.
Sellers using Python frameworks with strict defaults (FastMCP, Pydantic `extra='forbid'`) hit `ValidationError` before their handler runs, even though AdCP schemas explicitly allow `additionalProperties: true` and the handler would otherwise just ignore the field.
Originally surfaced as #4399. Closed PR #4405 attempted to fix this via Python-side docs guidance, but that treats the symptom rather than the cause — every Python implementer would have to absorb the workaround independently.
Why the storyboards aren't at fault
Storyboard YAML `sample_request` blocks for read tools do not include `idempotency_key`. The injection happens at runner dispatch time:
```
storyboard YAML → runner builds payload → injects idempotency_key → MCP tool/call
^^^^^^^^^^^^^^^^^^^^^^^^
should be conditional on tool semantics
```
Proposed fix
The runner should not inject `idempotency_key` on read tools. Concretely:
Out of scope
Refs
— Filed during triage 2026-05-13