SOPSellers' Open Protocol

事業者とサービスのデータ

エージェントは、貴店が何を売っているかを読み取れてはじめて、貴店を見つけ、予約できます。

事業者とサービスのデータ:公開中。商品フィード:計画中。

エージェントが読むのは構造化データ

SOPは、各事業者を構造化データに整えます。事業者情報、予約・キャンセルのルール、サービスごとの料金と所要時間です。データは、貴店が一つずつ確認してから公開されます。どのサービスと空き枠を公開するかも、貴店が決めます。

エージェントがSOPの掲載情報から読むもの
エージェントSOP事業者事業者情報、サービス、料金、スケジュール、キャンセル規定事業者の確認後に掲載search_businesses該当する事業者と一部のサービスを返すget_business事業者情報、料金とスケジュール付きのサービス、規定を返すget_availability空き枠と料金を返す
手順
  1. 事業者 → SOP:事業者情報、サービス、料金、スケジュール、キャンセル規定
  2. SOP → SOP:事業者の確認後に掲載
  3. エージェント → SOP:search_businesses
  4. SOP → エージェント:該当する事業者と一部のサービスを返す
  5. エージェント → SOP:get_business
  6. SOP → エージェント:事業者情報、料金とスケジュール付きのサービス、規定を返す
  7. エージェント → SOP:get_availability
  8. SOP → エージェント:空き枠と料金を返す

事業者のデータ

get_businessは次の内容を返します。

フィールド説明
business_id、name、categories、city事業者ID、名称、カテゴリー、市区町村
profile公開している住所、営業時間、予約ポリシー、支払いの説明、駐車場、備考、連絡先
faqよくある質問
quantity人数や枠の呼び方と単位、1回の予約の上限
online_cancellationオンラインでのキャンセル期限
payment支払い方法(店頭払いかオンライン決済か)と支払い期限
booking_moderequest_here(ここで予約)またはexternal_link(事業者自身の予約システムを使用)

事業者とサービスの取得: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の文字列版)と事業者の住所・連絡先は省略しています。

サービスのデータ

予約できるサービスごとに、service_id、name、category、summary、料金(amount、currency、unit、text)、所要時間duration_minがあります。エージェントは、空き枠の確認や予約の作成にservice_idを使います。

商品フィード

小売商品には、別の種類のデータが必要です。商品、在庫、価格、プロモーションを、事業者がまとめて提供するものです。この部分の現況は計画中です。商品フィードの取り込み機能はまだないため、ここでは形式やサンプルを示しません。

商品フィードとの比較

商品フィードの概念SOPのサービスデータ
基本的な商品データservice_id、名称、事業者情報
価格price(台湾ドル。予約の金額は最小単位)
在庫と販売可否get_availabilityが返す空き枠と、枠ごとの定員
提供方法住所、営業時間、予約・キャンセル規定
バリエーションサービスごとに分け、それぞれにservice_id
画像・レビュー・プロモーション提供していません