diff --git a/docs/specification/identity-linking.md b/docs/specification/identity-linking.md index c78c7ce30..3e082c3b1 100644 --- a/docs/specification/identity-linking.md +++ b/docs/specification/identity-linking.md @@ -124,6 +124,7 @@ CheckoutSession | Update | `ucp:scopes:checkout_session` CheckoutSession | Delete | `ucp:scopes:checkout_session` CheckoutSession | Cancel | `ucp:scopes:checkout_session` CheckoutSession | Complete | `ucp:scopes:checkout_session` +Order | Get | `ucp:scopes:order` A scope covering a capability must grant access to all operations associated to the capability. For example, ucp:scopes:checkout\_session must grant all of: @@ -145,6 +146,7 @@ supposed to be hosted in /.well-known/oauth-authorization-server as per "revocation_endpoint": "https://merchant.example.com/oauth2/revoke", "scopes_supported": [ "ucp:scopes:checkout_session", + "ucp:scopes:order", ], "response_types_supported": [ "code" diff --git a/docs/specification/order-mcp.md b/docs/specification/order-mcp.md new file mode 100644 index 000000000..a6ca5e326 --- /dev/null +++ b/docs/specification/order-mcp.md @@ -0,0 +1,401 @@ + + +# Order Capability - MCP Binding + +This document specifies the Model Context Protocol (MCP) binding for the +[Order Capability](order.md). + +## Protocol Fundamentals + +### Discovery + +Businesses advertise MCP transport availability through their UCP profile at +`/.well-known/ucp`. + +```json +{ + "ucp": { + "version": "2026-01-11", + "services": { + "dev.ucp.shopping": [ + { + "version": "2026-01-11", + "spec": "https://ucp.dev/specification/overview", + "transport": "mcp", + "schema": "https://ucp.dev/services/shopping/mcp.openrpc.json", + "endpoint": "https://business.example.com/ucp/mcp" + } + ] + }, + "capabilities": { + "dev.ucp.shopping.order": [ + { + "version": "2026-01-11", + "spec": "https://ucp.dev/specification/order", + "schema": "https://ucp.dev/schemas/shopping/order.json" + } + ] + } + } +} +``` + +### Request Metadata + +MCP clients **MUST** include a `meta` object in every request containing +protocol metadata: + +```json +{ + "jsonrpc": "2.0", + "method": "tools/call", + "params": { + "name": "get_order", + "arguments": { + "meta": { + "ucp-agent": { + "profile": "https://platform.example/profiles/shopping-agent.json" + } + }, + "order": { ... } + } + } +} +``` + +The `meta["ucp-agent"]` field is **required** on all requests to enable +[capability negotiation](overview.md#negotiation-protocol). Platforms **MAY** include +additional metadata fields. + +## Tools + +UCP Capabilities map 1:1 to MCP Tools. + +### Identifier Pattern + +MCP tools separate resource identification from payload data: + +* **Requests:** For operations on existing orders (`get`), a top-level `id` parameter identifies the target + resource. The `order` object in the request payload **MUST NOT** contain + an `id` field. +* **Responses:** All responses include `order.id` as part of the full resource state. + +| Tool | Operation | Description | +| :------------------ | :------------------------------ | :--------------- | +| `get_order` | [Get Order](order.md#get-order) | Get an order. | + +### `get_order` + +Maps to the [Get Order](order.md#get-order) operation. + +#### Input Schema + +* `id` (String): **Required**. The ID of the order. + +#### Output Schema + +* [Order](order.md#get-order) object. + +#### Example + +=== "Request" + + ```json + { + "jsonrpc": "2.0", + "method": "get_order", + "params": { + "meta": { + "ucp-agent": { + "profile": "https://platform.example/profiles/v2026-01/shopping-agent.json" + } + }, + "id": "order_abc123" + } + } + ``` + +=== "Response" + + ```json + { + "jsonrpc": "2.0", + "result": { + "ucp": { + "version": "2026-01-11", + "capabilities": { + "dev.ucp.shopping.order": [ + {"version": "2026-01-11"} + ] + } + }, + "id": "order_abc123", + "checkout_id": "chk_9dc94c0f", + "permalink_url": "https://example-merchant.com/orders/ord_a18026fb", + "line_items": [ + { + "id": "li_1", + "item": { + "id": "sku_stickers", + "title": "UCP Demo Sticker Pack", + "price": 599, + "image_url": "https://example.com/images/stickers.jpg" + }, + "quantity": { + "total": 2, + "fulfilled": 2 + }, + "totals": [ + { + "type": "subtotal", + "amount": 1198 + }, + { + "type": "total", + "amount": 1198 + } + ], + "status": "fulfilled" + }, + { + "id": "li_2", + "item": { + "id": "sku_mug", + "title": "UCP Demo Mug", + "price": 1999, + "image_url": "https://example.com/images/mug.jpg" + }, + "quantity": { + "total": 1, + "fulfilled": 1 + }, + "totals": [ + { + "type": "subtotal", + "amount": 1999 + }, + { + "type": "total", + "amount": 1999 + } + ], + "status": "fulfilled" + } + ], + "fulfillment": { + "events": [ + { + "id": "evt_eb6dfcb4", + "occurred_at": "2026-01-27T21:32:22.849Z", + "type": "shipped", + "line_items": [ + { + "id": "li_1", + "quantity": 2 + }, + { + "id": "li_2", + "quantity": 1 + } + ], + "tracking_number": "1Z999AA10123456784", + "tracking_url": "https://example-carrier.com/track/1Z999AA10123456784", + "carrier": "Mock Express", + "description": "Package handed over to carrier." + }, + { + "id": "evt_60bbb7f5", + "occurred_at": "2026-01-27T21:33:50.961Z", + "type": "shipped", + "line_items": [ + { + "id": "li_1", + "quantity": 2 + }, + { + "id": "li_2", + "quantity": 1 + } + ], + "tracking_number": "1Z999AA10123456784", + "tracking_url": "https://example-carrier.com/track/1Z999AA10123456784", + "carrier": "Mock Express", + "description": "Package handed over to carrier." + }, + { + "id": "evt_ecc976fa", + "occurred_at": "2026-01-27T21:33:51.595Z", + "type": "shipped", + "line_items": [ + { + "id": "li_1", + "quantity": 2 + }, + { + "id": "li_2", + "quantity": 1 + } + ], + "tracking_number": "1Z999AA10123456784", + "tracking_url": "https://example-carrier.com/track/1Z999AA10123456784", + "carrier": "Mock Express", + "description": "Package handed over to carrier." + }, + { + "id": "evt_379b8331", + "occurred_at": "2026-01-27T21:33:57.254Z", + "type": "shipped", + "line_items": [ + { + "id": "li_1", + "quantity": 2 + }, + { + "id": "li_2", + "quantity": 1 + } + ], + "tracking_number": "1Z999AA10123456784", + "tracking_url": "https://example-carrier.com/track/1Z999AA10123456784", + "carrier": "Mock Express", + "description": "Package handed over to carrier." + }, + { + "id": "evt_073ec7d4", + "occurred_at": "2026-01-27T21:33:58.237Z", + "type": "shipped", + "line_items": [ + { + "id": "li_1", + "quantity": 2 + }, + { + "id": "li_2", + "quantity": 1 + } + ], + "tracking_number": "1Z999AA10123456784", + "tracking_url": "https://example-carrier.com/track/1Z999AA10123456784", + "carrier": "Mock Express", + "description": "Package handed over to carrier." + } + ] + }, + "adjustments": [], + "totals": [ + { + "type": "subtotal", + "amount": 3197 + }, + { + "type": "total", + "amount": 3197 + } + ], + "event_id": "evt_073ec7d4", + "created_time": "2026-01-27T21:33:58.237Z" + } + } + ``` + +## Error Handling + +Error responses follow JSON-RPC 2.0 format while using the UCP error structure +defined in the [Core Specification](overview.md). The UCP error object is +embedded in the JSON-RPC error's `data` field: + +```json +{ + "jsonrpc": "2.0", + "error": { + "code": -36538, + "message": "Unauthorized", + "data": { + "status": "error", + "errors": [ + { + "code": "require_identity_linking", + "message": "Additional authorization required", + "severity": "requires_authorization", + } + ] + } + } +} +``` + +## Conformance + +A conforming MCP transport implementation **MUST**: + +1. Implement JSON-RPC 2.0 protocol correctly. +2. Provide all core order tools defined in this specification. +3. Handle errors with UCP-specific error codes embedded in the JSON-RPC error + object. +4. Validate tool inputs against UCP schemas. +5. Support HTTP transport with streaming. + +## Implementation + +UCP operations are defined using [OpenRPC](https://open-rpc.org/) (JSON-RPC +schema format). The [MCP specification](https://modelcontextprotocol.io/) +requires all tool invocations to use a `tools/call` method with the operation +name and arguments wrapped in `params`. Implementers **MUST** apply this +transformation: + +| OpenRPC | MCP | +|:---------|:-------------------| +| `method` | `params.name` | +| `params` | `params.arguments` | + +**Param conventions:** + +* `meta` contains request metadata +* `id` identifies the target resource (path parameter equivalent) +* `order` contains the domain payload (body equivalent) + +**Example:** Given the `get_order` operation defined in OpenRPC: + +```json +{ + "method": "get_order", + "params": { + "meta": { + "ucp-agent": { "profile": "https://..." } + }, + "id": "order_abc123" + } +} +``` + +Implementers **MUST** expose this as an MCP `tools/call` endpoint: + +```json +{ + "jsonrpc": "2.0", + "method": "tools/call", + "params": { + "name": "get_order", + "arguments": { + "meta": { + "ucp-agent": { "profile": "https://..." } + }, + "id": "order_abc123" + } + } +} +``` diff --git a/docs/specification/order.md b/docs/specification/order.md index 2188eefb3..ccc691d3f 100644 --- a/docs/specification/order.md +++ b/docs/specification/order.md @@ -22,7 +22,7 @@ Orders represent confirmed transactions resulting from a successful checkout submission. It provides a complete record of what was purchased, how -it will be delivered, and what has happened since order placement. +it will be delivered, what has happened since order placement, and post-purchase operations. ### Key Concepts @@ -252,6 +252,92 @@ Examples: `refund`, `return`, `credit`, `price_adjustment`, `dispute`, } ``` +## Authorization flow + +While using the Order Capability, the business **MUST** check for the `ucp:scopes:order` scope. +When missing, the business **MUST** return the appropriate error code for the platform to obtain the missing scope. +The platform **SHOULD** leverage the Identity Linking Capability, and retry the operation after. + + ┌──────────────┐ + │ platform │◀─────────────────────────────────────────┐ + └──────┬───────┘ │ + │ │ + ▼ │ + ┌──────────────────┐ │ + │ order_capability │───────┐ │ + └────┬───────┬─────┴ │ │ + │ │ │ │ + │ │ │ error:require_identity_linking │ + │ │ ▼ │ + │ │ ┌──────────────────┐ │ + │ │ │ identity_linking ├───────────────────────┤ + │ │ └──────────────────┘ │ + │ │ │ + │ │ error:require_sign_in │ + │ ▼ │ + │ ┌─────────┐ │ + │ │ sign_in ├─────────────────────────────────────────┘ + │ └─────────┘ + │ + ▼ + ┌──────────┐ + │ complete │ + └──────────┘ + +## Operations + +The Order capability defines the following logical operations. + +| Operation | Description | +| :-------------------- | :--------------------------------------------------------------------------------- | +| **Get Order** | Retrieves the current state of an existing order. | + +### Get Order + +It provides the latest state of the order resource. It is up to the business to decide how +long to keep the order data. If the platform requires a longer retention period than what the business +provides, it **SHOULD** leverage the Order Event Webhook instead. + +{{ method_fields('get_order', 'rest.openapi.json', 'order') }} + +### Error Handling + +The `messages` array contains errors, warnings, and informational messages +about the order. Error messages include a `severity` field that +declares **who resolves the error**: + +| Severity | Meaning | Platform Action | +| :---------------------- | :-------------------------------------------- | :----------------------------------- | +| `recoverable` | Platform can fix via API | Resolve using the API | +| `requires_authorization` | Buyer authorization is required | Leverage Identity Linking Capability | + +* `requires_authorization` means the order may be available — but policy or +regulatory rules require buyer authorization. + +#### Error Example + +When status is `requires_escalation`, platforms **SHOULD** process +errors as a prioritized stack. The example below illustrates a checkout with +three error types: a recoverable error (invalid phone), a buyer input +requirement (delivery scheduling), and a review requirement (high-value order). +The latter two require handoff and serve as explicit signals to the platform. +Businesses **SHOULD** surface such messages as early as possible, and platforms +**SHOULD** prioritize resolving recoverable errors before initiating handoff. + +```json +{ + "status": "requires_escalation", + "messages": [ + { + "type": "error", + "code": "require_identity_linking", + "severity": "requires_authorization", + "content": "Additional authorization required" + } + ] +} +``` + ## Events Businesses send order status changes as events after order placement. diff --git a/mkdocs.yml b/mkdocs.yml index 6686c99c9..538d4ee8a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -44,7 +44,9 @@ nav: - Overview: specification/cart.md - HTTP/REST Binding: specification/cart-rest.md - MCP Binding: specification/cart-mcp.md - - Order Capability: specification/order.md + - Order Capability: + - Overview: specification/order.md + - MCP Binding: specification/order-mcp.md - Identity Linking Capability: specification/identity-linking.md - Payment Handlers: - Guide: specification/payment-handler-guide.md diff --git a/source/services/shopping/openapi.json b/source/services/shopping/openapi.json index 542824061..564a15c4e 100644 --- a/source/services/shopping/openapi.json +++ b/source/services/shopping/openapi.json @@ -506,6 +506,62 @@ } } } + }, + "/orders/{id}": { + "parameters": [ + { "$ref": "#/components/parameters/order_id_path" } + ], + "get": { + "operationId": "get_order", + "summary": "Get Order", + "description": "Get the latest state of an order.", + "parameters": [ + { + "$ref": "#/components/parameters/authorization" + }, + { + "$ref": "#/components/parameters/x_api_key" + }, + { + "$ref": "#/components/parameters/request_signature" + }, + { + "$ref": "#/components/parameters/request_id" + }, + { + "$ref": "#/components/parameters/user_agent" + }, + { + "$ref": "#/components/parameters/content_type" + }, + { + "$ref": "#/components/parameters/accept" + }, + { + "$ref": "#/components/parameters/accept_language" + }, + { + "$ref": "#/components/parameters/accept_encoding" + } + ], + "responses": { + "200": { + "description": "Order retrieved", + "headers": { + "X-Detached-JWT": { + "$ref": "#/components/headers/x_detached_jwt" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/order" + } + } + } + } + } + } } }, "webhooks": { @@ -595,6 +651,15 @@ }, "description": "Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code)." }, + "order_id_path": { + "name": "id", + "in": "path", + "required": true, + "description": "The unique identifier of the order.", + "schema": { + "type": "string" + } + }, "x_api_key": { "name": "X-API-Key", "in": "header",