MCP Tool Contract - Viator Experiences

This document defines the behavior, field requirements, invocation rules, and expected specifications for the Viator search_experiences and get_experience_details MCP tools. These instructions guide the model on when and how to use each tool, what inputs are required, and how to manage ambiguity.

Table of Contents

Access

To use the MCP server, your client IP address must be added to an IP whitelist - no API keys or tokens are required. Once your IP address is whitelisted, all requests from that IP are accepted without further authentication.

To request access, reach out to the Viator Experiences team via partnerapi@tripadvisor.com with your client IP address. Requests are typically processed within one business day.

If you receive a 403 Forbidden response, your IP has not yet been whitelisted or the request is coming from an unexpected address.

Base URL & Setup

The MCP server is available at:

https://exp-app-mcp.prod.ep.viator.com/mcp

Configure this as the server URL in your MCP client. Once your IP is whitelisted, no additional setup is required.

Tools

search_experiences

Retrieves a curated list of Viator experiences based on required trip context and travel dates.

Supports both low-intent ("things to do in London this weekend") and high-intent ("kid-friendly cooking classes in Rome in July") queries.

The model may re-issue refined search calls when the user adds additional criteria.

When to Use

Invoke this tool whenever the user is asking for activities, experiences, or things to do in a destination and enough context is available or can be clarified.

Examples that should invoke the tool:

When NOT to Use

Do NOT invoke the tool when the user:

The model should ask clarifying questions when query or dates are missing or ambiguous.

Required Behavior Rules

Request Fields

Field Type Required Description
searchTermstringYThe full user search query string. Destination, preferences, and activity type can all be expressed here (e.g. "kid-friendly cooking classes in Rome").
startDatestringYFirst available date in ISO-8601 format (YYYY-MM-DD). LLM must convert natural language to a concrete date.
endDatestringNLast available date in ISO-8601 format (YYYY-MM-DD). Defaults to startDate if the user provides a single-day query.
durationintegerNMaximum experience duration in minutes.
fromPricenumberNLowest per person price.
toPricenumberNHighest per person price.
currencystringNCurrency code for price filtering and display (e.g. USD, EUR).
localestringNLanguage/region code (e.g. en-US). Defaults to en-US.
limitintegerNNumber of products to return (1-10). Defaults to 5.
sessionIdstringYSession identifier. Generate on first call and reuse throughout the session.

Request example

{
  "searchTerm": "kid-friendly things to do in Paris",
  "startDate": "2025-06-01",
  "endDate": "2025-06-07",
  "limit": 5,
  "sessionId": "550e8400-e29b-41d4-a716-446655440000"
}

Response Fields

The tool returns a JSON object with:

Field Type Required Description
sessionIdstringYConversation identifier; echoes the request sessionId when provided, otherwise a server-generated value to reuse on follow-up calls.
experiencesarrayYList of experience summaries. Each element has:
experiences[].titlestringYHuman-readable name of the experience.
experiences[].codestringYExperience internal code. Pass this to get_experience_details for full detail.
experiences[].thumbnailstringYURL of the lead image. To be used in product cards or lists.
experiences[].freeCancellationbooleanYWhether free cancellation is available for this experience.
experiences[].fromPricenumberYAdvertised per-person starting price in the search currency context.
experiences[].fromPriceBeforeDiscountnumberNStrikethrough or pre-discount price when shown; may be omitted or null.
experiences[].clickOffToLanderstringYOutbound URL to the Viator lander for this experience.
experiences[].ratingnumberNAverage customer rating when the API supplies it; may be omitted or null.
experiences[].reviewCountnumberNTotal review count when available; may be omitted or null.
experiences[].durationobjectNLength hints in minutes; may be omitted or null. When present, each sub-field may still be null:
experiences[].duration.fixedDurationInMinutesnumberNFixed duration for single-length experiences.
experiences[].duration.variableDurationFromMinutesnumberNLower bound when duration is a range.
experiences[].duration.variableDurationToMinutesnumberNUpper bound when duration is a range.
experiences[].keyAttributesobjectNDisplay-oriented experience metadata. May be omitted or null.
experiences[].keyAttributes.featuresarrayNFeature tags (e.g. KID_FRIENDLY).
experiences[].keyAttributes.mainCategorystringNPrimary category label for the experience when returned.

Response example

{
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "experiences": [
    {
      "title": "Example Walking Tour",
      "code": "12345P7",
      "thumbnail": "https://example.com/image.jpg",
      "rating": 4.8,
      "reviewCount": 1200,
      "freeCancellation": true,
      "fromPrice": 49.0,
      "fromPriceBeforeDiscount": 59.0,
      "clickOffToLander": "https://example.com/lander",
      "duration": {
        "fixedDurationInMinutes": 120,
        "variableDurationFromMinutes": null,
        "variableDurationToMinutes": null
      },
      "keyAttributes": {
        "features": ["KID_FRIENDLY"],
        "mainCategory": "Walking Tours"
      }
    }
  ]
}

