jcp: hand the caller its receipt, in MCP's namespaced _meta slot #69
Merged
dcharlot
merged 1 commit from 2026-09-03 15:12:27 -04:00
jcp/mcp-meta-receipt into main 2026-08-29
jcp: hand the caller its receipt, in MCP's namespaced _meta slot
The gateway authorized every `tools/call`, metered it on real hardware, debited the grant and sealed allow and deny alike into a JCR-1 chain — and the client never saw any of it. The receipt lived in `gateway.receipts()`: the enforcing party keeping an artifact about itself, which the subject of the decision could not present, dispute, or forward. That is most of what a receipt is for. MCP's 2026-07-28 revision supplies the place to put it. `_meta` is now a first-class namespaced extension point with a 12-month deprecation policy behind it, so a policy object carried there has a defined home rather than an invented one, and the home stops moving. - An allowed call returns its sealed receipt in `result._meta` under `science.transaction/jcp`, beside the upstream's own untouched payload, with the measured joules and the remaining budget. - A denial returns its sealed refusal too, in `error.data._meta` — JSON-RPC reserves `data` for exactly this. A refusal the caller cannot see is an enforcement nobody can audit, and a receipt's value does not depend on the verdict going the caller's way. - The witness moves from the hand-rolled flat `params._jcp_witness` into the namespaced slot, and the flat key keeps working. A silent behaviour change at an enforcement point is worse than a legacy key, so the fallback stays until something forces it out. The returned bytes are the same sealed envelope the gateway logged, not a summary of it — asserted directly rather than described. Attaching is defensive: a response that is not a JSON-RPC result object comes back untouched, because this must never mangle an upstream payload it does not understand. Base64 is hand-rolled (no new dependency for one encode) and pinned against the RFC 4648 §10 vectors, since a receipt that does not decode is not a receipt. Found by the 2026-08-29 sweep, whose repo-check phase also refuted two things I had queued as gaps — the IETF agent-audit-trail draft and a hardware-rooted attestation paper are both already shipped, the draft being a strictly weaker subset with an optional signature and no energy field. Not in this change, and worth its own: MCP's new `CacheableResult`/`ttlMs` makes a cache hit into joules avoided, and nothing in the family binds a TTL'd result to an energy receipt. Verified: 245 jcp-rs tests pass (5 new), conformance passes all classes, published vectors unchanged on disk, no new clippy warnings.