Transport bindings: MCP and HTTP
How the envelope (envelope.md) travels over two transports. The bindings are semantically equivalent; a receiver MAY support both.
MCP binding
Capability and prices
A server declares support in initialize:
{ "capabilities": { "experimental": { "stipendex/payments": { "version": "0.1" } } } }
Tools carry a price hint in their _meta (level L0 — useful even with zero
payment code; _meta is MCP's extension point and survives SDK validation,
unlike custom annotation keys):
{
"name": "web_search",
"_meta": {
"stipendex/pricing": { "model": "per_call", "estimate": "0.005 USD" }
}
}
stipendex/pricing is a planning hint, not an offer. The only obligation is a signed
Quote.
Payment cycle
- The agent calls a tool. Without payment the server returns a JSON-RPC error:
{
"error": {
"code": -32402,
"message": "Payment required",
"data": { "quote": { "stipendex": "quote/0.1", "…": "…" } }
}
}
- The host intercepts
-32402(the LLM never sees the error) and hands the Quote to the wallet. - The wallet pays → Proof; the host retries the call:
{
"method": "tools/call",
"params": {
"name": "web_search",
"arguments": { "query": "…" },
"_meta": { "stipendex/payment": { "stipendex": "proof/0.1", "…": "…" } }
}
}
- The server verifies the Proof (itself or via a facilitator), executes, returns
the result; the Receipt rides in the response
_meta:
{ "result": { "content": [ … ], "_meta": { "stipendex/receipt": { "…": "…" } } } }
Until stipendex/payments is accepted into the MCP specification (SEP), all of the above
lives in experimental/_meta and requires no protocol changes.
HITL via elicitation
In interactive mode the wallet requests confirmation through standard elicitation
(MCP hosts already have the UI): text = description + total + remaining limits.
The answer is recorded in a Confirmation.
Hosts without Stipendex support
The wallet runs in proxy mode: the host's MCP client connects not to the server
directly but to the wallet's local proxy, which handles -32402 transparently.
Adoption is not blocked on host vendors (though native integration is better — see
roadmap).
HTTP binding
Compatible in spirit with x402 (deliberately close in mechanics):
- Unpaid request:
HTTP/1.1 402 Payment Required
Content-Type: application/json
{ "quote": { "stipendex": "quote/0.1", "…": "…" } }
- Retry with payment:
GET /api/search?q=…
Stipendex-Payment: <base64url(Proof JWS)>
- Response:
HTTP/1.1 200 OK
Stipendex-Receipt: <base64url(Receipt JWS)>
Discoverability
GET /.well-known/stipendex.json — the receiver descriptor: id, key, rails, trust
registry link, supported profiles. The format matches the registry descriptor
(trust-registry.md) — the self-hosted variant of the same
document.
Requirements common to both transports
- Quote/Proof/Receipt travel whole, never reassembled by the transport: signatures verify over canonical JSON (JCS, RFC 8785).
- Retrying a request with the same Proof is idempotent (see envelope).
- TLS is mandatory for the HTTP binding; for MCP — per the host transport.