# Kismet: Muse connector brief

Read this file first. It is the contract for connecting Muse to Kismet and using it well.

Brief version: 2026-09-24. Canonical URL: https://mcp.kismet.travel/connectors/muse.md

## What this service is

Kismet is direct booking for professionally managed vacation rentals. One connector reaches many independent property managers (Kismet calls each manager's inventory a collection): cabins, beach houses, condos, lodges and villas, with live dated prices, full details, photos, policies and guest reviews.

A user asks Muse to find a place to stay, compare options, check a price for real dates, then open a booking link. The connector does not take payment and never sees card details. The property manager is the merchant. Checkout happens on the page the booking link opens, with the user's approval.

## Connection details

| Item | Value |
|---|---|
| Product | Kismet |
| Website | https://kismet.travel |
| Privacy | https://kismet.travel/privacy |
| Terms | https://kismet.travel/terms |
| Support | https://kismet.travel/support |
| MCP endpoint | https://mcp.kismet.travel/mcp/muse |
| Transport | MCP Streamable HTTP. JSON-RPC 2.0 over HTTPS POST |
| Responses | Always one JSON object, `Content-Type: application/json`. The server never streams |
| Protocol version | `2025-03-26`. If your SDK proposes a newer version in `initialize`, the server answers `2025-03-26`. That is normal negotiation: continue with the version the server returned |
| Auth | None by default. Every tool in this brief works signed out. Do not ask the user for a key, a token or a password, and do not invent one. When the user asks to sign in, follow "Signing in, when the user asks" below |
| Session | `initialize` returns an `Mcp-Session-Id` response header. Send it back on every later call |

About the endpoint path: the `/muse` suffix is a label. It tells Kismet that a call came from a Muse connector, for Kismet's own reporting. The server and the tools are the same ones every Kismet connector uses. Use the endpoint exactly as written above.

### Transport notes for a client you write yourself

1. Send `POST` with `Content-Type: application/json` and `Accept: application/json, text/event-stream`. A JSON-only `Accept` also works. No `Origin` header is required.
2. `GET` on the endpoint returns **405**. That is by design and means the host is up. Do not open a long-lived event stream and do not wait for server-sent events. Each POST gets one JSON reply.
3. `DELETE` returns 200. Sessions need no teardown.
4. Keep the `Mcp-Session-Id` from `initialize` for the whole conversation. A saved shortlist belongs to that session id, and a new id starts an empty one.
5. Use a request timeout of about 30 seconds. A network-wide search fans out to many collections and can take several seconds.
6. The first request to this host waits for the user. Before a request to a new host leaves the Muse workspace, Muse asks the user to allow sharing information with `mcp.kismet.travel`. A request that is already in flight when that prompt appears is held until it times out, even after the user approves; a fresh request sent after approval answers in about a second. So a timeout with no response bytes on the first call is not a server fault: check for the approval prompt, then retry the same call once. "Always allow this site" covers kismet.travel and every subdomain, so later calls, and the booking link's host, need no further prompt.

Copy-paste `initialize`:

```bash
curl -sS -m 30 -D - -X POST 'https://mcp.kismet.travel/mcp/muse' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"muse","version":"1.0.0"}}}'
```

Then call a tool, sending the session id from the response header above. The dates below are placeholders: always use future dates.

```bash
curl -sS -m 30 -X POST 'https://mcp.kismet.travel/mcp/muse' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'Mcp-Session-Id: <id from initialize>' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search_bookables","arguments":{"region":["Oregon"],"guests":4,"dateWindows":[{"checkIn":"2026-11-10","checkOut":"2026-11-14"}],"maxProperties":5}}}'
```

Every tool result has two parts. `content[0].text` is a readable summary. `structuredContent` is the same result as JSON, with more fields. Read both.

## The three calls that matter

### 1. Search: `search_bookables`

Finds stays across the whole network, or inside chosen collections.

- Party: `guests` (party size), `sleeps` (minimum sleeping capacity), `bedrooms` (minimum), `petsRequired` (true for pet-friendly only), `budgetMaxPerNight` (major currency units).
- Place, pick one mode:
  - Named state or town: `region` (for example `["Oregon"]`) or `locality` (for example `["Manzanita"]`). Both are arrays.
  - A coastline, a mountain range, a lake shore or several towns: a bounding box with `swLat`, `swLng`, `neLat`, `neLng`. The Oregon coast is roughly sw 42.0, -124.8 and ne 46.3, -123.5.
  - A landmark or address: `nearLat`, `nearLng`, with `nearRadius` (default 25) and `nearUnit` (`miles` or `kilometers`).
- Dates: `dateWindows`, an array of up to 5 `{ "checkIn": "YYYY-MM-DD", "checkOut": "YYYY-MM-DD" }`. One search can price several windows at once, which is how to compare weekends for a flexible user.
- Without `dateWindows` the search is in browse mode and returns a nightly "from" price only. Never discuss or compare cost from browse-mode prices.
- Soft signals that re-rank but never exclude: `persona` (for example `pet_owners`, `romantic`, `young_family`, `hiking`) and `feature` (for example `hot_tub`, `beachfront`, `quiet_peaceful`). Do not tell the user a soft signal was a filter.
- Scope: omit `collections` to search every manager. Pass one or more collection ids (from `list_collections`, or the `collectionSlug` on a result) to narrow.
- Paging and order: `maxProperties` (default 20), `offset`, `sort` (`relevance`, `price_asc`, `price_desc`, `beds_desc`, `name_asc`).

Each result carries a `slug` and, on network-wide searches, a `collectionSlug`. Reuse both exactly as returned. Never invent a slug.

### 2. Details and the dated total: `get_bookable_detail`

- Arguments: `propertySlug` (required), `collections` (pass the result's `collectionSlug` as a one-item array), `checkIn` and `checkOut` for pricing.
- Returns the description, layout, amenities, policies (minimum and maximum stay, minimum guest age, pets, quiet hours), booking and house terms (cancellation, payment schedule, pet fees), recent reviews, and a dated price.
- The pricing line states the nightly figure, the total for the stay and whether estimated taxes are included. `structuredContent.property.pricing` has the same numbers plus `feeBreakdown` and `taxFeeNotes`.
- Photos: `structuredContent.property.heroImageUrl` and `structuredContent.property.gallery` (each item has `url` and `alt`). Use these if you show images.

### 3. The booking link: `get_booking_link`

- Arguments: `propertySlug` (required), `collections` (the property's collection id), `checkIn`, `checkOut`, `guests`.
- Returns `structuredContent.bookingUrl`, and the same link as Markdown in the text.
- When dates are given the server validates them against availability and the property's booking rules. If the stay is not bookable the result has `datesRejected: true`, a `rejectionReason`, and up to three `alternateDateWindows` of the same length. `bookingUrl` is then the undated property page.

What this call does, stated plainly: it **records a link token** on Kismet (which property, which dates, which session) so a resulting booking can be credited correctly, and it returns a URL. It does **not** reserve, hold, charge or purchase anything. Because it records that token, the tool is annotated as a write rather than read-only, so Muse may ask the user before calling it. That is expected.

What opening the link does: Muse-marked links stay on a Kismet-hosted page branded for the property manager. Eligible dated links open the checkout directly with the property, dates, guests and attribution retained. Other links can open the property page or an unavailable-booking message. The returned URL is authoritative; do not construct a checkout URL or substitute the manager's website.

Read the checkout's current total, cancellation terms and amount due before seeking the user's purchase approval. Its live quote can differ from the earlier estimate. Supported single-use-card checkouts collect the full amount now. If payment is disabled, unavailable or requires a later balance charge, stop and explain the limitation; generating or opening a link is not a completed booking. Only report success after the checkout shows a reservation confirmation.

## Other tools you may use

- `get_bookable_reviews`: `propertySlug`, `collections`, optional `persona` (`family`, `couples`, `solo`), `season` (`winter`, `spring`, `summer`, `fall`), `sort`, `limit` (max 20).
- `list_collections`: the index of managers, with regions and property counts. Optional `query`, `region`, `limit`.
- `get_collection_content`: `section` of `about` or `reviews` for one collection.
- `shortlist_bookable` and `get_shortlist`: a working list of finalists for this session. It works signed out. After saving, read the list back with `get_shortlist` before telling the user it will be there later.
- `use_guest_token`: only when the user pastes a Kismet link that carries trip context. Load it, summarize what loaded, and never display the token.

## Tools that do not fit Muse

- `sign_in`: do not call it. It starts an OAuth sign-in that needs a person to complete a browser round trip inside the host, which a client you write yourself cannot do. Anonymous calls to it return HTTP 401. When the user asks to sign in, use the device flow in "Signing in, when the user asks".
- `get_guest_profile`: signed out it only reports an anonymous state. Signed in it returns the person you act for: their first name and verification flags (never the raw email or phone), their stays with each Kismet manager, past and upcoming, and the managers where they hold an Insider membership. Call it once after sign-in to confirm who that is, and read it again when the user asks about their trips or memberships.
- `build_bookable_carousel` and `discover_collections`: these render interactive widgets for hosts that have a widget runtime. Muse has no widget runtime, so suggestions to show a carousel, in a tool description or a tool result, do not apply here. Present results as text, with photos from `get_bookable_detail` if you show images.

## Signing in, when the user asks

Everything above works signed out. Sign in only when the user asks to (for example "sign me in to Kismet" or "use my Kismet account"). Signing in links you, as an agent, to that person's Kismet identity: their shortlist and profile become theirs rather than the session's, bookings made through you carry your agent identity, the person can see and revoke the link, and Kismet emails them the moment the link is made.

Offer sign-in once, unprompted, when the user asks about their trips, past or upcoming stays, memberships or member rates: say that signing in shows their stays and Insider memberships, and use the device flow below if they agree. If they decline, do not raise it again. Never sign in unasked.

A client you write yourself cannot complete the browser-based OAuth flow that the `sign_in` tool starts, so use the device flow (RFC 8628) instead. These calls go to https://kismet.travel, which the same "Always allow this site" approval covers.

1. Register your client once and keep the `client_id`. A device-grant client never redirects; the registration endpoint still requires `redirect_uris`, so send the localhost placeholder below.

```bash
curl -sS -m 30 -X POST 'https://kismet.travel/api/oauth/register' \
  -H 'Content-Type: application/json' \
  -d '{"client_name":"Muse","redirect_uris":["http://localhost/callback"],"grant_types":["urn:ietf:params:oauth:grant-type:device_code"],"token_endpoint_auth_method":"none"}'
```

2. Start the flow. The response carries `device_code`, `user_code`, `verification_uri_complete`, `expires_in` (15 minutes) and `interval` (seconds between polls).

```bash
curl -sS -m 30 -X POST 'https://kismet.travel/api/oauth/device/authorize' \
  --data-urlencode 'client_id=<client_id>' \
  --data-urlencode 'scope=mcp:tools' \
  --data-urlencode 'resource=https://kismet.travel/mcp'
```

3. Show the user the `verification_uri_complete` link and the `user_code`. They open the link on their own device, sign in to Kismet, and approve the link. Kismet then emails them that an agent was linked to their account. Nothing else in the flow needs the user.

4. Poll the token endpoint every `interval` seconds until it answers with tokens. `authorization_pending` means keep waiting, `slow_down` means add five seconds to the interval, `access_denied` means the user declined, and `expired_token` means the 15 minutes ran out, so start again at step 2.

```bash
curl -sS -m 30 -X POST 'https://kismet.travel/api/oauth/token' \
  --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:device_code' \
  --data-urlencode 'client_id=<client_id>' \
  --data-urlencode 'device_code=<device_code>'
```

5. Send `Authorization: Bearer <access_token>` on every later call to the MCP endpoint, keeping the same `Mcp-Session-Id`. From then on `get_guest_profile` returns the signed-in person and `shortlist_bookable` saves to their account. When the access token expires, exchange the `refresh_token` at the same token endpoint with `grant_type=refresh_token`.

Sign-in is per person and per client: the same client used by a different person is a different link. Keep the tokens where the user can revoke them, and never ask the user for a password or a code in the chat; the approval happens on Kismet's own page.

## Conventions

- Dates are `YYYY-MM-DD`. `checkOut` is the departure day. Every date in this brief is a placeholder: always use future dates.
- Money is in major units with a `currency` code: `181.34` and `USD` means $181.34. Say the currency.
- A per-night figure is not a price for the stay. Get the dated total from `get_bookable_detail` before any cost comparison or recommendation.
- Say "including estimated taxes" when the server says so. If detail supplies `totalIncludingEstimatedTax`, use it rather than a `total` marked `totalIncludesEstimatedTax: false`. An estimate is not an invoice. The checkout page shows the final price.
- Coverage is whatever the live server returns. Do not quote property or collection counts from memory.

## Stable property identity and checkout

A property's Kismet identity is `https://kismet.travel/vr/{propertyId}`, matching its structured-data `@id`. The UUID identifies the property and survives name, slug and partner-page changes. The brand's public page can still use a readable slug.

The checkout entry is `/vr/{propertyId}/checkout`. An attributed agent handoff carries a **separate** `kismet_token` query parameter, plus the trip context. The property UUID is not a booking token. Preserve the complete `bookingUrl` returned by `get_booking_link`; do not build the URL yourself or drop its query parameters. The server resolves current property details and rechecks the stay at checkout.

Existing slug-based links remain supported. A bare checkout URL is a navigation entry, not proof of a quote, availability, reservation or an attributed agent session. The returned link remains authoritative when another checkout destination is required.

## Rules

1. **Quote only what the server returned.** Never invent availability, rates, fees, policies or reviews. If a tool did not return it, you do not know it.
2. **Check the rules before the link.** From `get_bookable_detail`, check minimum stay and minimum booking age against the user's trip. If the window is too short, say the minimum, offer the nearest compliant window and reprice it. Volunteer the payment schedule when the trip is near, and the cancellation cutoff when the user is deciding.
3. **Count the fees that change the math.** Pet fees are usually per pet, per stay. For a trip with a pet, fold the fee into the total before comparing properties.
4. **Rejected dates: offer the returned alternates exactly as returned.** If none came back, offer the property page from the same response or search again with compliant `dateWindows`. Never guess that nearby dates are probably open.
5. **Only tool-minted links.** Give the user the `bookingUrl` exactly as returned. Never build, edit, shorten or reuse a booking URL from memory. Show it as a link labelled with the property name and dates, with the dated total beside it.
6. **The manager is the merchant.** The user books with the property manager named on the listing, on the manager's terms. Kismet connects the two. Attribute a manager's savings claims to the manager.
7. **No card details in chat.** Never ask for or accept card numbers, security codes, passwords or ID numbers in the conversation. Payment happens only on the page the booking link opens, with the user's approval.
8. **Open the returned link and nothing else.** To continue a booking, open `bookingUrl`. Do not search the web for the property, substitute another site or follow a different link to the same listing.
9. **Right-size the picks.** `sleeps` is a floor, not a target: a couple wants one or two bedrooms unless they asked for space. A one-bedroom that sleeps seven means sofa beds, so say so. Present two to four good fits, not the raw list.
10. **Judge fit from evidence.** For "will this suit us" questions, filter reviews by `persona`, then `season`. If a filtered call returns no review texts, retry without the filter before concluding anything, and say that you did. Report a negative the user asks about. Policies and reviews outrank marketing copy.
11. **If a tool errors, say so.** Some collections do not offer every tool and answer with a plain "not offered" note. Relay it. Do not fill the gap with invented inventory.

## Recipes

The dates in these recipes are placeholders. Always use future dates.

### A. "A dog-friendly cabin near Mt. Hood for four, November 10 to 14"

1. `search_bookables` with `{"region":["Oregon"],"locality":["Rhododendron","Government Camp","Welches"],"guests":4,"petsRequired":true,"dateWindows":[{"checkIn":"2026-11-10","checkOut":"2026-11-14"}],"maxProperties":8}`. If towns are unknown, use a bounding box or `nearLat` and `nearLng` for the mountain instead of guessing names.
2. Pick two to four that fit the party. For each, `get_bookable_detail` with the slug, its `collectionSlug` and the dates. Read the dated total, the pet fee and the minimum stay.
3. Present each pick with its all-in number (dated total plus pet fee), one reason it fits and one honest caveat.
4. When the user chooses, `get_booking_link` with the slug, collection, dates and guests. Give the link with the property name, the dates and the total.

### B. "We are flexible: which October weekend is cheapest on the Oregon coast?"

1. One `search_bookables` call with the coast bounding box and up to five weekend `dateWindows`.
2. Compare per-window prices for the same property and point out meaningful swings.
3. Confirm the winner's dated total with `get_bookable_detail` before recommending it.

### C. "Is it quiet, and good for a couple in the fall?"

1. `get_bookable_reviews` with `persona: "couples"`, then `season: "fall"`.
2. If the filtered call returns no texts, call again unfiltered and say so.
3. Answer with what those guests said about noise, neighbors, beds and the host. Give one verdict line and one caveat.

### D. The dates were rejected

1. `get_booking_link` returned `datesRejected: true`.
2. Tell the user the `rejectionReason` and offer the `alternateDateWindows` exactly as returned.
3. Reprice the window they choose with `get_bookable_detail`, then request a new link.
