Protocols and standards
Which protocols and standards SOP uses, and how far each one goes.
The status differs by item; see the table below.
Steps
- Agent → SOP: MCP tools/list, or REST /api/v1
- SOP → Agent: Tools and booking endpoints
- Agent → SOP: request_booking
- SOP → Agent: payment_handlers in UCP format (com.stripe.payments)
- Agent → SOP: complete_booking with a Shared Payment Token
- Agent → SOP: Or pay the payment link with MPP or x402 (testnet)
- SOP → Agent: /openapi.json describes the payment link
Summary
| Protocol or standard | Purpose | Status |
|---|---|---|
| MCP (Model Context Protocol) | Lets agents call tools | Live (Streamable HTTP, 9 tools) |
| REST API | Shares one core with MCP | Live |
OpenAPI discovery document /openapi.json | Describes the payment link (the payable operation) | Live (payment information is testnet only) |
UCP payment handler (com.stripe.payments) | Tells agents which payment handlers can be used | Live (booking responses use this format) |
The rest of UCP (checkout, identity linking, orders, and the discovery file /.well-known/ucp) | A full cross-platform commerce flow | Planned (not implemented) |
| MPP and x402 | Let agents pay directly over HTTP 402 | Testnet |
| ACP and AP2 | Other agentic commerce protocols | Planned (not implemented) |
MCP and REST
Both share one booking core and behave the same way. For the tool list and endpoints, see Connect to bookings.
UCP
The Universal Commerce Protocol (UCP) is an open standard (ucp.dev) that covers checkout, identity linking, order tracking and payment credential exchange. SOP's booking responses describe payment methods in UCP's payment handler format; full UCP checkout and the /.well-known/ucp discovery file (Planned) are not implemented.
MPP and x402
Both are open protocols that let an agent pay directly over HTTP 402. SOP offers them on testnet only for now; see Machine payments.
Discovery document
/openapi.json is currently the only machine-readable discovery document, and it marks payable operations with x-payment-info.
Read the discovery document: /openapi.json
curl
curl -sS "https://mcp.srct.tw/openapi.json" \
-H "User-Agent: my-agent/1.0"
Python
import json
import urllib.error
import urllib.request
BASE = "https://mcp.srct.tw"
def call(method, path, body=None, headers=None):
req = urllib.request.Request(
BASE + path,
data=None if body is None else json.dumps(body).encode(),
method=method,
headers={"User-Agent": "my-agent/1.0", "Content-Type": "application/json", **(headers or {})},
)
try:
with urllib.request.urlopen(req) as res:
return res.status, json.loads(res.read())
except urllib.error.HTTPError as err: # 4xx and 5xx responses still carry a JSON body
return err.code, json.loads(err.read())
def show(status, body):
print("HTTP", status)
print(json.dumps(body, ensure_ascii=False, indent=2))
show(*call("GET", "/openapi.json"))
Node.js
const BASE = "https://mcp.srct.tw";
async function call(method, path, body, headers = {}) {
const res = await fetch(BASE + path, {
method,
headers: { "User-Agent": "my-agent/1.0", "Content-Type": "application/json", ...headers },
body: body === undefined ? undefined : JSON.stringify(body),
});
return { status: res.status, body: await res.json() };
}
function show({ status, body }) {
console.log("HTTP", status);
console.log(JSON.stringify(body, null, 2));
}
(async () => {
show(await call("GET", "/openapi.json"));
})();
Example response
HTTP 200
{
"openapi": "3.1.0",
"info": {
"title": "… booking payments",
"version": "1.0.0",
"description": "Pay for … bookings with x402 or MPP. Create the booking first with the MCP tool request_booking or POST /api/v1/businesses/{business_id}/bookings; use the payment_link it returns."
},
"servers": [
{
"url": "https://mcp.srct.tw"
}
],
"x-service-info": {
"categories": [
"booking",
"fitness",
"services"
],
"docs": {
"homepage": "https://mcp.srct.tw/",
"apiReference": "https://mcp.srct.tw/docs"
}
},
"paths": {
"/pay/{business_id}/{booking_id}": {
"post": {
"summary": "Pay for a booking (x402 or MPP)",
"parameters": [
{
"name": "business_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "booking_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "t",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "pay token from the booking's payment_link"
}
],
"x-payment-info": {
"offers": [
{
"intent": "charge",
"method": "evm",
"amount": null,
"currency": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"description": "USDC on Base Sepolia (test network); also accepted as x402 exact"
},
{
"intent": "charge",
"method": "tempo",
"amount": null,
"currency": "0x20c0000000000000000000000000000000000000",
"description": "pathUSD on Tempo test network (Moderato)"
}
]
},
"responses": {
"200": {
"description": "Paid: booking and payment result"
},
"402": {
"description": "Payment Required: x402 PAYMENT-REQUIRED and MPP WWW-Authenticate challenges"
},
"409": {
"description": "Booking not payable"
},
"410": {
"description": "Payment window passed"
}
}
},
"get": {
"summary": "Pay for a booking (x402 or MPP)",
"parameters": [
{
"name": "business_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "booking_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "t",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "pay token from the booking's payment_link"
}
],
"x-payment-info": {
"offers": [
{
"intent": "charge",
"method": "evm",
"amount": null,
"currency": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"description": "USDC on Base Sepolia (test network); also accepted as x402 exact"
},
{
"intent": "charge",
"method": "tempo",
"amount": null,
"currency": "0x20c0000000000000000000000000000000000000",
"description": "pathUSD on Tempo test network (Moderato)"
}
]
},
"responses": {
"200": {
"description": "Paid: booking and payment result"
},
"402": {
"description": "Payment Required: x402 PAYMENT-REQUIRED and MPP WWW-Authenticate challenges"
},
"409": {
"description": "Booking not payable"
},
"410": {
"description": "Payment window passed"
}
}
}
}
}
}Run against the live service (read-only)
Payment handler fields
| Field | Value |
|---|---|
id | stripe_payments |
type | com.stripe.payments |
version | 2026-06-25 |
available_instruments | card, and link with config.network_id |
config.environment | livemode in production, testmode in tests |
config.business_profile | The Stripe profile the credential must be issued to |
Apple Pay and Google Pay are not offered as instruments.
Security
- Credentials are scoped: one amount, one time window, one recipient.
- SOP never handles raw card numbers.
- Each booking has its own
status_token; it gives access to that booking only. - Writes are rate-limited, and Stripe webhooks to SOP are signature-checked.
Related pages
- Connect to bookings: Search businesses, check availability, create and manage bookings
- Machine payments (MPP and x402, testnet): MPP and x402, on testnet only for now
- Payment credentials: Shared Payment Tokens, and the three roles: seller, agent and platform