From c87033969c3869588a6754d8e02fb7adc924c209 Mon Sep 17 00:00:00 2001 From: ContextVM Date: Sun, 3 May 2026 21:51:13 +0200 Subject: [PATCH 1/3] docs(cep-8)WIP: add payment interaction negotiation feature Add support for session-level payment interaction negotiation through the new `payment_interaction` tag, enabling clients to choose between `transparent` (default) and `explicit_gating` semantics for payment-gated invocations. --- src/content/docs/spec/ceps/cep-8.md | 151 ++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/src/content/docs/spec/ceps/cep-8.md b/src/content/docs/spec/ceps/cep-8.md index 852c674..7fa676e 100644 --- a/src/content/docs/spec/ceps/cep-8.md +++ b/src/content/docs/spec/ceps/cep-8.md @@ -22,6 +22,7 @@ ContextVM pricing for capabilities is implemented through a standardized mechani 1. **Pricing Tags**: Servers advertise pricing information using the `cap` tag 2. **Payment Method Identifiers (PMI)**: Both parties advertise supported payment methods using the `pmi` tag 3. **Payment Notifications**: Servers notify clients of payment requirements through the `notifications/payment_required` notification, and MAY acknowledge outcomes with `notifications/payment_accepted` / `notifications/payment_rejected` +4. **Payment Interaction Negotiation**: Clients MAY advertise a preferred session-level payment interaction semantic using the `payment_interaction` tag When a capability requires payment, the server acts as the payment processor (generating and validating payment requests) while the client acts as the payment handler (executing payments for supported payment methods). Clients can discover supported payment methods beforehand through PMI discovery, enabling informed decisions before initiating requests. @@ -34,6 +35,7 @@ This CEP defines: - How servers advertise **reference pricing** for capabilities. - How clients and servers advertise supported payment methods. - A minimal notification-based flow for requesting and acknowledging payments. +- An optional session-level negotiation surface for how payment-gated invocations are exposed to clients. This CEP does **not** define: @@ -88,6 +90,27 @@ The `direct_payment` tag is an optional optimization for bearer-asset payment me ["direct_payment", "", ""] ``` +#### `payment_interaction` Tag (optional) + +The `payment_interaction` tag is an optional negotiation tag used by clients to advertise the preferred payment interaction semantic for the current session. + +```json +["payment_interaction", ""] +``` + +Where `` is one of: + +- `transparent`: The default CEP-8 behavior. Payment handling is treated as transport/client middleware behavior. After payment is verified, the server MAY continue fulfilling the original invocation. +- `explicit_gating`: Payment must be surfaced as a gate for the current session. After payment is verified, the server SHOULD treat the original invocation as not fulfilled and require a subsequent invocation to consume the paid authorization. + +##### Notes + +- `payment_interaction` is a negotiation tag, not a pricing tag. +- Clients SHOULD send at most one `payment_interaction` tag on the first direct client-to-server message of a session. +- When present on that first direct message, it participates in the session discovery baseline described by [CEP-35: Stateless Session Discovery and Capability Learning](/spec/ceps/informational/cep-35). +- If omitted, `transparent` is the default. +- Servers MAY advertise supported interaction semantics in initialization responses or public announcements using the same tag format. + #### `change` Tag (optional) The `change` tag is an optional settlement artifact for bearer-asset payment methods. @@ -191,6 +214,30 @@ Using standardized PMIs provides: PMI discovery allows clients and servers to determine compatibility with payment methods, similar to encryption support discovery in [CEP-4](/spec/ceps/cep-4). +### Payment Interaction Negotiation + +CEP-8 defines an optional session-level negotiation surface for how payment-gated invocations should behave from the client's perspective. + +The transport primitive remains notification-based in both cases. The negotiated interaction affects how the server treats the original invocation after payment is verified. + +#### Advertisement and learning + +- Clients MAY advertise a preferred interaction using the `payment_interaction` tag. +- Servers MAY advertise supported interaction semantics in initialization responses, first direct responses, or public announcements. +- In stateless operation, `payment_interaction` follows the first-message exchange and capability-learning rules described by [CEP-35: Stateless Session Discovery and Capability Learning](/spec/ceps/informational/cep-35). + +#### Session-level semantics + +This CEP defines `payment_interaction` as a session-level negotiation tag. + +- When a client includes `payment_interaction` on the first direct client-to-server message of a session, that value expresses the client's preferred payment interaction semantic for the session. +- After the first-message exchange, implementations SHOULD omit repeated `payment_interaction` tags unless a future CEP defines stronger update semantics. +- If no compatible explicit preference is negotiated, implementations MUST fall back to `transparent` behavior. +- `transparent` means payment may be handled without surfacing an unfulfilled invocation to higher-level logic. +- `explicit_gating` means payment is surfaced as an unfulfilled, payment-gated outcome and the paid result is returned only on a subsequent invocation. + +Servers MAY advertise support for `explicit_gating` in discovery, initialization responses, or public announcements. Clients that want `explicit_gating` in stateless operation SHOULD request it on the first direct invocation event of the session. + #### PMI Advertisement Servers advertise supported PMIs using the `pmi` tag in initialization responses or public announcements: @@ -229,6 +276,27 @@ Clients advertise their supported PMIs in initialization requests: } ``` +Clients that prefer explicit payment gating for the session MAY include `payment_interaction` alongside `pmi` tags on the first direct message they send: + +```json +{ + "kind": 25910, + "content": { + "jsonrpc": "2.0", + "id": 0, + "method": "initialize", + "params": { + // Initialization parameters + } + }, + "tags": [ + ["p", ""], + ["pmi", "bitcoin-lightning-bolt11"], + ["payment_interaction", "explicit_gating"] + ] +} +``` + #### Discovery Methods Clients can discover PMI support through: @@ -240,6 +308,7 @@ Clients can discover PMI support through: Servers can discover PMI support through: 1. **Client Initialization Request**: Check `pmi` tags in client initialization request +2. **First direct client message in stateless operation**: Check `pmi` and `payment_interaction` tags on the first request event of the session ##### Stateless operation @@ -247,6 +316,8 @@ In stateless operation (no prior initialization), clients that want to use paid When sent on the first direct client-to-server message of a session, these `pmi` tags participate in the session discovery baseline described by [CEP-35: Stateless Session Discovery and Capability Learning](/spec/ceps/informational/cep-35). When sent on later requests, they are interpreted in the context of those requests unless another CEP explicitly defines stronger session-update semantics. +Clients that prefer `explicit_gating` SHOULD also include `payment_interaction` on that first direct request so the server can apply the session's negotiated payment interaction behavior from the start. + ### Payment Flow The complete payment flow for a capability with pricing information follows these steps: @@ -313,6 +384,78 @@ If the client included one or more `pmi` tags in the original request, the serve If the client did not advertise any PMIs (for example, in a purely stateless request), the server MAY send multiple `notifications/payment_required` notifications (for example, one per supported PMI). Clients MAY ignore any or all payment requests. +#### 3a. Interaction behavior + +Server and client behavior depends on the negotiated `payment_interaction` semantic for the session. + +##### `transparent` behavior + +In `transparent` mode, payment handling is treated as transport/client middleware behavior. + +- After payment is verified, the server MAY continue fulfilling the original invocation. +- The payment flow may remain invisible to higher-level agent logic. +- This is the default CEP-8 behavior. + +##### `explicit_gating` behavior + +In `explicit_gating` mode, payment is treated as an invocation gate for the session. + +- When the server determines that payment is required, it MUST emit `notifications/payment_required` and MUST NOT fulfill the triggering invocation. +- The original invocation becomes payment-gated and remains unfulfilled unless the client later sends a subsequent invocation. +- Clients SHOULD surface this state to higher-level application, user, or agent logic as a structured error-like result. +- When a JSON-RPC error object is used by the client runtime, clients SHOULD use code `-32042` with message `Payment Required`. +- The surfaced error data SHOULD include the payment details from `notifications/payment_required`, including `amount`, `pmi`, `pay_req`, optional `description`, optional `ttl`, and optional `_meta`. +- If payment is later verified, the server MUST NOT treat that verification as automatic completion of the original invocation. +- Instead, the server SHOULD create redeemable paid authorization state bound to the verified payment scope. +- The server MAY emit `notifications/payment_accepted` to acknowledge that payment was accepted, but in `explicit_gating` mode that notification does not fulfill the original invocation. +- To obtain the capability result, the client MUST send a subsequent invocation. +- On that subsequent invocation, the server SHOULD check for matching paid authorization, consume it atomically if valid, and then fulfill the invocation. + +This behavior is intended for clients that want payment to be surfaced explicitly to higher-level application or agent logic while preserving CEP-8's notification-based transport primitive. + +##### `explicit_gating` lifecycle + +The `explicit_gating` lifecycle is: + +1. The client negotiates `payment_interaction=explicit_gating` on the first direct message of the session. +2. The client invokes a priced capability. +3. The server evaluates the request, determines that payment is required, and emits `notifications/payment_required` correlated to the request event. +4. The server does not send the capability result for that invocation. +5. The client maps the payment requirement into a structured surfaced failure or gated result for higher-level logic. +6. A user, application, or agent decides whether to satisfy the payment request. +7. If payment is attempted, the server verifies it according to the selected PMI. +8. If verification succeeds, the server may emit `notifications/payment_accepted` and records redeemable paid authorization state. +9. The original invocation remains unfulfilled and is not resumed automatically. +10. The client sends a subsequent invocation for the paid capability. +11. The server validates that the subsequent invocation matches the recorded authorization scope. +12. The server consumes that authorization and returns the capability result on the subsequent invocation. + +##### Client surfaced error shape + +In `explicit_gating` mode, clients SHOULD expose the payment gate as structured invocation failure rather than as an opaque string. + +Conceptually, the surfaced object is: + +```json +{ + "code": -32042, + "message": "Payment Required", + "data": { + "amount": 100, + "pmi": "bitcoin-lightning-bolt11", + "pay_req": "lnbc...", + "description": "Payment for tool execution", + "ttl": 600, + "retry_after_payment": true, + "_meta": { + "note": "Optional PMI-specific metadata" + } + } +} +``` + +This error shape is client-side presentation guidance for request/response runtimes. The CEP-8 wire primitive remains `notifications/payment_required`. + #### 4. Payment Accepted Notification Once payment is verified, the server SHOULD notify the client that payment has been accepted. @@ -389,6 +532,8 @@ Once payment is verified, the server processes the capability request and respon } ``` +In `explicit_gating` mode, this capability result is typically returned on the subsequent invocation that consumes the satisfied payment authorization, not on the original invocation that triggered `notifications/payment_required`. + ### Payment Request Notification Fields The `notifications/payment_required` notification `params` object contains: @@ -422,6 +567,8 @@ The `notifications/payment_accepted` notification `params` object contains: If the server returns change for a bearer-asset direct payment, it SHOULD include a [`change` tag](#change-tag-optional) on the event. In that case, `amount` is the final amount charged for the request. +In `explicit_gating` mode, `notifications/payment_accepted` acknowledges successful payment verification and optional gate creation. It does not mean the original invocation has been fulfilled. + ### Payment Rejected Notification Fields The `notifications/payment_rejected` notification `params` object contains: @@ -477,6 +624,10 @@ Payment-related notifications MUST include an `e` tag referencing the original r Clients MAY retry publishing the same request event (same event id) to achieve idempotent semantics. Servers SHOULD treat duplicate request events with the same id as retries and MUST NOT charge more than once for the same request. +In `explicit_gating` mode, successful payment verification SHOULD create a redeemable payment authorization whose scope is defined by server policy. At minimum, servers SHOULD bind that authorization to the requesting client and the priced capability. Servers SHOULD also bind it to a quote, challenge, or equivalent server-side payment record. Servers MAY bind more narrowly, for example to a quoted amount, request shape, capability arguments, payment rail, or expiry. + +Unless server policy explicitly defines otherwise, this authorization SHOULD be single-use. Servers SHOULD consume that authorization atomically when fulfilling the subsequent paid invocation, MUST prevent concurrent double-consumption, and MUST NOT use payment acceptance alone as fulfillment of the original invocation. + ## Backward Compatibility This CEP introduces no breaking changes to the existing protocol: From 9c335764cd0664ad56ce1f0435d8fb4af695b7d4 Mon Sep 17 00:00:00 2001 From: ContextVM Date: Mon, 1 Jun 2026 11:19:50 +0200 Subject: [PATCH 2/3] docs(cep-8): clarify payment interaction lifecycles and add explicit gating Restructures the payment flow documentation to clearly distinguish between the default transparent notification-based lifecycle and the explicit gating lifecycle. Adds JSON-RPC error codes (-32042 Payment Required, -32043 Payment Pending) for explicit gating mode, defines authorization identity using RFC 8785 canonicalization, and clarifies that payment processing is a transport concern that MCP handlers may remain unaware of. Removes redundant sections and updates notification field references for consistency. --- src/content/docs/spec/ceps/cep-8.md | 301 ++++++++++++++-------------- 1 file changed, 153 insertions(+), 148 deletions(-) diff --git a/src/content/docs/spec/ceps/cep-8.md b/src/content/docs/spec/ceps/cep-8.md index 7fa676e..a3e5792 100644 --- a/src/content/docs/spec/ceps/cep-8.md +++ b/src/content/docs/spec/ceps/cep-8.md @@ -11,20 +11,20 @@ description: Pricing mechanism and payment processing for ContextVM capabilities ## Abstract -This CEP proposes a standardized pricing mechanism and payment flow for MCP capabilities over ContextVM. The mechanism allows servers to advertise pricing for their capabilities, enables clients to discover and pay for these capabilities through various payment methods, and defines a notification system for payment requests. This creates a sustainable ecosystem for capability servers while maintaining the decentralized nature of the protocol. +This CEP proposes a standardized pricing mechanism and payment flow for MCP capabilities over ContextVM. The mechanism allows servers to advertise pricing for their capabilities, enables clients to discover and pay for these capabilities through various payment methods, and defines transport-level payment interaction lifecycles. This creates a sustainable ecosystem for capability servers while maintaining the decentralized nature of the protocol. ## Specification ### Overview -ContextVM pricing for capabilities is implemented through a standardized mechanism with three main components: +ContextVM pricing for capabilities is implemented through four protocol surfaces: -1. **Pricing Tags**: Servers advertise pricing information using the `cap` tag -2. **Payment Method Identifiers (PMI)**: Both parties advertise supported payment methods using the `pmi` tag -3. **Payment Notifications**: Servers notify clients of payment requirements through the `notifications/payment_required` notification, and MAY acknowledge outcomes with `notifications/payment_accepted` / `notifications/payment_rejected` -4. **Payment Interaction Negotiation**: Clients MAY advertise a preferred session-level payment interaction semantic using the `payment_interaction` tag +1. **Pricing tags**: Servers advertise reference prices using the `cap` tag. +2. **Payment Method Identifiers (PMIs)**: Peers advertise compatible settlement methods using the `pmi` tag. +3. **Payment interaction negotiation**: Peers use the default `transparent` lifecycle or negotiate `explicit_gating` for agent-visible payment gates. +4. **Payment messages**: Servers request and acknowledge payment through notifications in `transparent` mode, or JSON-RPC errors in `explicit_gating` mode. -When a capability requires payment, the server acts as the payment processor (generating and validating payment requests) while the client acts as the payment handler (executing payments for supported payment methods). Clients can discover supported payment methods beforehand through PMI discovery, enabling informed decisions before initiating requests. +When a capability requires payment, the server acts as the payment processor (generating and validating payment requests) while the client acts as the payment handler (executing payments for supported payment methods). Clients can discover supported payment methods beforehand through PMI discovery. Servers MAY waive payment for a priced capability invocation based on server-side policy (for example, prepaid balances, subscriptions, allowlists, or internal accounting) and fulfill the request without emitting `notifications/payment_required`. @@ -34,14 +34,15 @@ This CEP defines: - How servers advertise **reference pricing** for capabilities. - How clients and servers advertise supported payment methods. -- A minimal notification-based flow for requesting and acknowledging payments. -- An optional session-level negotiation surface for how payment-gated invocations are exposed to clients. +- A default transparent notification-based flow for requesting and acknowledging payments. +- An optional explicit gating lifecycle for returning payment requirements as invocation errors. This CEP does **not** define: - Privacy guarantees for payment messages (use encryption mechanisms in [CEP-4](/spec/ceps/cep-4) where required). - Rate limiting / abuse prevention mechanisms. - Currency conversion rules or exchange rate discovery. +- Application-level payment APIs. Payment processing is a transport concern; underlying MCP handlers MAY remain unaware of CEP-8 payments. ### New Tags Introduced @@ -68,7 +69,7 @@ Where: ##### Notes -- The `cap` tag is a **reference** price signal for discovery and UX. The actual `amount` requested for payment is provided in [`notifications/payment_required`](#payment-request-notification-fields). +- The `cap` tag is a **reference** price signal for discovery and UX. The actual `amount` requested for payment is provided in [`notifications/payment_required`](#payment-required-fields) in the transparent lifecycle or in a `Payment Required` error payment option in the explicit gating lifecycle. - If `` is a range, servers MAY request any `amount` within the advertised inclusive range. Clients MAY accept or ignore the payment request based on their own policy. - If multiple `cap` tags are present for the same capability, clients SHOULD prefer the most specific and most recent context (for example, a live `tools/list` response over a public announcement). @@ -100,8 +101,8 @@ The `payment_interaction` tag is an optional negotiation tag used by clients to Where `` is one of: -- `transparent`: The default CEP-8 behavior. Payment handling is treated as transport/client middleware behavior. After payment is verified, the server MAY continue fulfilling the original invocation. -- `explicit_gating`: Payment must be surfaced as a gate for the current session. After payment is verified, the server SHOULD treat the original invocation as not fulfilled and require a subsequent invocation to consume the paid authorization. +- `transparent`: The default CEP-8 behavior. Payment is handled by transport/client middleware using payment notifications. After payment is verified, the server transport MAY continue fulfilling the original invocation. +- `explicit_gating`: Payment is surfaced as the response to the invocation. When payment is required, the server transport returns a JSON-RPC `Payment Required` error and does not forward the invocation to the underlying MCP handler until a later matching invocation consumes paid authorization. ##### Notes @@ -115,7 +116,7 @@ Where `` is one of: The `change` tag is an optional settlement artifact for bearer-asset payment methods. -It allows a server to return overpayment remainder on the [`notifications/payment_accepted`](#payment-accepted-notification-fields) event. +It allows a server to return overpayment remainder on the [`notifications/payment_accepted`](#payment-accepted-fields) event. ```json ["change", "", ""] @@ -185,9 +186,9 @@ The protocol supports multiple payment methods through Payment Method Identifier PMIs are not only a discovery label; they define the **settlement protocol surface** for CEP-8 payments. -- The `pmi` value in `notifications/payment_required` defines how a payment handler MUST interpret the associated opaque `pay_req` string. +- The `pmi` value in `notifications/payment_required` or in an explicit gating payment option defines how a payment handler MUST interpret the associated opaque `pay_req` string. - The format and semantics of `pay_req` are **PMI-defined**. -- The optional `_meta` objects in `notifications/payment_required` and `notifications/payment_accepted` MAY contain PMI-specific fields. Unknown `_meta` fields MUST be ignored. +- The optional `_meta` objects in payment requests, payment options, and payment acknowledgments MAY contain PMI-specific fields. Unknown `_meta` fields MUST be ignored. In other words, `pmi` is the type tag for `pay_req` (analogous to a content-type). @@ -195,36 +196,21 @@ In other words, `pmi` is the type tag for `pay_req` (analogous to a content-type PMIs MUST follow the format defined by the [W3C Payment Method Identifiers](https://www.w3.org/TR/payment-method-id/) specification, matching the pattern: `[a-z0-9-]+`. -##### Recommended PMIs (ContextVM ecosystem) +##### Recommended PMIs This CEP maintains no in-document registry of recommended PMIs. Recommended PMIs and naming conventions are documented in the informational companion CEP, [CEP-21: Payment Method Identifier (PMI) Recommendations](/spec/ceps/informational/cep-21). -#### PMI Benefits and Roles - -Using standardized PMIs provides: - -1. **Interoperability**: Clear communication about supported payment methods -2. **Extensibility**: Easy addition of new payment methods -3. **Multi-currency support**: Different PMIs handle different currencies and networks -4. **Clear separation of concerns**: Servers focus on payment processing, clients on payment handling - ### PMI Discovery -PMI discovery allows clients and servers to determine compatibility with payment methods, similar to encryption support discovery in [CEP-4](/spec/ceps/cep-4). +PMI discovery allows clients and servers to determine payment-method compatibility before, or during, paid capability use. ### Payment Interaction Negotiation -CEP-8 defines an optional session-level negotiation surface for how payment-gated invocations should behave from the client's perspective. - -The transport primitive remains notification-based in both cases. The negotiated interaction affects how the server treats the original invocation after payment is verified. - -#### Advertisement and learning +CEP-8 defines an optional session-level negotiation surface for how payment-gated invocations are exposed by the transport. -- Clients MAY advertise a preferred interaction using the `payment_interaction` tag. -- Servers MAY advertise supported interaction semantics in initialization responses, first direct responses, or public announcements. -- In stateless operation, `payment_interaction` follows the first-message exchange and capability-learning rules described by [CEP-35: Stateless Session Discovery and Capability Learning](/spec/ceps/informational/cep-35). +Payment interaction is a transport concern. A server transport MAY enforce payment before forwarding a priced invocation to the underlying MCP handler, and the underlying handler MAY remain unaware that payment was required. #### Session-level semantics @@ -232,13 +218,14 @@ This CEP defines `payment_interaction` as a session-level negotiation tag. - When a client includes `payment_interaction` on the first direct client-to-server message of a session, that value expresses the client's preferred payment interaction semantic for the session. - After the first-message exchange, implementations SHOULD omit repeated `payment_interaction` tags unless a future CEP defines stronger update semantics. -- If no compatible explicit preference is negotiated, implementations MUST fall back to `transparent` behavior. -- `transparent` means payment may be handled without surfacing an unfulfilled invocation to higher-level logic. -- `explicit_gating` means payment is surfaced as an unfulfilled, payment-gated outcome and the paid result is returned only on a subsequent invocation. +- `transparent` is the default and remains the compatibility baseline. +- `explicit_gating` applies only when requested by the client and supported by the server for the session. If no compatible explicit preference is negotiated, implementations MUST use `transparent`. +- `transparent` means payment may be handled through payment notifications without surfacing payment as the final invocation outcome. +- `explicit_gating` means payment is surfaced as a JSON-RPC error response to the invocation. The paid result is returned only if the client later sends a matching invocation after payment authorization is available. -Servers MAY advertise support for `explicit_gating` in discovery, initialization responses, or public announcements. Clients that want `explicit_gating` in stateless operation SHOULD request it on the first direct invocation event of the session. +Servers MAY advertise support for `explicit_gating` in discovery, initialization responses, first direct responses, or public announcements. In stateless operation, `payment_interaction` follows the first-message exchange and capability-learning rules described by [CEP-35: Stateless Session Discovery and Capability Learning](/spec/ceps/informational/cep-35). Clients that want `explicit_gating` in stateless operation SHOULD request it on the first direct invocation event of the session. -#### PMI Advertisement +#### PMI and interaction advertisement Servers advertise supported PMIs using the `pmi` tag in initialization responses or public announcements: @@ -255,28 +242,7 @@ Servers advertise supported PMIs using the `pmi` tag in initialization responses } ``` -Clients advertise their supported PMIs in initialization requests: - -```json -{ - "kind": 25910, - "content": { - "jsonrpc": "2.0", - "id": 0, - "method": "initialize", - "params": { - // Initialization parameters - } - }, - "tags": [ - ["p", ""], - ["pmi", "bitcoin-lightning-bolt11"], - ["pmi", "another-payment-method"] - ] -} -``` - -Clients that prefer explicit payment gating for the session MAY include `payment_interaction` alongside `pmi` tags on the first direct message they send: +Clients advertise supported PMIs in initialization requests. Clients that prefer explicit payment gating MAY include `payment_interaction` alongside `pmi` tags on the first direct message they send: ```json { @@ -292,23 +258,15 @@ Clients that prefer explicit payment gating for the session MAY include `payment "tags": [ ["p", ""], ["pmi", "bitcoin-lightning-bolt11"], + ["pmi", "another-payment-method"], ["payment_interaction", "explicit_gating"] ] } ``` -#### Discovery Methods +#### Discovery methods -Clients can discover PMI support through: - -1. **Public Announcements**: Check `pmi` tags in server announcements -2. **Initialization Responses**: Check `pmi` tags in server initialization responses -3. **Stateless Operations**: Handle compatibility at request time when no prior discovery is possible - -Servers can discover PMI support through: - -1. **Client Initialization Request**: Check `pmi` tags in client initialization request -2. **First direct client message in stateless operation**: Check `pmi` and `payment_interaction` tags on the first request event of the session +Clients discover server PMI support from public announcements, initialization responses, or request-time payment offers. Servers discover client PMI support from initialization requests or from `pmi` tags on the first direct client message in stateless operation. ##### Stateless operation @@ -320,11 +278,14 @@ Clients that prefer `explicit_gating` SHOULD also include `payment_interaction` ### Payment Flow -The complete payment flow for a capability with pricing information follows these steps: +The payment flow for a priced capability depends on the effective `payment_interaction` for the session: + +- `transparent` uses CEP-8 payment notifications and is the default compatibility lifecycle. +- `explicit_gating` returns payment requirements as JSON-RPC errors and is intended for clients, applications, or LLM agents that need payment gates to be visible as invocation outcomes. > Note: Pricing tags are a reference/discovery surface. Servers MAY decide at request time that an invocation does not require an interactive payment step (for example, a prepaid balance covers the price) and proceed directly to fulfill the request. -#### 1. Capability Request +#### Capability request The client sends a capability request to the server: @@ -348,7 +309,13 @@ The client sends a capability request to the server: } ``` -#### 2. Payment Required Notification +#### Transparent lifecycle + +In `transparent` mode, payment handling is treated as transport/client middleware behavior. The client sends an invocation, the server transport emits payment notifications when payment is required, and after payment is verified the server transport MAY continue fulfilling the original invocation. + +This is the default CEP-8 behavior and remains useful for clients with automatic payment handlers, embedded wallets, prepaid policies, or user interfaces that do not want payment requirements surfaced as application-level invocation errors. + +##### Payment required notification If the capability requires payment, the server responds with a `notifications/payment_required` notification containing payment details: @@ -376,7 +343,7 @@ If the capability requires payment, the server responds with a `notifications/pa } ``` -#### 3. Payment Processing +##### Payment processing and completion The client processes the payment and the server verifies it. When the client receives a payment request notification, it matches the PMI to determine if it supports the specified payment method. If compatible, the client processes the payment using the appropriate method for that PMI. The server verifies the payment according to the PMI implementation. @@ -384,81 +351,120 @@ If the client included one or more `pmi` tags in the original request, the serve If the client did not advertise any PMIs (for example, in a purely stateless request), the server MAY send multiple `notifications/payment_required` notifications (for example, one per supported PMI). Clients MAY ignore any or all payment requests. -#### 3a. Interaction behavior +Once payment is verified, the server processes the capability request and responds with the normal MCP result. The server SHOULD notify the client that payment has been accepted and MAY notify the client when an attempted payment is rejected. + +#### Explicit gating lifecycle -Server and client behavior depends on the negotiated `payment_interaction` semantic for the session. +In `explicit_gating` mode, payment is treated as an invocation gate. When payment is required, the server transport returns a JSON-RPC error response to the invocation instead of emitting `notifications/payment_required`. -##### `transparent` behavior +The server transport MUST NOT forward the payment-gated invocation to the underlying MCP handler unless a matching paid execution authorization is already available. -In `transparent` mode, payment handling is treated as transport/client middleware behavior. +The `explicit_gating` lifecycle is: -- After payment is verified, the server MAY continue fulfilling the original invocation. -- The payment flow may remain invisible to higher-level agent logic. -- This is the default CEP-8 behavior. +1. The client negotiates `payment_interaction=explicit_gating` on the first direct message of the session. +2. The client invokes a priced capability. +3. The server transport evaluates the request before forwarding it to the underlying MCP handler. +4. If payment is required and no matching paid execution authorization exists, the server transport returns a JSON-RPC `Payment Required` error response. +5. The error response contains one or more payment options and instructions for retrying the same invocation after payment. +6. A user, application, or agent decides whether to satisfy one payment option. +7. If payment is attempted and verification is still incomplete, repeated matching invocations SHOULD receive a JSON-RPC `Payment Pending` error response. +8. If payment succeeds, the server transport records paid execution authorization for the client and canonical invocation identity. +9. To obtain the capability result, the client SHOULD send a subsequent invocation with the same `method` and `params`. +10. When a matching authorization is available, the server transport consumes one authorization atomically, forwards the invocation to the underlying MCP handler, and returns the capability result. -##### `explicit_gating` behavior +##### Payment required error -In `explicit_gating` mode, payment is treated as an invocation gate for the session. +When payment is required in `explicit_gating` mode, the server transport MUST return a JSON-RPC error response with code `-32042` and message `Payment Required`. -- When the server determines that payment is required, it MUST emit `notifications/payment_required` and MUST NOT fulfill the triggering invocation. -- The original invocation becomes payment-gated and remains unfulfilled unless the client later sends a subsequent invocation. -- Clients SHOULD surface this state to higher-level application, user, or agent logic as a structured error-like result. -- When a JSON-RPC error object is used by the client runtime, clients SHOULD use code `-32042` with message `Payment Required`. -- The surfaced error data SHOULD include the payment details from `notifications/payment_required`, including `amount`, `pmi`, `pay_req`, optional `description`, optional `ttl`, and optional `_meta`. -- If payment is later verified, the server MUST NOT treat that verification as automatic completion of the original invocation. -- Instead, the server SHOULD create redeemable paid authorization state bound to the verified payment scope. -- The server MAY emit `notifications/payment_accepted` to acknowledge that payment was accepted, but in `explicit_gating` mode that notification does not fulfill the original invocation. -- To obtain the capability result, the client MUST send a subsequent invocation. -- On that subsequent invocation, the server SHOULD check for matching paid authorization, consume it atomically if valid, and then fulfill the invocation. +Example: -This behavior is intended for clients that want payment to be surfaced explicitly to higher-level application or agent logic while preserving CEP-8's notification-based transport primitive. +```json +{ + "jsonrpc": "2.0", + "id": 2, + "error": { + "code": -32042, + "message": "Payment Required", + "data": { + "instructions": "Pay one of the offered payment options, then repeat the same request with exactly the same method and params.", + "payment_options": [ + { + "amount": 100, + "pmi": "bitcoin-lightning-bolt11", + "pay_req": "lnbc...", + "description": "Payment for tool execution", + "ttl": 600, + "_meta": { + "note": "Optional PMI-specific metadata" + } + } + ] + } + } +} +``` -##### `explicit_gating` lifecycle +`error.data.payment_options` MUST contain one or more payment option objects. Each option contains the same payment request fields defined for `notifications/payment_required`: `amount`, `pmi`, `pay_req`, optional `description`, optional `ttl`, and optional `_meta`. -The `explicit_gating` lifecycle is: +`error.data.instructions` SHOULD be included and SHOULD clearly tell the client or agent to pay one option and retry the same request with the same `method` and `params`. -1. The client negotiates `payment_interaction=explicit_gating` on the first direct message of the session. -2. The client invokes a priced capability. -3. The server evaluates the request, determines that payment is required, and emits `notifications/payment_required` correlated to the request event. -4. The server does not send the capability result for that invocation. -5. The client maps the payment requirement into a structured surfaced failure or gated result for higher-level logic. -6. A user, application, or agent decides whether to satisfy the payment request. -7. If payment is attempted, the server verifies it according to the selected PMI. -8. If verification succeeds, the server may emit `notifications/payment_accepted` and records redeemable paid authorization state. -9. The original invocation remains unfulfilled and is not resumed automatically. -10. The client sends a subsequent invocation for the paid capability. -11. The server validates that the subsequent invocation matches the recorded authorization scope. -12. The server consumes that authorization and returns the capability result on the subsequent invocation. +##### Payment pending error -##### Client surfaced error shape +If a payment attempt is known to be in progress for the same client and canonical invocation identity, but paid execution authorization is not yet available, the server transport SHOULD return a JSON-RPC error response with code `-32043` and message `Payment Pending`. -In `explicit_gating` mode, clients SHOULD expose the payment gate as structured invocation failure rather than as an opaque string. +Example: -Conceptually, the surfaced object is: +```json +{ + "jsonrpc": "2.0", + "id": 2, + "error": { + "code": -32043, + "message": "Payment Pending", + "data": { + "instructions": "Payment is still being processed. Retry the same request later with exactly the same method and params.", + "retry_after": 5 + } + } +} +``` + +While payment verification remains pending, repeated matching invocations SHOULD continue to receive `Payment Pending`. If the server transport cannot establish that a payment is pending, if pending state is lost, or if payment verification fails, it MAY return a fresh `Payment Required` error. + +##### Explicit gating authorization identity + +In `explicit_gating` mode, successful payment verification authorizes future execution for a canonical invocation identity. + +The server transport derives that identity from: + +- the requesting client's pubkey +- the SHA-256 digest of the RFC 8785 JSON Canonicalization Scheme (JCS) serialization of a JSON object containing exactly the inner MCP request `method` and `params` + +The JSON-RPC `id`, outer Nostr event fields, event tags, signatures, timestamps, and other transport envelope fields MUST NOT be included in the canonicalized object. + +Example canonicalization input: ```json { - "code": -32042, - "message": "Payment Required", - "data": { - "amount": 100, - "pmi": "bitcoin-lightning-bolt11", - "pay_req": "lnbc...", - "description": "Payment for tool execution", - "ttl": 600, - "retry_after_payment": true, - "_meta": { - "note": "Optional PMI-specific metadata" + "method": "tools/call", + "params": { + "name": "get_weather", + "arguments": { + "location": "New York" } } } ``` -This error shape is client-side presentation guidance for request/response runtimes. The CEP-8 wire primitive remains `notifications/payment_required`. +Each successful payment SHOULD authorize one future execution for the matching client pubkey and canonical invocation identity unless server policy explicitly grants a different number of executions. Servers SHOULD consume one authorization atomically before forwarding the matching invocation to the underlying MCP handler and MUST prevent concurrent double-consumption. + +Servers MAY expire or evict unpaid payment options, pending payment state, and unused paid execution authorizations according to local policy. If no valid paid authorization exists when a matching invocation arrives, the server transport handles the invocation as unpaid and MAY return `Payment Required`. + +#### Transparent acknowledgments and result -#### 4. Payment Accepted Notification +##### Payment accepted notification -Once payment is verified, the server SHOULD notify the client that payment has been accepted. +In the transparent lifecycle, once payment is verified, the server SHOULD notify the client that payment has been accepted: ```json { @@ -485,9 +491,9 @@ The optional `_meta` field is the extension point for payment acceptance details For bearer-asset direct payments, the server MAY include a [`change` tag](#change-tag-optional) on this event to return any overpayment remainder. -#### 4b. Payment Rejected Notification +##### Payment rejected notification -If the server cannot accept payment for a request (for example, an invalid or insufficient `direct_payment` payload), it MAY notify the client that payment was rejected. +In the transparent lifecycle, if the server cannot accept payment for a request (for example, an invalid or insufficient `direct_payment` payload), it MAY notify the client that payment was rejected: ```json { @@ -507,9 +513,9 @@ If the server cannot accept payment for a request (for example, an invalid or in } ``` -#### 5. Capability Access +##### Capability access -Once payment is verified, the server processes the capability request and responds with the result: +In the transparent lifecycle, the paid capability result uses the normal MCP response shape: ```json { @@ -532,11 +538,13 @@ Once payment is verified, the server processes the capability request and respon } ``` -In `explicit_gating` mode, this capability result is typically returned on the subsequent invocation that consumes the satisfied payment authorization, not on the original invocation that triggered `notifications/payment_required`. +In `explicit_gating` mode, capability results are returned on subsequent invocations that consume paid execution authorization, not on the invocation that returned `Payment Required`. -### Payment Request Notification Fields +### Payment Message Fields -The `notifications/payment_required` notification `params` object contains: +#### Payment Required fields + +In the transparent lifecycle, the `notifications/payment_required` notification `params` object contains: - `amount` (required): Numeric payment amount - `pay_req` (required): Payment request data string @@ -557,9 +565,9 @@ The `notifications/payment_required` notification `params` object contains: - Some PMIs embed an expiry/TTL in the payment request itself (for example, a Lightning BOLT11 invoice). The optional `ttl` field provides a uniform expiry signal for clients, especially when the PMI payload does not embed one or when clients want a quick hint without parsing `pay_req`. - `_meta` is a general-purpose container for extra fields. Implementations SHOULD ignore unknown `_meta` fields. This CEP does not standardize `_meta` contents. -### Payment Accepted Notification Fields +#### Payment Accepted fields -The `notifications/payment_accepted` notification `params` object contains: +In the transparent lifecycle, the `notifications/payment_accepted` notification `params` object contains: - `amount` (required): Numeric payment amount accepted by the server - `pmi` (required): Payment Method Identifier string @@ -567,11 +575,9 @@ The `notifications/payment_accepted` notification `params` object contains: If the server returns change for a bearer-asset direct payment, it SHOULD include a [`change` tag](#change-tag-optional) on the event. In that case, `amount` is the final amount charged for the request. -In `explicit_gating` mode, `notifications/payment_accepted` acknowledges successful payment verification and optional gate creation. It does not mean the original invocation has been fulfilled. - -### Payment Rejected Notification Fields +#### Payment Rejected fields -The `notifications/payment_rejected` notification `params` object contains: +In the transparent lifecycle, the `notifications/payment_rejected` notification `params` object contains: - `pmi` (required): Payment Method Identifier string associated with the attempted payment. - `amount` (optional): Numeric amount hint. For example, if a bearer-asset `direct_payment` was insufficient, servers MAY set this to the required amount. @@ -580,7 +586,7 @@ The `notifications/payment_rejected` notification `params` object contains: ##### Notes - `notifications/payment_rejected` is a generic negative acknowledgment for CEP-8 payment attempts. -- For non-bearer PMIs (for example, invoice-based rails), servers will typically use [`notifications/payment_required`](#payment-request-notification-fields) to request the exact amount, and `payment_rejected` MAY be used when an attempted payment cannot be accepted or verified. +- For non-bearer PMIs (for example, invoice-based rails), servers will typically use [`notifications/payment_required`](#payment-required-fields) to request the exact amount, and `payment_rejected` MAY be used when an attempted payment cannot be accepted or verified. - Only bearer-asset direct payments can return remainder value via the [`change` tag](#change-tag-optional). ### Optional direct payment (bearer-asset optimization) @@ -607,8 +613,8 @@ Example (conceptual): `bitcoin-cashu-v4-direct`. If a server receives a request with a `direct_payment` tag: - If the server supports the specified PMI and validates the provided payload, it MAY proceed directly to fulfill the request. -- If the provided payload is valid and its value exceeds the final price, the server MAY return the remainder as change by including a [`change` tag](#change-tag-optional) on [`notifications/payment_accepted`](#payment-accepted-notification-fields). -- If the server cannot accept the provided payload (for example, invalid or insufficient), it MAY emit [`notifications/payment_rejected`](#payment-rejected-notification-fields) and/or fall back to the normal CEP-8 flow (emit `notifications/payment_required`), implementation-defined. +- If the provided payload is valid and its value exceeds the final price, the server MAY return the remainder as change by including a [`change` tag](#change-tag-optional) on [`notifications/payment_accepted`](#payment-accepted-fields). +- If the server cannot accept the provided payload (for example, invalid or insufficient), it MAY emit [`notifications/payment_rejected`](#payment-rejected-fields) and/or fall back to the normal CEP-8 flow (emit `notifications/payment_required`), implementation-defined. ##### Multiple `direct_payment` tags @@ -620,14 +626,10 @@ For bearer-asset PMIs, servers SHOULD treat `notifications/payment_rejected` as ### Correlation and Idempotency -Payment-related notifications MUST include an `e` tag referencing the original request event id. +Payment-related notifications in the transparent lifecycle MUST include an `e` tag referencing the original request event id. Clients MAY retry publishing the same request event (same event id) to achieve idempotent semantics. Servers SHOULD treat duplicate request events with the same id as retries and MUST NOT charge more than once for the same request. -In `explicit_gating` mode, successful payment verification SHOULD create a redeemable payment authorization whose scope is defined by server policy. At minimum, servers SHOULD bind that authorization to the requesting client and the priced capability. Servers SHOULD also bind it to a quote, challenge, or equivalent server-side payment record. Servers MAY bind more narrowly, for example to a quoted amount, request shape, capability arguments, payment rail, or expiry. - -Unless server policy explicitly defines otherwise, this authorization SHOULD be single-use. Servers SHOULD consume that authorization atomically when fulfilling the subsequent paid invocation, MUST prevent concurrent double-consumption, and MUST NOT use payment acceptance alone as fulfillment of the original invocation. - ## Backward Compatibility This CEP introduces no breaking changes to the existing protocol: @@ -636,6 +638,8 @@ This CEP introduces no breaking changes to the existing protocol: - **Existing clients** continue to work with existing servers - **New pricing** is additive - capabilities can be free or paid - **Optional participation**: Both providers and clients can choose to participate in pricing +- **Transparent compatibility**: If `explicit_gating` is not negotiated, implementations use the transparent notification lifecycle +- **Explicit gating is opt-in**: Clients that do not request `explicit_gating` are not required to handle `Payment Required` or `Payment Pending` invocation errors ## Reference Implementation @@ -646,4 +650,5 @@ A reference implementation of this CEP is available in the [ContextVM TypeScript - [CEP-4: Encryption Support](/spec/ceps/cep-4) - [CEP-6: Public Server Announcements](/spec/ceps/cep-6) - [CEP-35: Stateless Session Discovery and Capability Learning](/spec/ceps/informational/cep-35) +- [RFC 8785: JSON Canonicalization Scheme](https://www.rfc-editor.org/rfc/rfc8785) - [W3C Payment Method Identifiers](https://www.w3.org/TR/payment-method-id/) From 5707f431da8ad2f6d37ee66779565b673084481c Mon Sep 17 00:00:00 2001 From: ContextVM Date: Sat, 6 Jun 2026 12:59:38 +0200 Subject: [PATCH 3/3] docs(cep-8): clarify payment interaction request semantics and add effective mode disclosure Updates CEP-8 payment interaction negotiation to clarify that clients request rather than advertise preferred semantics. Adds detailed "Effective mode disclosure and lifecycle negotiation" section specifying that servers must confirm the effective mode on their first direct response when clients request explicit_gating. Introduces JSON-RPC error format for rejecting unsupported payment interaction modes. Adds "Correlation, Authorization Identity, and Idempotency" section distinguishing transparent lifecycle (correlated by Nostr event id) from explicit gating lifecycle (correlated by canonical invocation identity derived from MCP method and params). Clarifies authorization claim/consumption semantics and prevents double-consumption. --- src/content/docs/spec/ceps/cep-8.md | 95 +++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 24 deletions(-) diff --git a/src/content/docs/spec/ceps/cep-8.md b/src/content/docs/spec/ceps/cep-8.md index a3e5792..ec7f903 100644 --- a/src/content/docs/spec/ceps/cep-8.md +++ b/src/content/docs/spec/ceps/cep-8.md @@ -93,7 +93,7 @@ The `direct_payment` tag is an optional optimization for bearer-asset payment me #### `payment_interaction` Tag (optional) -The `payment_interaction` tag is an optional negotiation tag used by clients to advertise the preferred payment interaction semantic for the current session. +The `payment_interaction` tag is an optional negotiation tag used by clients to request the payment interaction semantic for the current session. ```json ["payment_interaction", ""] @@ -107,10 +107,10 @@ Where `` is one of: ##### Notes - `payment_interaction` is a negotiation tag, not a pricing tag. -- Clients SHOULD send at most one `payment_interaction` tag on the first direct client-to-server message of a session. +- Clients SHOULD send at most one `payment_interaction` tag on the first direct client-to-server message of a session. The tag expresses the requested effective lifecycle for the session, not a list of all lifecycles the client supports. Clients MUST NOT rely on the presence of multiple `payment_interaction` tags to express an ordered preference list. - When present on that first direct message, it participates in the session discovery baseline described by [CEP-35: Stateless Session Discovery and Capability Learning](/spec/ceps/informational/cep-35). - If omitted, `transparent` is the default. -- Servers MAY advertise supported interaction semantics in initialization responses or public announcements using the same tag format. +- Servers MAY advertise supported interaction semantics in initialization responses or public announcements using the same tag format. Because `transparent` is the default compatibility baseline, a server that supports `explicit_gating` MAY advertise support by including a `payment_interaction=explicit_gating` tag in its public announcement, initialization response, or first direct response. Such advertisement means explicit gating is available as an opt-in mode; it does not make explicit gating the effective lifecycle unless the client requests it and the server accepts it for the session. #### `change` Tag (optional) @@ -206,6 +206,18 @@ Recommended PMIs and naming conventions are documented in the informational comp PMI discovery allows clients and servers to determine payment-method compatibility before, or during, paid capability use. +#### Discovery methods + +Clients discover server PMI support from public announcements, initialization responses, or request-time payment offers. Servers discover client PMI support from initialization requests or from `pmi` tags on the first direct client message in stateless operation. + +#### Stateless operation + +In stateless operation (no prior initialization), clients that want to use paid capabilities SHOULD include one or more `pmi` tags in the request event so the server can select a compatible payment method. + +When sent on the first direct client-to-server message of a session, these `pmi` tags participate in the session discovery baseline described by [CEP-35: Stateless Session Discovery and Capability Learning](/spec/ceps/informational/cep-35). When sent on later requests, they are interpreted in the context of those requests unless another CEP explicitly defines stronger session-update semantics. + +Clients that prefer `explicit_gating` SHOULD also include `payment_interaction` on that first direct request so the server can apply the session's negotiated payment interaction behavior from the start. + ### Payment Interaction Negotiation CEP-8 defines an optional session-level negotiation surface for how payment-gated invocations are exposed by the transport. @@ -216,16 +228,49 @@ Payment interaction is a transport concern. A server transport MAY enforce payme This CEP defines `payment_interaction` as a session-level negotiation tag. -- When a client includes `payment_interaction` on the first direct client-to-server message of a session, that value expresses the client's preferred payment interaction semantic for the session. +- When a client includes `payment_interaction` on the first direct client-to-server message of a session, that value expresses the client's requested payment interaction semantic for the session. - After the first-message exchange, implementations SHOULD omit repeated `payment_interaction` tags unless a future CEP defines stronger update semantics. - `transparent` is the default and remains the compatibility baseline. -- `explicit_gating` applies only when requested by the client and supported by the server for the session. If no compatible explicit preference is negotiated, implementations MUST use `transparent`. +- `explicit_gating` becomes the effective lifecycle for the session only when it is requested by the client and accepted by the server. If the client does not request `explicit_gating`, or if the server does not accept it, the effective lifecycle is `transparent`. - `transparent` means payment may be handled through payment notifications without surfacing payment as the final invocation outcome. - `explicit_gating` means payment is surfaced as a JSON-RPC error response to the invocation. The paid result is returned only if the client later sends a matching invocation after payment authorization is available. -Servers MAY advertise support for `explicit_gating` in discovery, initialization responses, first direct responses, or public announcements. In stateless operation, `payment_interaction` follows the first-message exchange and capability-learning rules described by [CEP-35: Stateless Session Discovery and Capability Learning](/spec/ceps/informational/cep-35). Clients that want `explicit_gating` in stateless operation SHOULD request it on the first direct invocation event of the session. +In stateless operation, `payment_interaction` follows the first-message exchange and capability-learning rules described by [CEP-35: Stateless Session Discovery and Capability Learning](/spec/ceps/informational/cep-35). Clients that want `explicit_gating` in stateless operation SHOULD request it on the first direct invocation event of the session. + +#### Effective mode disclosure and lifecycle negotiation + +The effective `payment_interaction` for a session is established during the first direct message exchange. To avoid clients waiting for the wrong lifecycle, the effective mode MUST be observable on the first direct server-to-client response when the client requested a non-default mode. + +- When a client includes `payment_interaction=explicit_gating` on the first direct client-to-server message of a session, the server MUST indicate the effective mode on its first direct server-to-client message in the same session. The server indicates acceptance by including a `payment_interaction=explicit_gating` tag on that first direct response. +- When a client includes `payment_interaction=explicit_gating` and the server does not support or does not accept it for the session, the server MUST NOT silently fall back to transparent payment behavior for the priced invocation. The server SHOULD either: + - return a JSON-RPC error indicating that the requested `payment_interaction` is unsupported or unavailable for the session, or + - indicate `payment_interaction=transparent` on the first direct response and use the transparent lifecycle for the session. +- If a client includes `payment_interaction=transparent` explicitly, or omits the tag, the effective mode is `transparent` for that session. The server MAY still echo `payment_interaction=transparent` on its first direct response; if it does not, implementations MUST continue to behave as if the effective mode is `transparent`. +- Clients SHOULD treat the observed effective `payment_interaction` on the first direct response as authoritative for the session. A client that requested `explicit_gating` and receives an effective `transparent` response, no effective-mode disclosure, or transparent `notifications/payment_required` messages SHOULD treat negotiation of `explicit_gating` as failed for that session and MAY abort the session or fall back to the observed lifecycle according to local policy. +- A client that required `explicit_gating` because payment decisions must be visible to the application or agent SHOULD NOT automatically satisfy transparent `notifications/payment_required` messages received in a session where `explicit_gating` was not accepted. + +##### JSON-RPC error when `explicit_gating` is unsupported + +When a server rejects a `payment_interaction=explicit_gating` request, it SHOULD return a JSON-RPC error with code `-32602 Invalid params` and a `data` object identifying the requested mode and, when useful, the modes the server supports. Example: + +```json +{ + "jsonrpc": "2.0", + "id": 2, + "error": { + "code": -32602, + "message": "Unsupported payment_interaction", + "data": { + "requested": "explicit_gating", + "supported": ["transparent"] + } + } +} +``` + +Returning this error is sufficient to satisfy the server's effective-mode-disclosure obligation for the first direct response; it is not a payment error. -#### PMI and interaction advertisement +#### PMI advertisement Servers advertise supported PMIs using the `pmi` tag in initialization responses or public announcements: @@ -264,18 +309,6 @@ Clients advertise supported PMIs in initialization requests. Clients that prefer } ``` -#### Discovery methods - -Clients discover server PMI support from public announcements, initialization responses, or request-time payment offers. Servers discover client PMI support from initialization requests or from `pmi` tags on the first direct client message in stateless operation. - -##### Stateless operation - -In stateless operation (no prior initialization), clients that want to use paid capabilities SHOULD include one or more `pmi` tags in the request event so the server can select a compatible payment method. - -When sent on the first direct client-to-server message of a session, these `pmi` tags participate in the session discovery baseline described by [CEP-35: Stateless Session Discovery and Capability Learning](/spec/ceps/informational/cep-35). When sent on later requests, they are interpreted in the context of those requests unless another CEP explicitly defines stronger session-update semantics. - -Clients that prefer `explicit_gating` SHOULD also include `payment_interaction` on that first direct request so the server can apply the session's negotiated payment interaction behavior from the start. - ### Payment Flow The payment flow for a priced capability depends on the effective `payment_interaction` for the session: @@ -361,7 +394,7 @@ The server transport MUST NOT forward the payment-gated invocation to the underl The `explicit_gating` lifecycle is: -1. The client negotiates `payment_interaction=explicit_gating` on the first direct message of the session. +1. The client requests `payment_interaction=explicit_gating` on the first direct message of the session, and the server accepts it by including `payment_interaction=explicit_gating` on its first direct response (see [Effective mode disclosure and lifecycle negotiation](#effective-mode-disclosure-and-lifecycle-negotiation)). 2. The client invokes a priced capability. 3. The server transport evaluates the request before forwarding it to the underlying MCP handler. 4. If payment is required and no matching paid execution authorization exists, the server transport returns a JSON-RPC `Payment Required` error response. @@ -456,7 +489,7 @@ Example canonicalization input: } ``` -Each successful payment SHOULD authorize one future execution for the matching client pubkey and canonical invocation identity unless server policy explicitly grants a different number of executions. Servers SHOULD consume one authorization atomically before forwarding the matching invocation to the underlying MCP handler and MUST prevent concurrent double-consumption. +Each successful payment SHOULD authorize one future execution for the matching client pubkey and canonical invocation identity unless server policy explicitly grants a different number of executions. Servers MUST atomically claim or consume one matching authorization before forwarding the invocation to the underlying MCP handler, MUST prevent concurrent double-consumption, and MUST NOT forward a priced invocation to the underlying MCP handler unless a matching paid authorization has been claimed. Server policy determines whether a failed or interrupted execution restores, expires, or consumes the claimed authorization. Servers MAY expire or evict unpaid payment options, pending payment state, and unused paid execution authorizations according to local policy. If no valid paid authorization exists when a matching invocation arrives, the server transport handles the invocation as unpaid and MAY return `Payment Required`. @@ -624,11 +657,25 @@ Clients SHOULD include at most one `direct_payment` tag. If multiple `direct_pay For bearer-asset PMIs, servers SHOULD treat `notifications/payment_rejected` as meaning the bearer asset was not consumed/redeemed. -### Correlation and Idempotency +### Correlation, Authorization Identity, and Idempotency + +Correlation and idempotency depend on the effective `payment_interaction` lifecycle for the session. The two lifecycles use different correlation identities on purpose: the transparent lifecycle correlates by the outer Nostr request event id, while the explicit gating lifecycle correlates by a canonical invocation identity derived from the inner MCP `method` and `params`. + +#### Transparent lifecycle + +In the transparent lifecycle, payment-related notifications MUST include an `e` tag referencing the original request event id. The "same request" for transparent idempotency means the same outer Nostr request event (same event id). + +Clients MAY retry publishing the same request event, with the same event id, to achieve idempotent transparent request semantics. Servers SHOULD treat duplicate request events with the same id as retries and MUST NOT charge more than once for the same transparent request event. + +#### Explicit gating lifecycle + +In the explicit gating lifecycle, payment authorization and retry matching are based on the explicit-gating authorization identity, not the outer request event id. For explicit gating, "the same invocation" means the same requesting client pubkey and the same canonical invocation identity, derived from the SHA-256 digest of the RFC 8785 JSON Canonicalization Scheme (JCS) serialization of a JSON object containing exactly the inner MCP request `method` and `params`. + +The JSON-RPC `id`, outer Nostr event id, timestamps, signatures, and event tags MUST NOT affect this identity. A retry MAY therefore use a different JSON-RPC `id` or a different outer event id and still match a paid authorization if `method`, `params`, and client pubkey are unchanged. -Payment-related notifications in the transparent lifecycle MUST include an `e` tag referencing the original request event id. +Servers MUST atomically claim or consume one matching paid authorization before forwarding the invocation to the underlying MCP handler, MUST prevent concurrent double-consumption, and MUST NOT forward a priced invocation to the underlying MCP handler unless a matching paid authorization has been claimed. Server policy determines whether a failed or interrupted execution restores, expires, or consumes the claimed authorization. -Clients MAY retry publishing the same request event (same event id) to achieve idempotent semantics. Servers SHOULD treat duplicate request events with the same id as retries and MUST NOT charge more than once for the same request. +A paid authorization grants permission for execution; it does not by itself guarantee replay of the completed result. If the invocation is executed and the resulting response is not received by the client because of transport loss, relay behavior, client disconnect, or another delivery failure, this CEP does not require the server to replay the completed result. A later matching invocation for which no unused paid authorization remains MAY be treated as unpaid and MAY receive a fresh `Payment Required` error, unless server-specific policy provides another remedy. ## Backward Compatibility