A side-by-side look at the 1stay and GetExperience MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose 1stay for travel & transportation work as a local process; choose GetExperience for travel & transportation over a hosted endpoint. You can also run both side by side in one mcpServers block.
search_hotels, get_hotel_details, book_hotel.search_experiences, get_experience_details, get_experience_schedule.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | Travel & Transportation | Travel & Transportation |
| Quality signal | 61/100 · Good | 51/100 · Good |
| Install command | Confirmed | Confirmed |
| Transport | Local subprocess (stdio) | Remote HTTP/SSE |
| Auth | Not declared | Not declared |
| Pricing | Not declared | Not declared |
| Required env vars | None listed | None listed |
| Verified owner | Community listing | Community listing |
| GitHub stars | 3 | 0 |
| Directory activity | 1 views 0 installs 0 upvotes | 0 views 0 installs 0 upvotes |
| Tools | 8 | 7 |
| Full listing | View 1stay | View GetExperience |
search_hotelsSearch hotels by location and dates for exactly one room. Returns available properties with nightly rates across major brands, boutique, and independent hotels.
The first 1Stay release supports one room per search and reservation. Guest pays the hotel directly. Hotel's own confirmation number. Loyalty points apply.get_hotel_detailsGet rates and room details for exactly one room at a specific hotel. Returns room types, live rates, amenities, cancellation policies, and rate_codes required by book_hotel.
Guest pays the hotel directly. Displayed totals include taxes and any applicable, itemized 1Stay Booking Fee. Loyalty points eligible.
Room type notes: "Run of house" means the hotel assigns the room at check-in. "Suite" at select-service brands usually means a larger room with a sofa, not a separate bedroom.book_hotelBook exactly one hotel room. The first 1Stay release does not support multi-room searches or reservations. Returns a secure checkout URL. This tool accepts no guest identity or payment fields. Name, email, phone, and payment details are collected on the secure checkout page.
Guest pays the hotel directly. Their credit card statement shows the hotel name, not 1Stay. Hotel's own confirmation number. Loyalty points apply automatically. The displayed total includes taxes and any applicable, itemized 1Stay Booking Fee.
Requires a rate_code from get_hotel_details, valid approximately 15 minutes. Expired codes are rejected.lookup_bookingLook up a reservation by verifying the guest's identity. Returns the confirmation number and booking summary in conversation.
Requires the guest's full name plus either the hotel confirmation number, or the booking email together with the last 4 digits of the card used to book. An email address alone is not enough. An optional check-in date can narrow repeat stays.resend_confirmationResend a hotel reservation confirmation email. Use either the confirmation number from a successful lookup_booking call, or the guest's full name and email address when they do not know their confirmation number, card last four, or check-in date. The email contains the hotel confirmation number the guest can then use with lookup_booking or cancel_booking. Recovery requests never reveal whether a reservation matched. The confirmation is sent only to the email address already on the booking record; there is no recipient override. Email changes are handled at https://stayker.com/service.get_bookingLook up a reservation exclusively by the hotel confirmation number shown in the guest's confirmation email. Internal Stayker booking IDs are never accepted or disclosed by MCP. Returns full booking details including hotel, dates, guest info, rate, and status.
Anonymous access requires a verification_token issued by lookup_booking and scoped to a single booking. Without a valid token, no booking data is returned. Developers authenticated with their own API key do not need a token; their access is scoped to their own bookings.cancel_bookingPrepare a hotel cancellation for completion on a secure 1Stay page. Verifies the guest's first name, last name, and hotel confirmation number, then returns the hotel policy classification and any cancellation deadline recorded with the reservation, plus a short-lived cancellation_url. 1Stay does not estimate a refund, credit, penalty, or hotel charge. Calling this tool never cancels the reservation. The guest must review the terms and select Cancel reservation on the secure page. A later verified booking-status lookup reporting status cancelled is the authoritative confirmation that the reservation was cancelled.search_toolsList available 1Stay hotel booking tools. An optional keyword filters by search, book, cancel, or details; absence of a keyword returns all tools.search_experiencesSearch for tours and experiences on GetExperience.com. Works without an account or authentication. Returns list of matching experiences with id, title, price, location, duration, rating, payment type, photos, booking link.
BOOKING FLOW: search_experiences → get_experience_details → get_experience_schedule → add_to_checkout → create_order
EXAMPLES:
- "walking tour in Istanbul" → location: "Istanbul", categories: ["walking-tour"]
- "romantic dinner in Paris" → location: "Paris", categories: ["romantic", "food-and-drink"]
- "things to do in Bali" → location: "Bali"
- "водные виды спорта в Анталье" → location: "Antalya", categories: ["water-sports"], lang: "ru"
- "short private tour with free cancellation" → privateGroup: true, standardCancellation: true, durations: ["10-60"]
- "tours with partial payment in Turkey" → countryCode: "TR", paymentOptions: "part"
- "yacht party" → q: "yacht party"
- "tours for solo traveler" → peopleMin: 1
- "tours with transport included" → features: ["transport"]
All prices are in USD.
SEARCH TIPS:
- location: use English name for best results (e.g. "Istanbul" not "Стамбул"). Non-Latin input is supported but English is more reliable.
- If location is not found, you'll get a clear error — not random results. Try a broader location (country instead of city).
- "languages" filters tours by guide language (["es"] = tours in Spanish)
- "lang" sets the language of titles and descriptions in the response. ALWAYS pass it, matching the END USER's language (e.g. Russian query → lang: "ru"), even when location/other params are in English. Supported: en, es, de, ru, cn, fr, ar, pl, it, pt, nl, tr, cs, th, gr; falls back to "en" if unsupported.
- All filters can be combined: location + categories + paymentOptions + durations + standardCancellation etc.
- "q" is a free-text search across titles and descriptions — use it for specific queries that don't fit into categories.
━━━ PAGINATION ━━━
Results are paginated (default 25 per page). The response always shows the total count and which results you're viewing (e.g. "showing 1–25 of 80"). If there are more results, ALWAYS offer the user to see the next page. Use page parameter to fetch subsequent pages.
━━━ HOW TO PRESENT THESE RESULTS ━━━
DEFAULT PRESENTATION — a visual card gallery, not a plain text list. Every response
carries a [GXP_STRUCTURED] block; build the gallery from it whenever the surface can
render rich output, without waiting to be asked. Fall back to the markdown text above
only on text-only surfaces or when the user explicitly asks for a plain list.
Each card shows, in this order:
1. the photo — mainPhoto (photos[] holds more; keep the aspect ratio, no cropping of faces)
2. title
3. price (the "price" field is already formatted; priceCents is the raw value)
4. duration, and rating with review count when present
5. a link/button to the item's "link" field, labelled like "View" / "Подробнее" (localise to the user's language)
Lay the cards out in a responsive grid (2–3 per row on desktop, 1 on mobile), keep the
source order — it is relevance-ranked — and render every returned item, not a subset.
Photo URLs are public CDN links and may be used directly in <img> tags.
The cards REPLACE the plain list, not the surrounding information: still convey the
result count / pagination line above and offer the next page when more results exist.
Parse the block with:
const match = text.match(/\[GXP_STRUCTURED\]([\s\S]*?)\[\/GXP_STRUCTURED\]/);
const data = match ? JSON.parse(match[1].trim()) : null;
// → data.items[] — each item has: id, title, price, priceCents, location,
// durationMinutes, rating, reviewsCount, mainPhoto, photos[], paymentOptions, link
To get pure JSON without markdown: set responseFormat: "json"get_experience_detailsGet full details of a specific experience by ID.
Call when: the user wants to learn more about a tour before booking, or to confirm full description, photos, pricing and availability options.
Call after search_experiences — use id from search results.
All prices are in USD.
RETURNS: full description, all photos, price breakdown, duration, guide languages, group size limits, categories, rating, cancellation policy, payment options, included features, booking link.
⚠️ CANCELLATION POLICY — MANDATORY DISCLOSURE:
The response includes cancellationPolicy field. You MUST clearly communicate it to the user when presenting experience details. Examples:
- Strict policy: "Please note: this experience has a strict cancellation policy — no refunds after booking."
- Flexible policy: "Good news — free cancellation up to 24 hours before the experience."
- If cancellationPolicy is null or empty: "Cancellation policy is not specified — we recommend contacting the host before booking."
Always present this BEFORE the user proceeds to booking.
━━━ HOW TO PRESENT THIS EXPERIENCE ━━━
DEFAULT PRESENTATION — a rich card, not a wall of text. The response carries a
[GXP_STRUCTURED] block: lead with the photos (photos[] is the gallery, mainPhoto the
hero image), then title, price, duration, rating, what's included, the cancellation
line above, and a link/button to the "link" field. Photo URLs are public CDN links usable
directly in <img> tags. Fall back to the markdown text only on text-only surfaces or
when the user asks for plain text. The card format never removes a required disclosure:
the cancellation policy and, when partial payment is available, the deposit option must
still be stated (see the sections above).
💰 PARTIAL PAYMENT — PROACTIVE SUGGESTION:
The response includes paymentOptions (["full"], ["part"] or ["full", "part"]). If "part" is present, you MUST proactively tell the user: "This experience offers partial payment — you can pay a deposit now and the rest on site." The deposit is in prepayPrice (display string) / prepayPriceCents (number) in the structured item — show it next to the total price. If paymentOptions is ["part"] only, full online payment is NOT available: pass paymentType: "part" to create_order (the API rejects "full" for such tours). When both options exist, ask the user: "Would you like to pay the full amount or just the deposit?"
[GXP_STRUCTURED] block: data.item — single experience object with all fields including cancellationPolicy, paymentOptions, prepayPrice(+Cents), features, onDemand.
Parse: const match = text.match(/\[GXP_STRUCTURED\]([\s\S]*?)\[\/GXP_STRUCTURED\]/); const data = JSON.parse(match[1].trim());get_experience_scheduleGet available time slots for a specific experience on a specific date. Required step before booking.
CRITICAL: Copy configId and startAt EXACTLY from the chosen slot — pass these unchanged to add_to_checkout. Do not modify or recalculate these values. Copy durationMinutes when the slot provides it; if the slot returns durationMinutes as null (common for on-demand or flexible schedules), you may omit it from add_to_checkout — the server will fill it in from the experience's default duration.
All prices are in USD.
RETURNS: available slots with configId, startAt (ISO datetime), durationMinutes.
Each slot exposes price per adult / child / private group in two formats:
- numeric USD values: priceAdult, priceChild, pricePrivateGroup (for arithmetic — e.g. priceAdult * adults)
- formatted display strings: priceAdultFormatted, priceChildFormatted, pricePrivateGroupFormatted (e.g. "US$45.00", for display)
- partial payment: prepayPriceFrom (deposit, USD number) + prepayPricePublicFormatted / prepayPricePrivateGroupFormatted; null when the tour is full-payment only
BOOKING FLOW: search_experiences → get_experience_schedule → add_to_checkout → create_order
[GXP_STRUCTURED] block: data.slots[] — array of available slots with all pricing details.get_checkoutGet current cart contents by sessionId.
Use when: you need to confirm what's in the cart before placing the order, or to retrieve checkoutId if it was not saved after add_to_checkout.
sessionId is returned by add_to_checkout — always save it.
RETURNS: checkoutId (required for create_order), list of items with titles and prices, total price, payment options.
💰 PARTIAL PAYMENT INFO:
If paymentOptions includes "part", the response shows:
- Total price (full amount)
- Pay now (deposit amount)
- Pay on site (remaining amount)
Present all three amounts to the user clearly. Before proceeding to create_order, ask: "Would you like to pay the full amount or just the deposit?"
PRICE BREAKDOWN: catalog prices (search/details/schedule) are WITHOUT the service fee; the checkout total is WITH it. When the response includes the breakdown, it shows Subtotal (excl. service fee) + Service fee = Total — present all three lines to the user. If the response has only Total (older deployments / breakdown unavailable), show just the Total — NEVER invent or compute the split yourself.
[GXP_STRUCTURED] block: data.checkoutId, data.items[], data.subTotalPrice(+Formatted), data.totalServiceFee(+Formatted), data.totalPrice(+Formatted), data.paymentOptions, data.totalPrepayPriceFormatted, data.payLaterPriceFormatted.add_to_checkoutAdd an experience to the cart. Requires a connected GetExperience account: without user authorization the call is answered with an OAuth sign-in challenge (HTTP 401, or a tool error carrying the challenge on clients that link accounts per tool) — connect the account and retry. Call get_experience_schedule first and take configId, startAt from the chosen slot — pass them here EXACTLY as returned. Also pass durationMinutes when the slot provides it; if it is null, you may omit it and the server will substitute the experience's default duration.
RETURNS: sessionId, checkoutId, total price.
⚠️ SAVE sessionId — you will need it for get_checkout and create_order.
To add multiple experiences to the same cart: reuse the same sessionId in subsequent calls.
NEXT STEP: After adding to cart, call get_checkout to see full cart details including payment options. If partial payment is available, the checkout will show deposit amount vs total — present both to the user.
BOOKING FLOW: get_experience_schedule → add_to_checkout → get_checkout → create_order
PRICE BREAKDOWN: catalog prices (search/details/schedule) are WITHOUT the service fee; the checkout total is WITH it. When the response includes the breakdown, it shows Subtotal (excl. service fee) + Service fee = Total — present all three lines to the user. If the response has only Total (older deployments / breakdown unavailable), show just the Total — NEVER invent or compute the split yourself.
[GXP_STRUCTURED] block: data.sessionId, data.checkoutId, data.subTotalPrice(+Formatted), data.totalServiceFee(+Formatted), data.totalPrice(+Formatted).create_orderPlace the booking order for the cart — the last step of the booking flow. Requires a connected GetExperience account: without user authorization the call is answered with an OAuth sign-in challenge (HTTP 401, or a tool error carrying the challenge on clients that link accounts per tool) — connect the account and retry.
BOOKING FLOW: search → get_details → get_schedule → add_to_checkout → get_checkout → create_order
The order is created UNPAID (status "new") and the response carries a secure payment link. Share the link with the user: payment is completed on the checkout page in their browser — never in the chat, and never ask the user for card details. The booking becomes active after payment. The link expires in 24 hours.
Do NOT ask for guest details: the booking is attributed to the connected GetExperience account — name and e-mail come from the account, and GetExperience e-mails the confirmation and voucher to the account address (hosts never e-mail guests directly). The ONLY detail that can be missing is a phone number: if the server reports that the account has none on file, ask the user for their phone and retry with user: { tel: "+34...", countryCode: "ES" }. The number is saved to their account, so this happens at most once.
⚠️ CANCELLATION POLICY REMINDER — MANDATORY BEFORE PAYMENT:
Before calling create_order, you MUST remind the user about the cancellation policy (from get_experience_details response). If the policy is strict (no refunds), explicitly warn: "Once you pay, this booking cannot be canceled or refunded." If flexible, reassure them. If unknown, advise checking with the host. Do NOT proceed to payment without this disclosure.
💰 PAYMENT TYPE CHOICE — ASK BEFORE ORDERING:
Check paymentOptions from get_checkout or get_experience_details. If "part" is available, you MUST ask the user before placing the order: "Would you like to pay the full amount ($X) or just the deposit ($Y) and pay the rest on site?" Then pass the chosen paymentType ("full" or "part") to create_order.
[GXP_STRUCTURED] block: data.orderId, data.status, data.priceFormatted, data.paymentType, data.paidAmountFormatted, data.link, data.paymentPageUrl.request_availabilitySend an availability request to the host of an ON-DEMAND experience.
Call when: an experience is "available on request" (onDemand: true in search/details, or get_experience_schedule says "available on request") and the user wants it for specific dates. On-demand experiences CANNOT be booked instantly — the host must confirm availability first. This tool sends the request; it does NOT create a booking or charge anyone.
Do NOT call for instant-booking experiences (onDemand: false) — use get_experience_schedule → add_to_checkout → create_order instead. The tool rejects non-on-demand experiences with an error.
BEFORE calling, collect from the user: preferred dates, number of guests, their NAME and EMAIL (required — the host's answer is delivered to this email and to the guest's account in the app where the request was made; without email the guest never learns the result).
WHAT HAPPENS NEXT (tell the user):
1. The host reviews the request, usually within 24–48 hours.
2. The guest gets the answer by email and in their account.
3. If confirmed — the answer contains a booking link valid for 12 hours.
4. If declined — suggest other experiences nearby.
There is no status-polling endpoint; the answer is delivered to the guest directly, not to this chat.
⚠️ Limit: up to 3 availability requests at a time per guest.
[GXP_STRUCTURED] block: data.requestId, experienceId, dates, guests, hostResponseWindowHours, bookingWindowHoursAfterConfirm.Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
{
"mcpServers": {
"1stay": {
"command": "npx",
"args": [
"-y",
"1stay-mcp"
]
}
}
}
{
"mcpServers": {
"getexperience": {
"url": "https://getexperience.com/mcp"
}
}
}