予約サービスに接続する
エージェントはMCPまたはREST APIで事業者を検索し、空き枠を確認し、予約を作成・管理します。
公開中予約と照会の機能は提供中です。予約時のオンライン決済はお客さまに代わって支払うをご覧ください。2026年10月22日、最初の事業者(パーソナルトレーナー)がオンライン決済を有効にしました。
このページは、連携を担当するエンジニア向けです。事業者の方は読む必要はありません。事業者側の仕組みはエージェント経由の予約を受けるをご覧ください。
仕組み
SOPの予約コネクターは、MCPサーバーとREST APIの2つのインターフェースを提供します。どちらも同じ予約の中核処理を共有しており、動作は同じです。エージェントの典型的な流れは次のとおりです:事業者を検索し、事業者のデータと料金を読み、空き枠を確認して予約を作成します。オンライン決済が必要なときは支払いを行い、その後で照会・変更・キャンセルができます。
手順
- お客様 → エージェント:サービスを依頼
- エージェント → SOP:事業者と空き枠を検索
- SOP → エージェント:サービス、料金、空き枠を返す
- お客様 → エージェント:枠を選ぶ
- エージェント → SOP:request_booking
- SOP → エージェント:枠を確保:awaiting_payment、金額、期限、payment_handlers
- お客様 → エージェント:ウォレットで金額を承認
- エージェント → SOP:支払い認証情報でcomplete_booking
- SOP → SOP:1回だけ決済。事業者が辞退・未確認なら全額返金
- SOP → 事業者:新しい予約を通知
- 事業者 → SOP:初回の予約は事業者が確認。期限内に確認がなければ解放して返金
- SOP → エージェント:予約状況(確定または確認待ち)と決済結果を返す
- エージェント → お客様:確定、または事業者の確認待ちを伝える
MCPサーバー
エンドポイントはhttps://mcp.srct.tw/mcpです。Streamable HTTPを使い、ステートレスで、レスポンスはJSONです。接続したら、まずinitializeを呼び、次にtools/listでツール一覧を取得します。
MCPセッションの開始:initialize
curl
curl -sS -X POST https://mcp.srct.tw/mcp \
-H "User-Agent: my-agent/1.0" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"my-agent","version":"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))
MCP = {"Accept": "application/json, text/event-stream"} # the server requires both types
show(*call("POST", "/mcp", {
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "my-agent", "version": "1.0"}},
}, MCP))
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));
}
const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types
(async () => {
show(await call("POST", "/mcp", {
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "my-agent", version: "1.0" } },
}, MCP));
})();
レスポンス例
HTTP 200
{
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {
"listChanged": true
}
},
"serverInfo": {
"name": "…-booking",
"title": "…",
"version": "1.0.0",
"websiteUrl": "https://mcp.srct.tw/docs"
},
"instructions": "… lists local service businesses (gyms, personal trainers, studios) in Taiwan that accept bookings through AI agents. Times are Asia/Taipei. Bookings are paid at the venue, unless the business takes online payment: then request_booking returns status awaiting_payment with amount, currency, expires_at and payment_handlers, and complete_booking takes a Stripe shared payment token (payments are collected by Seller Tech U.S.A. LLC through Stripe; unconfirmed paid bookings are refunded in full). A booking is either confirmed immediately or waits for the business to confirm; the status field says which. A pending booking is held only for the business's pending_hold time (10 minutes unless the business sets another); if not confirmed by then, it is released."
},
"jsonrpc": "2.0",
"id": 1
}本番サービスで実行済み(読み取りのみ)
ツール一覧の取得:tools/list
curl
curl -sS -X POST https://mcp.srct.tw/mcp \
-H "User-Agent: my-agent/1.0" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
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))
MCP = {"Accept": "application/json, text/event-stream"} # the server requires both types
show(*call("POST", "/mcp", {"jsonrpc": "2.0", "id": 2, "method": "tools/list"}, MCP))
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));
}
const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types
(async () => {
show(await call("POST", "/mcp", { jsonrpc: "2.0", id: 2, method: "tools/list" }, MCP));
})();
レスポンス例
HTTP 200
{
"result": {
"tools": [
{
"name": "search_businesses",
"title": "Search businesses",
"description": "Search …'s listed businesses by keyword (business name, city or district, or type of class) and/or category. Returns up to 10 businesses with a few matching services. Read-only.",
"inputSchema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"keyword": {
"description": "Words to match, e.g. 'personal training' or 'Taipei'",
"type": "string",
"maxLength": 40
},
"category": {
"description": "gym, coaching (personal trainers), sauna, or recovery",
"type": "string",
"enum": [
"gym",
"coaching",
"sauna",
"recovery"
]
},
"limit": {
"description": "Maximum results, default 5",
"type": "integer",
"minimum": 1,
"maximum": 10
}
}
},
"annotations": {
"title": "Search businesses",
"readOnlyHint": true,
"destructiveHint": false,
"idempotentHint": true,
"openWorldHint": false
},
"execution": {
"taskSupport": "forbidden"
}
},
…
]
},
"jsonrpc": "2.0",
"id": 2
}本番サービスで実行済み(読み取りのみ):表示用に省略しています。…は省略部分を示します。9個のツールが返されます:search_businesses、get_business、get_availability、request_booking、complete_booking、modify_booking、get_booking_status、cancel_booking、get_booking_link。ここでは最初の1つだけを示します。
ツールは全部で9つあります。
| ツール | 用途 | 読み取り専用 |
|---|---|---|
search_businesses | キーワードまたはカテゴリーで事業者を検索します(最大10件)。 | はい |
get_business | 事業者の情報、予約・キャンセルのルール、予約できるサービスの料金と所要時間を取得します。 | はい |
get_availability | あるサービスの今後1〜14日の空き枠と残り枠を取得します。 | はい |
request_booking | お客さまの予約を作成します。 | いいえ |
complete_booking | お客さまが承認した支払い認証情報で、オンライン決済が必要な予約を完了します。 | いいえ |
modify_booking | 予約の時間帯または人数を変更します。 | いいえ |
get_booking_status | 予約の状態を取得します(支払いと返金の情報を含む)。 | はい |
cancel_booking | 予約をキャンセルします。オンラインで支払い済みの場合は返金し、お客さま自身のキャンセルでは決済手数料とプラットフォーム手数料を差し引きます。 | いいえ |
get_booking_link | 事業者が自社の予約システムを使っている場合に、そのシステムへのリンクを取得します。 | いいえ(クリックを1回記録します) |
書き込み系のツールはidempotency_keyを受け取り、再試行を安全にできます。お客さまに代わって支払うをご覧ください。
事業者の検索とデータの取得
search_businessesは、キーワード(事業者名、市区町村、クラスの種類)とカテゴリーで検索できます。get_businessは公開情報と予約できるサービスを返します。business_idとservice_idは、これらのツールが返した値をそのまま使い、自分で組み立てないでください。
事業者の検索:search_businesses
curl
curl -sS -X POST https://mcp.srct.tw/mcp \
-H "User-Agent: my-agent/1.0" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_businesses","arguments":{"keyword":"Taipei","limit":3}}}'
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))
MCP = {"Accept": "application/json, text/event-stream"} # the server requires both types
show(*call("POST", "/mcp", {"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "search_businesses", "arguments": {"keyword": "Taipei", "limit": 3}}}, MCP))
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));
}
const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types
(async () => {
show(await call("POST", "/mcp", { jsonrpc: "2.0", id: 3, method: "tools/call", params: { name: "search_businesses", arguments: { keyword: "Taipei", limit: 3 } } }, MCP));
})();
レスポンス例
HTTP 200
{
"result": {
"content": [
{
"type": "text",
"text": …
}
],
"structuredContent": {
"ok": true,
"count": 1,
"results": [
{
"business_id": "coach-dunken",
"name": "Dunken教練",
"city": "台北市信義區(Taipei Xinyi)",
"categories": [
"coaching"
],
"booking_mode": "request_here",
"services": [
{
"service_id": "first_assessment",
"name": "初次體能檢查(含InBody一次)",
"price": "免費"
},
{
"service_id": "pt",
"name": "PT一對一教練課",
"price": "NT$18,000/10堂"
},
{
"service_id": "trx",
"name": "TRX懸吊訓練",
"price": "NT$18,000/10堂"
}
]
}
]
},
"isError": false
},
"jsonrpc": "2.0",
"id": 3
}本番サービスで実行済み(読み取りのみ):表示用に省略しています。…は省略部分を示します。result.content[0].text(同じJSONの文字列版)は省略しています。
事業者とサービスの取得:get_business
curl
curl -sS -X POST https://mcp.srct.tw/mcp \
-H "User-Agent: my-agent/1.0" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"get_business","arguments":{"business_id":"coach-dunken"}}}'
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))
MCP = {"Accept": "application/json, text/event-stream"} # the server requires both types
show(*call("POST", "/mcp", {"jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": {"name": "get_business", "arguments": {"business_id": "coach-dunken"}}}, MCP))
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));
}
const MCP = { Accept: "application/json, text/event-stream" }; // the server requires both types
(async () => {
show(await call("POST", "/mcp", { jsonrpc: "2.0", id: 4, method: "tools/call", params: { name: "get_business", arguments: { business_id: "coach-dunken" } } }, MCP));
})();
レスポンス例
HTTP 200
{
"result": {
"content": [
{
"type": "text",
"text": …
}
],
"structuredContent": {
"ok": true,
"business": {
"business_id": "coach-dunken",
"name": "Dunken教練",
"categories": [
"coaching"
],
"booking_mode": "request_here",
"city": "台北市信義區(Taipei Xinyi)",
"profile": {
"city": "台北市信義區(Taipei Xinyi)",
"address": "…",
"hours": "每天08:00至20:00",
"booking_policy": "取消與改期需在課程開始24小時前(經AI助理線上取消也是開始前24小時截止)。遲到15分鐘將直接結束當次課程,且無法退費。",
"payment": "現金或轉帳。課程一次購買10堂NT$18,000(每堂折合NT$1,800)。",
"notes": "請自行準備合適的訓練服裝,至少提早10分鐘抵達教室。",
"contact": "…"
},
"faq": [],
"quantity": {
"label": "人數",
"unit": "位",
"max_per_booking": 1
},
"online_cancellation": {
"closes_minutes_before_start": 1440,
"text": "Online cancellation closes 24 hours before the start time; after that, contact the business directly."
},
"pending_hold": {
"minutes": 10,
"text": "Bookings that need the business's confirmation are held for 10 minutes. If the business does not confirm by then, the hold is released automatically and the customer can choose another slot."
},
"payment": {
"mode": "venue",
"text": "Pay at the venue."
}
},
"services": [
{
"service_id": "first_assessment",
"name": "初次體能檢查(含InBody一次)",
"category": "一對一",
"summary": "第一次上課:基本動作與體能檢查,並免費做一次InBody身體組成量測(非醫療評估)。",
"price": {
"amount": 0,
"currency": "TWD",
"unit": "每次",
"text": "免費"
},
"duration_min": 60,
"schedule": [
{
"weekday": "sun",
"time": "08:00",
"teacher": "Dunken"
},
{
"weekday": "sun",
"time": "09:00",
"teacher": "Dunken"
},
…
],
"capacity_per_slot": 1,
"teachers": [
"Dunken"
],
"credit_cost": 0
},
…
],
"payment": "Paid services are paid at the venue; services priced at 0 are free. This connector does not take payments."
},
"isError": false
},
"jsonrpc": "2.0",
"id": 4
}本番サービスで実行済み(読み取りのみ):表示用に省略しています。…は省略部分を示します。result.content[0].text(同じJSONの文字列版)と事業者の住所・連絡先は省略しています。
空き枠の確認
get_availabilityにはbusiness_idとservice_idが必要で、days(1〜14、既定は14)とteacherも指定できます。返される各時間帯には、固定のslot_id(slotと同じ値)、残り枠、料金が含まれます。タイムゾーンはAsia/Taipeiです。
予約可能な時間枠の確認
curl
curl -sS "https://mcp.srct.tw/api/v1/businesses/coach-dunken/availability?service_id=pt&days=2" \
-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", "/api/v1/businesses/coach-dunken/availability?service_id=pt&days=2"))
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", "/api/v1/businesses/coach-dunken/availability?service_id=pt&days=2"));
})();
レスポンス例
HTTP 200
{
"ok": true,
"service_id": "pt",
"bookable_here": true,
"payment": "venue",
"timezone": "Asia/Taipei",
"earliest_bookable": "120 minutes from now",
"online_cancellation": "Online cancellation closes 24 hours before the start time; after that, contact the business directly.",
"quantity_unit": "位",
"slots": [
{
"slot_id": "2026-10-22T08:00",
"slot": "2026-10-22T08:00",
"start": "2026-10-22T08:00:00+08:00",
"label": "10/22(四)08:00",
"remaining": 1,
"price": {
"amount": 1800000,
"currency": "twd",
"per": "person or place",
"text": "NT$18,000/10堂"
},
"resource": {
"teacher": "Dunken"
}
},
{
"slot_id": "2026-10-22T09:00",
"slot": "2026-10-22T09:00",
"start": "2026-10-22T09:00:00+08:00",
"label": "10/22(四)09:00",
"remaining": 1,
"price": {
"amount": 1800000,
"currency": "twd",
"per": "person or place",
"text": "NT$18,000/10堂"
},
"resource": {
"teacher": "Dunken"
}
},
…
]
}本番サービスで実行済み(読み取りのみ):表示用に省略しています。…は省略部分を示します。price.amountは通貨の最小単位(台湾ドルの100倍)です。時刻はAsia/Taipeiです。枠は呼び出した日によって変わります。
REST API
同じ中核処理をRESTでも呼び出せます。パスはすべて/api/v1/businesses/{business_id}/の下です。予約に関するエンドポイントはAuthorization: Bearer <status_token>で認証します。status_tokenは予約作成時に1回だけ返されます。
| メソッドとパス | 用途 |
|---|---|
GET availability?service_id=…&days=… | 空き枠の確認 |
POST bookings | 予約の作成(内容はrequest_bookingと同じ) |
GET bookings/{booking_id} | 予約の取得(金額、期限、支払い方法を含む) |
POST bookings/{booking_id}/complete | 支払い認証情報で予約を完了 |
PATCH bookings/{booking_id} | 時間帯または人数の変更 |
DELETE bookings/{booking_id} | 予約のキャンセル(支払い済みなら返金。お客さま自身のキャンセルでは決済手数料とプラットフォーム手数料を差し引きます) |
エラーは常に{"error": {"code", "message"}}と対応するHTTPステータスで返ります。主なコードはエージェント経由の予約を受けるをご覧ください。
ディスカバリー文書
https://mcp.srct.tw/openapi.jsonは現在、マシンペイメントの支払いリンクだけを記述し、支払いできる操作にx-payment-infoを付けています。現在使われるのはテストネットのマシンペイメントだけです。マシンペイメントをご覧ください。
ディスカバリー文書の取得:/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"));
})();
レスポンス例
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"
}
}
}
}
}
}本番サービスで実行済み(読み取りのみ)
利用時の注意
- 書き込み系の操作には1時間あたりの回数制限があります。超えると
rate_limitedが返ります(RESTでは429)。 - 予約を作成する前に、サービス、時間、人数、連絡先についてお客さまの同意を得てください。氏名、電話番号、メールアドレスは、その事業者にだけ送られます。
- 備考欄
noteには、健康や医療に関する情報を入れないでください。 - 予約の結果は
confirmed(即時確定)、pending(事業者の確認待ち)、awaiting_payment(支払い待ち)のいずれかです。レスポンスのstatusで判断してください。 - 事業者は既定では店頭払いです。
awaiting_paymentが出るのは、事業者がオンライン決済に設定した場合だけです。
機械可読なリソース
- MCPエンドポイントの
tools/listは、すべてのツールと入力スキーマを返します。 /openapi.jsonはマシンペイメントの支払いリンク(テストネット)を記述し、x-payment-infoを付けています。llms.txtと/.well-known/ucpはまだありません。
連携をテストする
- 読み取り専用の呼び出し(
search_businesses、get_business、get_availability、tools/list)は、本番サービスに対して実行しても安全です。 - 予約の作成・変更・キャンセルは、実在する事業者の予定に反映されます。公開テスト用の事業者はまだないため、必要な場合はお問い合わせください。
- 独自の
User-Agentヘッダーを送ってください。Pythonのurllibの既定値のままのリクエストは、ネットワークのエッジでブロックされます。
関連ページ
- お客さまに代わって支払う:お客さまが承認した支払い認証情報で予約の支払いを完了する
- エージェント経由の予約を受ける:事業者向け:エージェントにできること、確認する場所、返金の仕組み
- プロトコルと標準:MCP、REST、UCP、MPP、x402とSOPの対応