wendily

Wendily MCP server

Version 0.6.0

Let an AI agent write trip itineraries in Wendily as it talks to a user about a trip. Every operation in the REST API is also a tool here, with the same authorization, validation, and errors.

Server URL
https://wendily.com/mcp
Transport
Streamable HTTP
Authentication
OAuth 2.1, or a key as the bearer token

Connect

ChatGPT

  1. Open Wendily in ChatGPT and click Install plugin.
  2. Sign in to Wendily when asked, and click Allow.
  3. In a new chat, type @Wendily and tell it about your trip.

Claude

  1. Open Add custom connector in Claude, which has the name and URL filled in, and click Add.
  2. Click Connect, sign in to Wendily when asked, and click Allow.

Claude Code

claude mcp add --transport http wendily https://wendily.com/mcp

Then run /mcp in a session to sign in. To use a key instead:

claude mcp add --transport http wendily https://wendily.com/mcp \
  --header "Authorization: Bearer trips_sk_…"

Other MCP clients

Add the server URL. If your client signs in over OAuth, it finds everything it needs from there, as long as its redirect URI is allowed. Otherwise, create a key on the Connections page and send it as a header. The exact format depends on your client; many use this one:

{
  "mcpServers": {
    "wendily": {
      "url": "https://wendily.com/mcp",
      "headers": {
        "Authorization": "Bearer trips_sk_…"
      }
    }
  }
}

Authentication

Every request except ping needs a credential, and every credential acts for one user. It reaches the MCP server and the REST API and nothing else, and it can read and write that user's own trips and the trips shared with them, with the role they were given. It does not expire. The user can disconnect it at any time on the Connections page, and that takes effect on the next request.

OAuth

The right choice for any client that can open a browser. A request without a credential gets 401 with a pointer to the metadata:

WWW-Authenticate: Bearer realm="wendily", resource_metadata="https://wendily.com/.well-known/oauth-protected-resource/mcp"

Redirect URIs must be https:// on chatgpt.com, claude.ai, claude.com, agent.meta.ai, muse.ai, or platform.muse.ai, or a loopback address (localhost, 127.0.0.1, [::1]). Custom URL schemes are refused, so a client that needs one should use a key. To support another app, write to team@wendily.com. The full walkthrough, with every request and error, is in auth.md.

Key

For clients without OAuth. The user adds a connection on the Connections page, which shows the key once, and you send it on every request as Authorization: Bearer trips_sk_….

Protocol

curl https://wendily.com/mcp \
  -H "Authorization: Bearer trips_sk_…" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Results and errors

A successful call returns the REST response as text and as structuredContent, so data is under data as it is in the API. An operation with nothing to return, such as a delete, returns the text Done.

Workflow

Clients show their model these instructions when they connect. If you drive the server yourself, follow them too. Calling start_trip before asking anything matters most: the user sees the trip appear in Wendily straight away, rather than nothing for the minutes the conversation takes.

Wendily stores trip itineraries. You write them here as you talk to the user about a trip.

Workflow:
1. Before asking the user anything, check for a trip already in progress: if you know of one from an earlier conversation, or list_trips shows a trip that is still being planned, offer to continue it and use its id instead of starting over.
2. Otherwise call start_trip immediately, before your first reply. It creates a placeholder so the user can see in Wendily that you are working. Pass whatever you already know; nothing is required. Keep the returned id.
3. Ask the user a focused set of questions: destination, dates and flexibility, who is travelling and any practical needs, interests and pace, budget and preferences, anything already booked.
4. When you know the trip, call update_trip with the real title, destination, dates, and a cover image. Then add days with add_day and stops with add_item, including times, descriptions, locations, practical notes, travel between stops, and a photo for each place, activity, and stay when you can find a real one.
5. Keep the itinerary current as the plan changes, and after each meaningful update tell the user briefly what you changed.

Rules:
- Never invent bookings, prices, availability, reservation codes, or image URLs. Use direct links to real, publicly reachable photos, or leave the field out.
- Dates are YYYY-MM-DD; times are HH:MM in the trip's local time.
- Only the trip owner can invite people, and invitations must be accepted by the invitee before they see the trip.

Tools

