{
  "$schema": "https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",
  "version": "1.0",
  "protocolVersion": "2025-06-18",
  "serverInfo": {
    "name": "ro.example/phone-farm",
    "title": "PhoneFarm.ro",
    "version": "0.1.0"
  },
  "description": "Real iPhones with Romanian SIMs for AI agents: screen control, SMS codes, voice calls. Prepaid, billed per second.",
  "documentationUrl": "https://<domain>/",
  "transport": {
    "type": "streamable-http",
    "endpoint": "https://mcp.<domain>/mcp"
  },
  "capabilities": {
    "tools": {
      "listChanged": false
    }
  },
  "authentication": {
    "required": true,
    "schemes": [
      "bearer"
    ],
    "description": "API key as Bearer token; buy one with x402 at https://mcp.<domain>/x402/topup"
  },
  "payments": {
    "x402": "https://mcp.<domain>/x402/topup",
    "card": "phone_top_up tool (Stripe Checkout)"
  },
  "tools": [
    {
      "name": "phone_list_available_devices",
      "title": "List phones",
      "description": "List your running phones (pass the id as `device` to the other tools), the free phones you can start, and your credit."
    },
    {
      "name": "phone_start_device",
      "title": "Start a phone",
      "description": "Rent a free phone and return its session id. All phones are real iPhones: `platform` and `kind` are accepted for compatibility and may only be \"ios\" and \"device\". `device_id` picks a particular phone from phone_list_available_devices. `minutes` (default 30, max 240) is the most the session can run. Billing is live from your prepaid credit, per second with a 1-minute minimum; a few minutes are reserved up front and the unused part is refunded on phone_stop_device."
    },
    {
      "name": "phone_stop_device",
      "title": "Stop a phone",
      "description": "End your phone session: billing stops, any call is hung up, and the unused reserved credit is refunded."
    },
    {
      "name": "phone_get_screen_size",
      "title": "Screen size",
      "description": "The phone's screen size in the coordinate space the tap and swipe tools use."
    },
    {
      "name": "phone_take_screenshot",
      "title": "Take a screenshot",
      "description": "Take a screenshot of the phone. The image is usually smaller than the screen; the text before it says how to convert a point you see into screen coordinates. Do not reuse an old screenshot."
    },
    {
      "name": "phone_list_elements_on_screen",
      "title": "List what is on screen",
      "description": "List the elements on screen that have text (buttons, labels, fields) with the center of each, ready to tap with phone_click_on_screen_at_coordinates. On an iPhone this is the accessibility tree from WebDriverAgent, so positions are exact. Call it again after the screen changes."
    },
    {
      "name": "phone_click_on_screen_at_coordinates",
      "title": "Tap",
      "description": "Tap the screen at (x, y) in screen coordinates, such as a center from phone_list_elements_on_screen."
    },
    {
      "name": "phone_long_press_on_screen_at_coordinates",
      "title": "Long press",
      "description": "Press and hold at (x, y) for `duration` milliseconds (default 800)."
    },
    {
      "name": "phone_swipe_on_screen",
      "title": "Swipe",
      "description": "Swipe in a direction: the way the finger moves, so \"up\" reveals what is below. Starts at (x, y), or the screen center, and travels `distance` points (default 40% of the screen)."
    },
    {
      "name": "phone_type_keys",
      "title": "Type text",
      "description": "Type text into the focused field. Tap the field first so the keyboard is up. `submit` presses Enter afterwards."
    },
    {
      "name": "phone_press_button",
      "title": "Press a button",
      "description": "Press a system button: HOME, APP_SWITCHER, or ENTER. An iPhone has no Back button: use the app's back control (top left) or swipe right from the left edge."
    },
    {
      "name": "phone_list_apps",
      "title": "List apps",
      "description": "List the apps on the iPhone by bundle id. Any of them can be passed to phone_launch_app."
    },
    {
      "name": "phone_launch_app",
      "title": "Launch an app",
      "description": "Open an app by its name (\"Safari\", \"Messages\", \"Notes\") or bundle id (com.apple.mobilesafari). Settings, Find My, Wallet, App Store, Watch and Shortcuts are not available to renters."
    },
    {
      "name": "phone_get_foreground_app",
      "title": "Foreground app",
      "description": "Which app is in front right now."
    },
    {
      "name": "phone_open_url",
      "title": "Open a URL",
      "description": "Open a URL on the phone: a web page opens in Safari; app links (universal links, custom schemes) open their app. Links into Settings (prefs:, App-prefs:), Find My, Wallet, the App Store and Shortcuts are refused."
    },
    {
      "name": "phone_get_number",
      "title": "Phone number",
      "description": "The phone number of this phone's SIM, in international format: give it to sites that send SMS codes."
    },
    {
      "name": "phone_place_call",
      "title": "Place a call",
      "description": "Call a number (international format, e.g. +40712345678). Billed per second with a 1-minute minimum on top of the phone time. Call audio is a WebSocket of 16 kHz 16-bit mono PCM frames at the URL returned (send the same format to speak). Check phone_get_call_status; end with phone_hang_up."
    },
    {
      "name": "phone_answer_call",
      "title": "Answer a call",
      "description": "Answer the incoming call that is ringing (see phone_get_call_status). Billed per second with a 1-minute minimum."
    },
    {
      "name": "phone_hang_up",
      "title": "Hang up",
      "description": "End the current call, or decline one that is ringing."
    },
    {
      "name": "phone_send_dtmf",
      "title": "Press keypad tones",
      "description": "Press keys during a call, for phone menus (\"press 1\"): digits 0-9, * and #."
    },
    {
      "name": "phone_get_call_status",
      "title": "Call status",
      "description": "Whether a call is idle, ringing (incoming: answer with phone_answer_call), dialing or active, with the number, duration and audio URL."
    },
    {
      "name": "phone_read_sms",
      "title": "Read SMS",
      "description": "SMS received by this phone during your session, oldest first, each with an id. Pass the last id as `since_id` to get only newer ones."
    },
    {
      "name": "phone_send_sms",
      "title": "Send an SMS",
      "description": "Send an SMS from this phone's number. Billed per 160 characters."
    },
    {
      "name": "phone_wait_for_code",
      "title": "Wait for an SMS code",
      "description": "Wait for an SMS with a one-time code (2FA, sign-up) and return just the code. Looks at SMS received during your session that this tool has not returned before, newest first; `sender` filters by sender (substring), `pattern` is an optional regex whose first group is the code. Times out after `timeout_seconds` (max 300)."
    },
    {
      "name": "phone_get_balance",
      "title": "Balance",
      "description": "Your prepaid credit, what running phones are spending per minute, and how long it lasts."
    },
    {
      "name": "phone_top_up",
      "title": "Add credit",
      "description": "Add prepaid credit. method=\"card\" returns a Stripe Checkout link for a human to pay (amount in EUR, 5 to 500). method=\"x402\" returns x402 payment requirements (USDC on Base; amount in USD) for an agent wallet to pay at the URL given."
    }
  ]
}