{
  "openapi": "3.1.0",
  "info": {
    "title": "eSIM-Global.VIP eSIM Catalog",
    "version": "2.0.5",
    "summary": "Prepaid travel eSIM data plans for 200+ countries.",
    "description": "Prepaid travel eSIM data plans for 200+ countries from eSIM-Global.VIP. Search by destination (any language, one country or several, or a region), get every plan fact the catalogue holds — coverage with networks per country, data and speed rules, activation and validity rules, hotspot / APN / registration facts, top-up, refund rule, price with its basis — let the server recommend plans for a whole trip, read the shop's own facts (delivery, payment, guarantee, support, devices) and hand the customer a direct buy link. Three plan shapes: Data Pack (a fixed amount of data over a validity window), Unlimited Flex (priced per day - the customer chooses the duration at checkout within the plan's day range - with a daily high-speed allowance that continues at reduced speed) and Daily Pack (a set number of days, each with its own high-speed allowance). The eSIM is emailed immediately after payment — no account, no app.\n\n## This is an MCP server, not a REST API\n\nEvery call is `POST /mcp` with a JSON-RPC 2.0 body. The path never changes — only the body does. Transport is MCP streamable-HTTP, protocol version `2025-06-18`. Server version 2.0.5.\n\n### Call order\n\n1. `POST /mcp` with `method: \"initialize\"`. The response carries an **`Mcp-Session-Id`** response header and the server instructions (read them — they carry the pricing and presentation rules).\n2. `POST /mcp` with `method: \"notifications/initialized\"` and that header. Returns 202 with an empty body — it is a notification, so no JSON-RPC result comes back.\n3. Every later request repeats the `Mcp-Session-Id` header.\n\nThe server also answers a `tools/call` without the handshake (the session id is advisory, never enforced) — the handshake is still the correct order because it delivers the instructions.\n\n### Reading errors correctly\n\nJSON-RPC errors are returned with **HTTP 200** and an `error` object in the body. This is what the JSON-RPC 2.0 and MCP specifications require, and it is deliberate. Do not treat a 200 as success without checking that `result` is present and `error` is absent. A tool that cannot fulfil a request (unknown plan, unknown destination, missing argument) answers with `result.isError: true` and a `structuredContent.code` such as `not_found`, `unknown_destination` or `invalid_argument`. A body that is not valid JSON at all returns HTTP 400. `GET /mcp` returns 405: this server implements streamable-HTTP without an SSE stream, so refusing GET is correct.\n\n### Results\n\nEvery tool result carries `structuredContent` (the JSON object) and the same JSON as text in `content[0].text`. Plans carry only the facts the catalogue holds — an absent field is unknown, never to be inferred. `coverage_countries` lists every country a plan works in (ISO code, name, network operators where known); `covers_via` marks a multi-country plan returned for a single-country search.\n\n### Prices\n\n`price` must be read with `price_basis`. `total` (Data Pack, Daily Pack): the full price of the plan. `per_day` (Unlimited Flex): the price of ONE day - the customer picks the duration at checkout within `days_range`, longer durations are discounted, and `price_max` is the price for the longest duration with the discount applied; `start_checkout` with `days` returns `total_for_days`, `recommend_plan` returns `total_for_trip`. Never present a per-day price as the price of the plan. Prices are quoted in `pricing_bucket` (language-region, default en-us / USD); pass `bucket` or `currency` to quote in the customer's own.\n\n### Limits and safety\n\nRate limit: 60 requests per minute per session and 120 per IP for unrecognised clients (recognised assistants: 120 per session, 480 per IP); exceeding it returns 429 by design. No authentication, no API key, no account — the endpoint is public and read-only. `start_checkout` only returns a `buy_url`; it takes no payment and creates no order, so every tool here is safe to call in a test run. Prices and availability are live, so exact values change between calls — assert on shape, not on specific prices.\n",
    "contact": {
      "name": "IbiPoint Ltd",
      "url": "https://esim-global.vip/esimshop/",
      "email": "contact@esim-global.vip"
    },
    "x-mcp": {
      "endpoint": "https://esim-global.vip/esimshop/wp-json/esimglobal/mcp",
      "transport": "streamable-http",
      "protocolVersion": "2025-06-18",
      "serverCard": "https://esim-global.vip/.well-known/mcp/server-card.json",
      "llms": "https://esim-global.vip/llms.txt"
    }
  },
  "servers": [
    {
      "url": "https://esim-global.vip/esimshop/wp-json/esimglobal",
      "description": "eSIM-Global.VIP MCP server (public, no auth)"
    }
  ],
  "externalDocs": {
    "description": "Plain-language guide for LLM agents",
    "url": "https://esim-global.vip/llms.txt"
  },
  "paths": {
    "/mcp": {
      "post": {
        "operationId": "mcpCall",
        "summary": "MCP JSON-RPC 2.0 endpoint — handshake, tools/list, tools/call, resources, prompts",
        "description": "All MCP traffic goes here. Pick the request body from the examples; they are in call order. Send Mcp-Session-Id on every call after initialize.",
        "parameters": [
          {
            "name": "Mcp-Session-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Returned by initialize as a response header. Repeat it on every subsequent request. Omit it only on the initialize call itself."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              },
              "examples": {
                "1_initialize": {
                  "summary": "Step 1 — handshake (must be first)",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "initialize",
                    "params": {
                      "protocolVersion": "2025-06-18",
                      "capabilities": {},
                      "clientInfo": {
                        "name": "your-agent",
                        "version": "1.0"
                      }
                    }
                  }
                },
                "2_initialized": {
                  "summary": "Step 2 — notification, send with Mcp-Session-Id",
                  "value": {
                    "jsonrpc": "2.0",
                    "method": "notifications/initialized"
                  }
                },
                "3_tools_list": {
                  "summary": "Step 3 — list the available tools",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/list"
                  }
                },
                "4_search_esim_plans": {
                  "summary": "tools/call — search_esim_plans",
                  "description": "Find eSIM-Global.VIP eSIM plans for one destination or several (countries or regions, any language). Returns the best-value plans first with full facts, coverage lists and a direct link; multi-country plans that include the destination are returned too and marked covers_via. Use when a user wants mobile data abroad; use recommend_plan when they describe a whole trip.",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 4,
                    "method": "tools/call",
                    "params": {
                      "name": "search_esim_plans",
                      "arguments": {
                        "destination": "Japan",
                        "language": "en"
                      }
                    }
                  }
                },
                "5_get_plan_details": {
                  "summary": "tools/call — get_plan_details",
                  "description": "Full facts for one eSIM-Global.VIP plan by product id: coverage with networks per country, data and speed rules, activation and validity rules, hotspot / APN / registration facts, top-up, refund rule, price with basis, reviews and link — quoted in the customer's bucket or currency when given.",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 5,
                    "method": "tools/call",
                    "params": {
                      "name": "get_plan_details",
                      "arguments": {
                        "product_id": 12171,
                        "language": "en"
                      }
                    }
                  }
                },
                "6_start_checkout": {
                  "summary": "tools/call — start_checkout",
                  "description": "Hand the customer to the eSIM-Global.VIP shop to buy one plan. Returns buy_url (the product page where they tap Buy now and pay securely — card, Apple Pay, Google Pay, PayPal, with an optional discount code) plus the plan name and price in the customer's bucket or currency. No account needed; the eSIM is emailed right after payment. There is no in-chat order tracking.",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 6,
                    "method": "tools/call",
                    "params": {
                      "name": "start_checkout",
                      "arguments": {
                        "product_id": 12171,
                        "days": 10,
                        "language": "en"
                      }
                    }
                  }
                },
                "7_recommend_plan": {
                  "summary": "tools/call — recommend_plan",
                  "description": "Turn a trip into up to three plan recommendations: countries (1-5, any language), number of days and how much data the traveller uses. Each fit carries the full plan facts, the total price for the whole trip and a one-sentence reason built only from catalogue facts. Use this first whenever the user describes a trip rather than asks for a list.",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 7,
                    "method": "tools/call",
                    "params": {
                      "name": "recommend_plan",
                      "arguments": {
                        "countries": [
                          "Germany",
                          "Italy",
                          "Switzerland"
                        ],
                        "days": 14,
                        "usage": "normal",
                        "language": "en"
                      }
                    }
                  }
                },
                "8_get_shop_facts": {
                  "summary": "tools/call — get_shop_facts",
                  "description": "How eSIM-Global.VIP works, in the shop's own words: delivery and installation, device requirements, payment methods, money-back guarantee, top-up, hotspot, support and company details. Use for any \"how / when / what if\" question that is not about one specific plan.",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 8,
                    "method": "tools/call",
                    "params": {
                      "name": "get_shop_facts",
                      "arguments": {}
                    }
                  }
                },
                "9_resources_list": {
                  "summary": "resources/list — llms.txt, shop facts, setup guide",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 9,
                    "method": "resources/list"
                  }
                },
                "10_prompts_list": {
                  "summary": "prompts/list — find_plan_for_trip, compare_plans",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 10,
                    "method": "prompts/list"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response. Carries either `result` (success, possibly with `isError: true` for a tool-level failure) or `error` (a JSON-RPC error — still HTTP 200 by specification). The initialize response additionally sets Mcp-Session-Id.",
            "headers": {
              "Mcp-Session-Id": {
                "description": "Session id, set on the initialize response only.",
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/JsonRpcSuccess"
                    },
                    {
                      "$ref": "#/components/schemas/JsonRpcError"
                    }
                  ]
                }
              }
            }
          },
          "202": {
            "description": "Accepted — returned for JSON-RPC notifications such as notifications/initialized. Empty body, no result."
          },
          "400": {
            "description": "The body was not valid JSON at all (WordPress REST rest_invalid_json). A well-formed request that fails validation returns 200 with a JSON-RPC error instead."
          },
          "405": {
            "description": "GET is not served — streamable-HTTP without an SSE stream."
          },
          "429": {
            "description": "Rate limit exceeded — 60 requests per minute per session / 120 per IP for unrecognised clients."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": "integer",
            "description": "Correlation id. Omit for notifications."
          },
          "method": {
            "type": "string",
            "enum": [
              "initialize",
              "notifications/initialized",
              "ping",
              "tools/list",
              "tools/call",
              "resources/list",
              "resources/read",
              "prompts/list",
              "prompts/get"
            ]
          },
          "params": {
            "type": "object",
            "description": "Shape depends on method — see examples."
          }
        }
      },
      "JsonRpcSuccess": {
        "type": "object",
        "required": [
          "jsonrpc",
          "id",
          "result"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": "integer"
          },
          "result": {
            "type": "object",
            "description": "For tools/call: content[] (text = the JSON as a string), structuredContent (the JSON object) and isError."
          }
        }
      },
      "JsonRpcError": {
        "type": "object",
        "required": [
          "jsonrpc",
          "error"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "integer",
                "description": "-32700 parse error, -32600 invalid request, -32601 method not found, -32602 invalid params (includes unknown tool name), -32603 internal error."
              },
              "message": {
                "type": "string"
              },
              "data": {}
            }
          }
        }
      },
      "search_esim_plans_arguments": {
        "type": "object",
        "properties": {
          "destination": {
            "type": "string",
            "description": "Country or region in any language, e.g. \"Thailand\", \"Japon\", \"Europa\", or a 2-letter ISO code. For a city, pass its country."
          },
          "destinations": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 5,
            "description": "Several countries for one trip; only plans covering ALL of them are returned. Overrides destination."
          },
          "type": {
            "type": "string",
            "enum": [
              "data_pack",
              "daily_pack",
              "unlimited_flex",
              "any"
            ],
            "description": "Plan type. Default any."
          },
          "min_days": {
            "type": "integer",
            "minimum": 1,
            "description": "Only plans valid for at least this many days (Unlimited Flex counts when days_range allows it)."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50,
            "description": "Plans to return. Default 10; all_plans_url lists everything."
          },
          "sort": {
            "type": "string",
            "enum": [
              "value",
              "price",
              "data"
            ],
            "description": "value = best price per GB / cheapest Flex first (default); price = cheapest first; data = most data first."
          },
          "language": {
            "type": "string",
            "description": "ISO language of the conversation, e.g. en, de, ja. Plan names and country names come back in it when available. Bare \"en\" is quoted in en-us / USD."
          },
          "bucket": {
            "type": "string",
            "description": "Optional language-region bucket the customer shops in, e.g. \"en-gb\", \"de-de\". Wins over language; prices and links are quoted in it."
          },
          "currency": {
            "type": "string",
            "description": "Optional ISO 4217 code. Every price in the result is quoted in it."
          }
        },
        "required": [],
        "title": "search_esim_plans arguments",
        "description": "Find eSIM-Global.VIP eSIM plans for one destination or several (countries or regions, any language). Returns the best-value plans first with full facts, coverage lists and a direct link; multi-country plans that include the destination are returned too and marked covers_via. Use when a user wants mobile data abroad; use recommend_plan when they describe a whole trip."
      },
      "get_plan_details_arguments": {
        "type": "object",
        "properties": {
          "product_id": {
            "type": "integer",
            "description": "Product id from search_esim_plans or recommend_plan."
          },
          "language": {
            "type": "string",
            "description": "ISO language of the conversation, e.g. en, de, ja. Plan names and country names come back in it when available. Bare \"en\" is quoted in en-us / USD."
          },
          "bucket": {
            "type": "string",
            "description": "Optional language-region bucket the customer shops in, e.g. \"en-gb\", \"de-de\". Wins over language; prices and links are quoted in it."
          },
          "currency": {
            "type": "string",
            "description": "Optional ISO 4217 code. Every price in the result is quoted in it."
          }
        },
        "required": [
          "product_id"
        ],
        "title": "get_plan_details arguments",
        "description": "Full facts for one eSIM-Global.VIP plan by product id: coverage with networks per country, data and speed rules, activation and validity rules, hotspot / APN / registration facts, top-up, refund rule, price with basis, reviews and link — quoted in the customer's bucket or currency when given."
      },
      "start_checkout_arguments": {
        "type": "object",
        "properties": {
          "product_id": {
            "type": "integer",
            "description": "Product id to buy."
          },
          "days": {
            "type": "integer",
            "minimum": 1,
            "description": "Unlimited Flex only: the duration the customer wants; the returned total_for_days is the checkout total for it."
          },
          "language": {
            "type": "string",
            "description": "ISO language of the conversation, e.g. en, de, ja. Plan names and country names come back in it when available. Bare \"en\" is quoted in en-us / USD."
          },
          "bucket": {
            "type": "string",
            "description": "Optional language-region bucket the customer shops in, e.g. \"en-gb\", \"de-de\". Wins over language; prices and links are quoted in it."
          },
          "currency": {
            "type": "string",
            "description": "Optional ISO 4217 code. Every price in the result is quoted in it."
          }
        },
        "required": [
          "product_id"
        ],
        "title": "start_checkout arguments",
        "description": "Hand the customer to the eSIM-Global.VIP shop to buy one plan. Returns buy_url (the product page where they tap Buy now and pay securely — card, Apple Pay, Google Pay, PayPal, with an optional discount code) plus the plan name and price in the customer's bucket or currency. No account needed; the eSIM is emailed right after payment. There is no in-chat order tracking."
      },
      "recommend_plan_arguments": {
        "type": "object",
        "properties": {
          "countries": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "maxItems": 5,
            "description": "Countries or regions of the trip, any language."
          },
          "days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "description": "Trip length in days."
          },
          "usage": {
            "type": "string",
            "enum": [
              "light",
              "normal",
              "heavy"
            ],
            "description": "light ≈ 0.5 GB/day (maps, messaging), normal ≈ 1.5 GB/day (social, some video), heavy ≈ 4 GB/day (video, hotspot). Default normal."
          },
          "gb_per_day": {
            "type": "number",
            "minimum": 0.1,
            "description": "Overrides usage with an explicit daily need in GB."
          },
          "budget": {
            "type": "number",
            "minimum": 0,
            "description": "Optional maximum total for the trip, in the result currency."
          },
          "language": {
            "type": "string",
            "description": "ISO language of the conversation, e.g. en, de, ja. Plan names and country names come back in it when available. Bare \"en\" is quoted in en-us / USD."
          },
          "bucket": {
            "type": "string",
            "description": "Optional language-region bucket the customer shops in, e.g. \"en-gb\", \"de-de\". Wins over language; prices and links are quoted in it."
          },
          "currency": {
            "type": "string",
            "description": "Optional ISO 4217 code. Every price in the result is quoted in it."
          }
        },
        "required": [
          "countries",
          "days"
        ],
        "title": "recommend_plan arguments",
        "description": "Turn a trip into up to three plan recommendations: countries (1-5, any language), number of days and how much data the traveller uses. Each fit carries the full plan facts, the total price for the whole trip and a one-sentence reason built only from catalogue facts. Use this first whenever the user describes a trip rather than asks for a list."
      },
      "get_shop_facts_arguments": {
        "type": "object",
        "properties": {},
        "title": "get_shop_facts arguments",
        "description": "How eSIM-Global.VIP works, in the shop's own words: delivery and installation, device requirements, payment methods, money-back guarantee, top-up, hotspot, support and company details. Use for any \"how / when / what if\" question that is not about one specific plan."
      }
    }
  },
  "security": []
}