There are 19 tools, each one REST operation. The arguments are the request body plus the path ids, and tools that change something are marked so that chat apps can ask the user first.tools/list returns the full JSON Schema for each.

Trips

list_tripsGET /tripsReads

List the trips the user can access, newest first. Use this before starting a new trip to see whether one is already being planned.

Arguments (4)
kindpast | planned`planned` for a trip being planned, `past` for one already taken.
archivedexclude | include | onlyArchived trips are excluded by default.
limitinteger
offsetinteger
start_tripPOST /trips/startAdds

Call this first, before asking the user anything. Creates a placeholder trip so the user sees their agent is working. Every argument is optional. Fill the trip in later with update_trip; do not create a second trip.

Arguments (9)
titlestring
kindpast | planned`planned` for a trip being planned, `past` for one already taken.
summarystringA sentence or two about the trip.
destinationstringWhere the trip goes, e.g. "Kyoto, Japan".
startDatedateYYYY-MM-DD
endDatedateYYYY-MM-DD
plannedForstringWho the trip is for, when not the user.
coverImageUrluriA real, publicly reachable photo of the destination. Never invent URLs.
timeZonestringIANA time zone, e.g. Europe/Athens.
create_tripPOST /tripsAdds

Create a trip whose details you already know. If you are about to gather them in conversation, use start_trip instead.

Arguments (9)
titlerequiredstring
kindrequiredpast | planned`planned` for a trip being planned, `past` for one already taken.
summarystringA sentence or two about the trip.
destinationstringWhere the trip goes, e.g. "Kyoto, Japan".
startDatedateYYYY-MM-DD
endDatedateYYYY-MM-DD
plannedForstringWho the trip is for, when not the user.
coverImageUrluriA real, publicly reachable photo of the destination. Never invent URLs.
timeZonestringIANA time zone, e.g. Europe/Athens.
get_tripGET /trips/{tripId}Reads

Read one trip with all its days and items.

Arguments (1)
tripIdrequireduuidThe trip id.
update_tripPATCH /trips/{tripId}Changes

Change trip fields. Only the arguments you pass are changed. The first update to a placeholder turns it into an ordinary trip.

Arguments (10)
tripIdrequireduuidThe trip id.
titlestring
kindpast | planned`planned` for a trip being planned, `past` for one already taken.
summarystringA sentence or two about the trip.
destinationstringWhere the trip goes, e.g. "Kyoto, Japan".
startDatedateYYYY-MM-DD
endDatedateYYYY-MM-DD
plannedForstringWho the trip is for, when not the user.
coverImageUrluriA real, publicly reachable photo of the destination. Never invent URLs.
timeZonestringIANA time zone, e.g. Europe/Athens.
archive_tripPOST /trips/{tripId}/archiveChanges

Archive a trip (owner only). It stays readable but cannot be changed until restored.

Arguments (1)
tripIdrequireduuidThe trip id.
restore_tripPOST /trips/{tripId}/restoreAdds

Restore an archived trip (owner only).

Arguments (1)
tripIdrequireduuidThe trip id.

Days

add_dayPOST /trips/{tripId}/daysAdds

Add a day to a trip. dayNumber is 1-based and unique within the trip.

Arguments (6)
tripIdrequireduuidThe trip id.
dayNumberrequiredinteger1-based position within the trip.
datedateYYYY-MM-DD; optional while dates are unsettled.
titlestring
summarystringWhat the day is about, in a sentence.
notesstringPractical advice: tickets, timing, what to bring.
update_dayPATCH /trips/{tripId}/days/{dayId}Changes

Change a day. Only the arguments you pass are changed.

Arguments (7)
tripIdrequireduuidThe trip id.
dayIdrequireduuidThe day id, from get_trip or add_day.
dayNumberinteger1-based position within the trip.
datedateYYYY-MM-DD; optional while dates are unsettled.
titlestring
summarystringWhat the day is about, in a sentence.
notesstringPractical advice: tickets, timing, what to bring.
delete_dayDELETE /trips/{tripId}/days/{dayId}Changes

Delete a day and everything on it.

Arguments (2)
tripIdrequireduuidThe trip id.
dayIdrequireduuidThe day id, from get_trip or add_day.

Stops

add_itemPOST /trips/{tripId}/days/{dayId}/itemsAdds

