Skip to content

Commit bcd1060

Browse files
vvillait88claude
andcommitted
docs: update README + signed_ucp_merchant example for new spec-compliant shape
README and the canonical signed_ucp_merchant example now show services / payment_handlers as dicts keyed by reverse-DNS name (matches the build_ucp_profile output and the live Pura Vida reference profile). README section on profile-body signing reframed: not "UCP §6 trust-mode requires signing" (it doesn't — Pura Vida ships unsigned in production); instead "vendor extension for trust-mode verifiers that opt into auditable profiles". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent fa0bc9a commit bcd1060

2 files changed

Lines changed: 56 additions & 11 deletions

File tree

README.md

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,9 @@ headers = build_payment_headers(BuildPaymentHeadersInput(
181181

182182
```python
183183
from agentscore_commerce.identity import (
184-
UCPService,
184+
UCPServiceBinding,
185185
UCPSigningKey,
186-
UCPPaymentHandler,
186+
UCPPaymentHandlerBinding,
187187
A2AAgentCardCapabilities,
188188
build_a2a_agent_card,
189189
build_ucp_profile,
@@ -193,16 +193,41 @@ from agentscore_commerce.identity import (
193193
card = build_a2a_agent_card(name="My Service", url=base_url, capabilities=A2AAgentCardCapabilities(...), data=assess_result)
194194

195195
# Google Universal Commerce Protocol. Publish at /.well-known/ucp.
196+
# Output shape: {"ucp": {"version", "services", "capabilities",
197+
# "payment_handlers", "name?", "supported_versions?"}, "signing_keys": [...]}
198+
# — services / capabilities / payment_handlers are MAPS keyed by reverse-DNS
199+
# service / capability / handler name. Verified against the live Pura Vida
200+
# reference at puravidabracelets.com/.well-known/ucp.
196201
profile = build_ucp_profile(
197202
name="My Service",
198-
services=[UCPService(type="rest", url=base_url)],
199-
payment_handlers=[UCPPaymentHandler(name="tempo", config={"recipient": TEMPO_ADDR})],
203+
services={
204+
"dev.ucp.shopping": [
205+
UCPServiceBinding(
206+
version="2026-04-08",
207+
spec="https://ucp.dev/2026-04-08/specification/overview",
208+
transport="mcp",
209+
endpoint=f"{base_url}/api/ucp/mcp",
210+
schema="https://ucp.dev/services/shopping/openrpc.json",
211+
),
212+
],
213+
},
214+
payment_handlers={
215+
"sh.agentscore.payment.tempo": [
216+
UCPPaymentHandlerBinding(
217+
id="tempo",
218+
version="2026-04-08",
219+
spec="https://agentscore.sh/specification/payment-handlers/tempo",
220+
schema="https://agentscore.sh/schemas/payment-handlers/tempo.json",
221+
config={"recipient": TEMPO_ADDR},
222+
),
223+
],
224+
},
200225
signing_keys=[UCPSigningKey(kid="me", kty="EC", alg="ES256")],
201226
data=assess_result,
202227
)
203228
```
204229

205-
UCP §6 trust-mode requires profiles to carry a JWS signature backed by a JWKS at `/.well-known/jwks.json`. Sign + verify via the optional `joserfc` extra (tested against joserfc v1.x; pin `joserfc>=1.0.0,<2`):
230+
UCP §6 doesn't mandate profile-body JWS signing — Pura Vida and other Shopify-backed UCP merchants ship unsigned. AgentScore's `agentscore-profile+jws` is a vendor extension for trust-mode verifiers (Visa AP2 pilots, regulated-commerce verifiers) that opt into auditable profiles. Sign + verify via the optional `joserfc` extra (tested against joserfc v1.x; pin `joserfc>=1.0.0,<2`):
206231

207232
```bash
208233
pip install agentscore-commerce[ucp]
@@ -222,8 +247,8 @@ from agentscore_commerce.identity import (
222247
key = generate_ucp_signing_key(kid="merchant-2026-05")
223248
profile = build_ucp_profile(
224249
name="My Service",
225-
services=[...],
226-
payment_handlers=[...],
250+
services={...},
251+
payment_handlers={...},
227252
signing_keys=[UCPSigningKey.from_jwk(key.public_jwk)],
228253
)
229254
signed = sign_ucp_profile(profile.to_dict(), signing_key=key.private_key, kid=key.public_jwk["kid"], alg="EdDSA")

examples/signed_ucp_merchant.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
from fastapi.responses import JSONResponse
3535

3636
from agentscore_commerce.identity import (
37-
UCPPaymentHandler,
38-
UCPService,
37+
UCPPaymentHandlerBinding,
38+
UCPServiceBinding,
3939
UCPSigningKey,
4040
UCPVerificationError,
4141
build_jwks_response,
@@ -105,8 +105,28 @@ async def well_known_ucp() -> JSONResponse:
105105
key = await load_signing_key()
106106
profile = build_ucp_profile(
107107
name="My Agent Service",
108-
services=[UCPService(type="rest", url="https://agents.example.com")],
109-
payment_handlers=[UCPPaymentHandler(name="tempo", config={"recipient": "0xfeedface"})],
108+
services={
109+
"dev.ucp.shopping": [
110+
UCPServiceBinding(
111+
version="2026-04-08",
112+
spec="https://ucp.dev/2026-04-08/specification/overview",
113+
transport="mcp",
114+
endpoint="https://agents.example.com/api/ucp/mcp",
115+
schema="https://ucp.dev/services/shopping/openrpc.json",
116+
),
117+
],
118+
},
119+
payment_handlers={
120+
"sh.agentscore.payment.tempo": [
121+
UCPPaymentHandlerBinding(
122+
id="tempo",
123+
version="2026-04-08",
124+
spec="https://agentscore.sh/specification/payment-handlers/tempo",
125+
schema="https://agentscore.sh/schemas/payment-handlers/tempo.json",
126+
config={"recipient": "0xfeedface"},
127+
),
128+
],
129+
},
110130
signing_keys=[UCPSigningKey.from_jwk(key.public_jwk)],
111131
)
112132
signed = sign_ucp_profile(

0 commit comments

Comments
 (0)