get_experience_details

Retrieves enriched details for a specific Viator product, including description, highlights, images, inclusions/exclusions, insider tips, pricing, and booking URL.

Used when the user selects a product or explicitly asks for more information about one previously shown.

When to Use

Invoke this tool when the user:

If the user's reference is ambiguous (e.g. "the museum tour" after showing several), ask which one they mean before calling.

When NOT to Use

Request Fields

Field Type Required Description
codestringYUnique identifier of the Viator experience. Must be previously returned by search_experiences.
localestringNLanguage/region code (e.g. en-US). Defaults to en-US unless the user specifies or context implies another.
sessionIdstringYA unique identifier for the current user-session. Re-used from the search_experiences response.

Request example

{
  "code": "12345P7",
  "locale": "en-US",
  "sessionId": "550e8400-e29b-41d4-a716-446655440000"
}

Response Fields

The tool returns a JSON object with:

Field Type Required Description
sessionIdstringYConversation identifier; echoes the request sessionId when provided, otherwise a server-generated value to reuse on follow-up calls.
experienceDetailsobjectYDetailed data for the selected experience.
experienceDetails.codestringYExperience internal code.
experienceDetails.titlestringYHuman-readable name of the experience.
experienceDetails.thumbnailstringYURL of the lead image. To be used in product cards or lists.
experienceDetails.freeCancellationbooleanYWhether free cancellation is available for this experience.
experienceDetails.fromPricenumberYAdvertised per-person starting price in the search currency context.
experienceDetails.fromPriceBeforeDiscountnumberNStrikethrough or pre-discount price when shown; may be omitted or null.
experienceDetails.clickOffToPDPstringYOutbound URL to the Viator lander for this experience.
experienceDetails.ratingnumberNAverage customer rating when the API supplies it; may be omitted or null.
experienceDetails.reviewCountnumberNTotal review count when available; may be omitted or null.
experienceDetails.durationobjectNLength hints in minutes; may be omitted or null. When present, each sub-field may still be null:
experienceDetails.duration.fixedDurationInMinutesnumberNFixed duration for single-length experiences.
experienceDetails.duration.variableDurationFromMinutesnumberNLower bound when duration is a range.
experienceDetails.duration.variableDurationToMinutesnumberNUpper bound when duration is a range.
experienceDetails.keyAttributesobjectNDisplay-oriented experience metadata. May be omitted or null.
experienceDetails.keyAttributes.featuresarrayNFeature tags (e.g. KID_FRIENDLY).
experienceDetails.keyAttributes.mainCategorystringNPrimary category label for the experience when returned.
experienceDetails.descriptionstringNMain experience description text.
experienceDetails.insiderTipsstringNExtra tips when available. May be omitted or not.

Response example

{
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "experienceDetails": {
    "code": "12345P7",
    "title": "Example Walking Tour",
    "description": "Explore the city with a local expert guide.",
    "insiderTips": "Wear comfortable shoes.",
    "thumbnail": "https://example.com/image.jpg",
    "rating": 4.8,
    "reviewCount": 1200,
    "freeCancellation": true,
    "clickOffToPDP": "https://example.com/pdp"
  }
}

Error & Ambiguity Handling

Error Handling Rules

Empty results behaviour

If the tool returns zero results, the model must proactively guide the user toward a successful refinement. It should review the parameters used in the failed request and recommend the most appropriate based on the proposed logic below:

Rate Limiting

The API enforces rate limits to ensure reliable service across all consumers. Limits are generous and designed to accommodate standard conversational usage patterns, most integrations won't need to think about this at all.

If you do hit a limit, the server responds with the error message “Rate limit exceeded. Please retry after X seconds." Exponential backoff is also a safe default strategy.

If your use case involves unusually high call volumes, please reach out to the team before going live.

End-to-end example

Here's a complete flow from a user message through to a detail request:

User: "Find me kid-friendly things to do in Paris next weekend."

Step 1: Model resolves the date (assuming today is 2025-05-28): "next weekend" = startDate: 2025-06-07, endDate: 2025-06-08.

Step 2: Call search_experiences

{
  "searchTerm": "kid-friendly things to do in Paris next weekend",
  "startDate": "2025-06-07",
  "endDate": "2025-06-08",
  "limit": 5
}

Step 3: Server returns results including (among others):

{
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "experiences": [
    {
      "title": "Paris Catacombs Skip-the-Line Tour for Families",
      "code": "12345P7",
      "rating": 4.8,
      "reviewCount": 1200,
      "freeCancellation": true,
      "fromPrice": 49.0,
      "clickOffToLander": "https://viator.com/..."
    }
  ]
}

User: "Tell me more about the Catacombs tour."

Step 4: Call get_experience_details

{
  "code": "12345P7",
  "locale": "en-US",
  "sessionId": "550e8400-e29b-41d4-a716-446655440000"
}

Step 5: Server returns enriched product detail, which the model uses to present highlights, inclusions, pricing, and a booking link to the user.