Add a stop to a day: a place, activity, stay, transport leg, or note. Include times, a description, location, practical notes, travel from the previous stop, and a real photo URL when you have them.

Arguments (22)
tripIdrequireduuidThe trip id.
dayIdrequireduuidThe day id, from get_trip or add_day.
kindrequiredplace | activity | stay | transport | note
titlerequiredstring
positioninteger0-based order within the day; omit to append.
descriptionstringTraveller-facing context about the stop.
notesstringPractical advice: tickets, timing, what to bring.
startTimestringHH:MM, 24-hour, local to the trip.
endTimestringHH:MM, 24-hour, local to the trip.
locationNamestring
locationAddressstring
latitudenumber
longitudenumber
urluriThe place or activity website.
imageUrluriA real, publicly reachable photo of the stop. Never invent URLs.
travelModewalk | transit | taxi | drive | bicycle | ferry | flight | otherHow to reach this stop from the previous one.
travelDurationMinutesinteger
travelDistanceMetersinteger
providerstringAirline, hotel, operator.
phonestring
reservationCodestringOnly if the user gave it to you.
bookingUrluri
update_itemPATCH /trips/{tripId}/days/{dayId}/items/{itemId}Changes

Change a stop. Only the arguments you pass are changed.

Arguments (23)
tripIdrequireduuidThe trip id.
dayIdrequireduuidThe day id, from get_trip or add_day.
itemIdrequireduuidThe item id, from get_trip or add_item.
kindplace | activity | stay | transport | note
titlestring
positioninteger0-based order within the day; omit to append.
descriptionstringTraveller-facing context about the stop.
notesstringPractical advice: tickets, timing, what to bring.
startTimestringHH:MM, 24-hour, local to the trip.
endTimestringHH:MM, 24-hour, local to the trip.
locationNamestring
locationAddressstring
latitudenumber
longitudenumber
urluriThe place or activity website.
imageUrluriA real, publicly reachable photo of the stop. Never invent URLs.
travelModewalk | transit | taxi | drive | bicycle | ferry | flight | otherHow to reach this stop from the previous one.
travelDurationMinutesinteger
travelDistanceMetersinteger
providerstringAirline, hotel, operator.
phonestring
reservationCodestringOnly if the user gave it to you.
bookingUrluri
delete_itemDELETE /trips/{tripId}/days/{dayId}/items/{itemId}Changes

Remove a stop from a day.

Arguments (3)
tripIdrequireduuidThe trip id.
dayIdrequireduuidThe day id, from get_trip or add_day.
itemIdrequireduuidThe item id, from get_trip or add_item.

Sharing

list_membersGET /trips/{tripId}/membersReads

List who has access to a trip and any pending invitations (owner sees pending).

Arguments (1)
tripIdrequireduuidThe trip id.
invite_memberPOST /trips/{tripId}/membersSends email

Invite someone by email as an editor or viewer (owner only). They must accept in Wendily before they can see the trip.

Arguments (3)
tripIdrequireduuidThe trip id.
emailrequiredemailThe person to invite.
rolerequirededitor | viewerEditors can change the itinerary; viewers can only read it.
leave_tripDELETE /trips/{tripId}/members/meChanges

Remove the user’s own access to a trip they were invited to. Owners cannot leave.

Arguments (1)
tripIdrequireduuidThe trip id.
update_memberPATCH /trips/{tripId}/members/{memberId}Changes

Change a collaborator’s role (owner only).

Arguments (3)
tripIdrequireduuidThe trip id.
memberIdrequireduuidThe member id, from list_members.
rolerequirededitor | viewerEditors can change the itinerary; viewers can only read it.
remove_memberDELETE /trips/{tripId}/members/{memberId}Changes

Remove a collaborator or cancel a pending invitation (owner only).

Arguments (2)
tripIdrequireduuidThe trip id.
memberIdrequireduuidThe member id, from list_members.
resend_invitationPOST /trips/{tripId}/members/{memberId}/resendSends email

Send a pending invitation email again (owner only). Refused within ten minutes of the last send and after four sends.

Arguments (2)
tripIdrequireduuidThe trip id.
memberIdrequireduuidThe member id, from list_members.

Testing

There is no separate sandbox. Trips are private until shared, so test against the live server and archive test trips when you are done.

See also