🌍 TravelLog

TravelLog MCP tool reference

Last updated: October 28, 2026 · server version 1.4.0

TravelLog's official MCP server lets an AI assistant the user connects read their trips, notes, expenses, travel history and saved places and, if the user allows it, create trips, file suggestions, add the user's own bookings and want-to-go places, and edit or remove itinerary entries. Setup for end users is on Connect TravelLog to your AI assistant. This page is the technical reference for each tool.

Endpoint
https://travellogapp.web.app/mcp

Streamable HTTP (single JSON responses, stateless) · MCP 2026-07-28, 2025-11-25 and 2025-06-18 · OAuth 2.1 with PKCE (S256) · protected-resource metadata at /.well-known/oauth-protected-resource/mcp · dynamic client registration, client ID metadata documents and pre-registered clients · or a personal access key (tlpat_…) for clients that only take a static bearer token

Tools: list_trips get_itinerary list_suggestions create_trip add_suggestions add_booking update_entry remove_entry get_wishlist add_to_wishlist get_travel_history get_trip_notes get_trip_expenses add_entry add_expense add_day_note add_been_place import_trips accept_suggestion dismiss_suggestion · request_account_deletion · Without the app · Let an assistant set the user up · Access keys (Grok, custom bots)

Setup and permissions

ScopeConsent screen wordingTools
trips:readSee your trips, itineraries, notes, expenses, travel history and saved placeslist_trips, get_itinerary, list_suggestions, get_wishlist, get_travel_history, get_trip_notes, get_trip_expenses, request_account_deletion
trips:writeCreate and import trips, add the entries, expenses, notes and places you ask for, and edit or remove entries in trips you can editcreate_trip, update_entry, remove_entry, add_to_wishlist, add_entry, add_expense, add_day_note, add_been_place, import_trips, accept_suggestion
suggestions:writeAdd suggestions to your trips, and dismiss themadd_suggestions, dismiss_suggestion
bookings:writeAdd your own bookings to your tripsadd_booking

tools/list shows only the tools the token's scopes allow. Calling a tool outside them returns HTTP 403 with error="insufficient_scope" and the scope needed. A Read only link (or a read-only access key) sees the seven read tools plus request_account_deletion (which only emails the user a link).

Classes

Every tool sets all four MCP annotations explicitly: readOnlyHint, destructiveHint, idempotentHint: true (backed by clientRequestId) and openWorldHint: false (TravelLog's own data only). None involves payments or sending.

Conventions

Tools

list_trips Read

Scope
trips:read
Does
Lists the user's trips (their own and ones shared with them), newest first, paged.
Inputs
  • cursor (optional): nextCursor from the previous page
  • limit (optional): 1–50, default 20
Outputs
trips[]: tripId, name, destinations[], startDate, endDate, days, role (owner, editor or viewer), entryCount; total; nextCursor when there is more.
Side effects
None.
Errors
invalid (a cursor this server didn't issue), plus the common errors.

get_itinerary Read

Scope
trips:read
Does
One trip's itinerary, optionally for a range of days.
Inputs
  • tripId (required)
  • fromDay, toDay (optional, 1-based)
Outputs
trip; days[] of {day, date, entries[], notes[]}; unscheduled[] (full range only); suggestionBatches[] (batchId, label, from, count). Each entry: id, title, category, date, tz, start, end, endDate, endTz, startUtc, endUtc, place {name, address, lat, lng}, from/to {name, code} (transit), flightNumber, notes, group, status (when not PLANNED). Up to 400 entries; truncated: true beyond that (narrow the day range).
Side effects
None.
Errors
not_found (no such trip, or not shared with this user), invalid (fromDay after toDay).

list_suggestions Read

Scope
trips:read
Does
The pending suggestion batches this user can see on a trip, so the assistant can check before retrying or adding more.
Inputs
tripId (required)
Outputs
batches[]: batchId, label, from, count, createdAt, items[] (id, title, category, day, start, end, tz, place, why, groupId, groupName).
Side effects
None.
Errors
not_found.

create_trip Write

Scope
trips:write
Does
Creates a new, empty trip owned by the user. Nothing else is changed.
Inputs
  • name (1–120 chars), destinations[] (1–10), startDate, endDate (at most a year apart), clientRequestId: required
  • notes (optional, ≤ 2000)
Outputs
status, tripId, trip.
Side effects
A new trip appears in the user's TravelLog on every device.
Statuses
applied · duplicate (replay: the same trip, nothing new).
Errors
invalid (bad dates, end before start, too long).

add_suggestions Write

Scope
suggestions:write
Does
Files the assistant's ideas as one batch of pending suggestions labelled "From <assistant name>". Nothing becomes a real entry until the user adds it in the app.
Inputs
  • tripId, clientRequestId, items[] (1–40): required
  • each item: title and day (required); category, start, end, tz (tz required with a time), place {name, address, lat, lng, types[]}, why (≤ 200), groupId, groupName
Outputs
status, batchId, label, count, perDay[] {day, count}, skipped[] {index, title, reason}.
Side effects
A suggestion batch on the trip, visible only to this user (also on a shared trip). Health-care places are not accepted and come back in skipped with reason health_place.
Statuses
applied · duplicate · skipped (every item was filtered; nothing written).
Errors
forbidden (viewer), not_found, invalid (day outside the trip, a time without tz, more than 40 items).

add_booking Write

Scope
bookings:write
Does
Adds one of the user's own confirmed bookings (flight, stay, restaurant, event, transit) as a real itinerary entry. Not for ideas.
Inputs
  • tripId, category, title, startDate, tz, clientRequestId: required
  • startTime, endDate, endTime, endTz (arrival zone), confirmationCode, place, from/to {name, code}, flightNumber, notes: optional
Outputs
status, entryId, day, entry (as in get_itinerary).
Side effects
A new entry on the trip, seen by every member on their next sync.
Statuses
applied · duplicate (replay, or the same confirmation code and start time is already on the trip: returns the existing entry and adds nothing).
Errors
forbidden (viewer), not_found, invalid (bad zone or date, ends before it starts).

update_entry Write · destructive

Scope
trips:write
Does
Changes an existing itinerary entry (any entry get_itinerary returns) on a trip the user owns or can edit. Only the fields passed change. It overwrites the user's data, so the assistant must say what will change and get the user's OK first.
Inputs
  • tripId, entryId, clientRequestId: required
  • at least one of: title, category, notes ("" clears), place (replaces the whole place), startDate, startTime, endDate, endTime, tz, endTz, status (PLANNED, VISITED, COMPLETED, SKIPPED), confirmationCode, flightNumber (flights), from/to (transit)
Time rules
  • Moving the start keeps the entry's length unless an end is passed.
  • An end time earlier than the start, with no endDate, is read as the next day.
  • Passing only tz keeps the wall-clock time and re-reads it in that zone.
  • tz is required when the entry has no zone yet. A startDate schedules an unscheduled idea.
Outputs
status, entryId, changed[] (stored field names), entry (after the change); on conflict also notApplied[] and message.
Side effects
The entry changes for every member of the trip on their next sync. Never touches other members' private entries, anyone's own visit records, expenses or photos.
Statuses
applied · duplicate (replay) · conflict (someone changed the same field at the same moment and their change was kept; read the entry again and ask the user).
Errors
forbidden (viewer), not_found (no such entry, a private entry, or already removed; an edit never brings back a removed entry), not_editable (times recorded by location tracking, a cruise's times, or a stop's status on a shared trip, which each person marks for themselves in the app), invalid (nothing to change, from/to on a non-transit entry, ends before it starts).

remove_entry Write · destructive

Scope
trips:write
Does
Removes one itinerary entry from a trip the user owns or can edit, on every device and for every member of a shared trip. It deletes the user's data, so the assistant must name the entry and get the user's explicit OK before calling it.
Inputs
tripId, entryId, clientRequestId: all required.
Outputs
status, entryId, removed (the entry as it was, so the assistant can tell the user what went); on conflict, entry and message.
Side effects
The entry is deleted through the normal sync path, so every TravelLog app removes it on its next sync. Other members' private entries can't be removed.
Statuses
applied · duplicate (replay, or the entry was already removed) · conflict (it was edited at the same moment and kept).
Errors
forbidden (viewer), not_found (no such entry, or a private entry).

get_wishlist Read

Scope
trips:read
Does
The user's saved places: the been list and the want to go list, with their own why-notes.
Inputs
list (optional): all (default), been or want
Outputs
synced (false until the user's lists have synced to their account), been[], wantToGo[]: each name, country, lat, lng, note, savedAt (YYYY-MM-DD); counts {been, wantToGo}.
Side effects
None.
Errors
invalid (unknown list).

add_to_wishlist Write

Scope
trips:write
Does
Adds one place to the user's want-to-go list, with an optional note. It never edits or removes a saved place.
Inputs
  • name (1–200), lat, lng, clientRequestId: required
  • country (≤ 120), note (≤ 2000): optional
Outputs
status, place {name, country, lat, lng, note, savedAt}, list (want, or been when the place was already on the been list).
Side effects
The place appears on the user's Places → Want to go list on their devices' next sync.
Statuses
applied · duplicate (replay, or the same place, by name within about 10 km, is already on either list: nothing changes).
Errors
not_editable (the user's lists haven't synced to TravelLog yet: they open Profile → Places in the app once), invalid (bad coordinates, a health-care place, or 500 places already saved).

get_travel_history Read

Scope
trips:read
Does
Where the user has travelled, computed when called from the trips synced to their account (their own and ones shared with them). Nothing is stored.
Inputs
limit (optional): past trips to list, 1–100, default 50
Outputs
asOf, tripCount (trips that have started), daysAway (days on those trips up to today, overlaps counted once), countries[] and cities[] ({name, trips}, most trips first, from the addresses of the places on each trip), trips[] (as list_trips, plus cities[], countries[], newest first), upcoming[], upcomingCount, partial when places were read for only the newest 60 trips.
Side effects
None. No location-tracking data, no times, and only the shared (non-private) places of each trip.
Errors
The common errors.

get_trip_notes Read

Scope
trips:read
Does
One trip's notes: the trip-level notes and the day notes.
Inputs
tripId (required)
Outputs
trip, tripNotes, days[] of {day, date, notes[]}.
Side effects
None.
Errors
not_found.

get_trip_expenses Read

Scope
trips:read
Does
One trip's expenses as its Expenses tab shows them, for a trip the user owns or can edit.
Inputs
tripId (required)
Outputs
trip; expenses[]: id, title, amountCents (minor units), currency, category, date, day, paidBy (the trip roster name; empty = not set), split {mode, everyone, people[]} with mode EQUAL (people[].name when not everyone), PERCENT (percent), EXACT (amountCents) or ITEMIZED (personalCents; the rest is split evenly); totals[] of {currency, totalCents, count}.
Side effects
None. Receipts, line items, exchange rates and settlement payments are not included.
Errors
forbidden (a viewer of a shared trip), not_found.

Adding what the user asks for (server 1.4.0)

These write directly into the user's TravelLog, so call them only when the user explicitly asked you to add that thing (for example "log my trip to Rome", "add the €32 tickets", "import my travel spreadsheet"). Your own plans and ideas always go in with add_suggestions. The descriptions and the server instructions say the same.

add_entry Write

Scope
trips:write
Does
Adds one stop or place (or a transit leg) to a day of a trip the user owns or can edit, as a real itinerary entry.
Inputs
  • tripId, title (1–120), tz (IANA zone of the place), clientRequestId: required
  • date (YYYY-MM-DD) or day (1-based): required, inside the trip
  • category (default OTHER), startTime / endTime (HH:MM; an end before the start is the next day), endDate (a stay's check-out day, or with endTime), endTz, place {name, address, lat, lng}, notes (≤ 2000), status, transit from / to / flightNumber, confirmationCode: optional
Status rule
visited, planned or skipped. Default: visited on a trip that has ended, otherwise planned; never visited for a day that hasn't happened. Transit is stored as completed. On a trip shared with others, a visited or skipped stop is recorded as this user's own visit (yourVisit in the result) and the stop stays the group's plan, exactly as when one person marks a shared stop in the app. Another member's visit is never written.
Outputs
status, entryId, day, entry (as get_itinerary shows it), yourVisit (shared trips).
Statuses
applied · duplicate (replay, or the same title at the same time is already on the trip).
Errors
forbidden (viewer), not_found, invalid (date outside the trip, bad time or zone, …).

add_expense Write

Scope
trips:write
Does
Adds one expense to a trip the user owns or can edit, as on the trip's Expenses tab.
Inputs
  • tripId, title, amount (normal units, e.g. 12.50), currency (3 letters), clientRequestId: required
  • category (FOOD, LODGING, TRANSPORT, ACTIVITIES, SHOPPING, OTHER), date (inside the trip; default its first day), paidBy (default "me"), split {mode: equal|exact, people: [{name, amount?}]}: optional
People
paidBy and the split may name only people already on the trip (the names get_trip_expenses shows) or "me" for the user. Equal splits between everyone unless people are listed; an exact split must add up to the amount.
Outputs
status, expenseId, expense (as get_trip_expenses shows it).
Statuses
applied · duplicate (replay, or the same title, amount, currency and date).
Errors
forbidden, not_found, invalid (someone not on the trip, a split that doesn't add up, …).

add_day_note Write

Scope
trips:write
Does
Adds a note to one day. A day has one note: if it already has one, the text is added at the end, never replacing it.
Inputs
tripId, text (1–2000), date or day, clientRequestId.
Outputs
status, day, date, text (the day's whole note).
Statuses
applied (new note) · appended · duplicate (replay, or the text is already there) · conflict (someone edited the note at the same moment; theirs was kept).

add_been_place Write

Scope
trips:write
Does
Adds one place to the user's been list. Same inputs, outputs and rules as add_to_wishlist; it never edits or removes a saved place.
Errors
not_editable (an account made in the app whose lists haven't synced yet; an account made on the website has no such wait), invalid.

import_trips Write

Scope
trips:write
Does
Bulk import of the user's own trips (travel history from notes, a spreadsheet or another app). Each trip is created whole or not at all (one sync transaction per trip; several trips per batch).
Inputs
clientRequestId and trips[] (1–20), each: name, destinations[], startDate, endDate (required); key (your stable id for the trip; default its position), notes, entries[] (≤ 80, the add_entry fields without tripId), dayNotes[] {date | day, text} (≤ 15; two for one day are joined).
Outputs
status (applied, duplicate, partial or failed), results[] one per trip {index, key, status, tripId?, entries?, dayNotes?, reason?, message?, fields?}, counts.
Per-trip statuses
applied · duplicate (replay with the same clientRequestId + key) · exists (the user already has a trip with this name and dates: named in tripId, nothing added to it) · invalid (with the field) · skipped (daily_limit: import it tomorrow with the same clientRequestId and key) · failed. Retry only the trips that didn't go in.
Limits
Its own, per user (every assistant and key together): 10 imports an hour, 200 trips a day. It does not use the 20 writes an hour of the other tools.

accept_suggestion Write

Scope
trips:write
Does
Turns pending suggestions into planned entries, exactly as Add in the app: the entry (at the idea's day and slot, its place and why as notes) and the removal of the suggestion are one change. Only when the user asked to add these ideas.
Inputs
tripId, suggestionIds[] (1–40, ids from list_suggestions), clientRequestId.
Outputs
status, results[] {suggestionId, status, entryId?, entry?}.
Statuses
applied · duplicate (already added: the same entry) · not_found (dismissed, or not this user's) · partial.

dismiss_suggestion Write · destructive

Scope
suggestions:write
Does
Removes pending suggestions without adding them, exactly as Dismiss in the app. Name them to the user and get their OK first. Entries are never touched.
Inputs / outputs
As accept_suggestion; duplicate = already gone.

request_account_deletion Write · destructive

Scope
trips:read (any connected assistant or key may start it; only the user can finish it)
Does
Deletes nothing. TravelLog emails the account's own address a sign-in link. It opens /account?action=delete, which signs the user in, lists exactly what will be deleted (profile, trips they own — for everyone on them — trips they leave, places, connected assistants and keys, the sign-in) and needs one tap on Delete my account. That runs the same deletion as the apps. Afterwards every key, token and session of the account stops working, including the one that asked. Tell the user it's permanent and get their OK first.
Inputs
none
Returns
{status: "sent", deleted: false, sentTo: "a•••@example.com"}. One link a minute, 3 a day (rate); no_email if the account has no email address.

Using TravelLog without the app

A person doesn't need the TravelLog app to use this connector.

  1. Create an account at travellogapp.web.app/account, or on the sign-in page their assistant opens (/connect): email and password, or Google. They accept the Terms and Privacy Policy, confirm they are at least 18, and pick a name and @handle — the same account the app makes.
  2. Verify the email (email sign-ups). Until then the connector is read-only for that account and every write tool returns email_unverified.
  3. Connect an assistant (OAuth), or create an access key at /account for a bot.
  4. Ask the assistant to log past trips (import_trips, add_entry, add_expense, add_day_note, add_been_place), plan new ones (create_trip, add_suggestions) and fold ideas in (accept_suggestion, dismiss_suggestion).
  5. Disconnect assistants, manage keys, export or delete the account at /account. If they install the app later and sign in with the same account, everything is there.

Let an assistant set the user up (server 1.5.0)

An assistant can create the user's TravelLog account for them. The user's only step is tapping the link in one email.

Use the sign-up URL https://travellogapp.web.app/mcp?signup=1 with no token. It serves a signed-out MCP session (the server returns an Mcp-Session-Id on initialize) that lists only three tools. The plain /mcp URL is unchanged: without a token it answers 401, so clients that sign in with OAuth keep doing that (and can create an account on the sign-in page).

ClientWhat it sees
Claude, ChatGPT, grok.com connector on /mcpUnchanged: 401 → OAuth → TravelLog's sign-in page, where the user signs in or creates an account. The sign-up tools never appear.
The same clients on /mcp?signup=1Connect without signing in and see get_started, create_account, check_account_status. When the account is active, the same connection is signed in: the server sends notifications/tools/list_changed in that response, and the next tools/list returns the full set. A later, new connection on this URL starts signed out again, so for everyday use switch the connector to /mcp (OAuth) or use the access key.
xAI (Grok) API remote MCP tool, custom botsserver_url = the sign-up URL, no authorization. Within one response, if the client resends the Mcp-Session-Id (MCP clients must), the bot gets the full tools after the claim. Save the accessKey that check_account_status returns and put it in authorization (with /mcp) from the next request on.
Clients that send no session id (stateless, or the 2026-07-28 revision)The same three tools; the claim returns the key with connectionUpgraded: false. Reconnect with the key as the bearer.

get_started Read · signed out

Does
The steps below, the Terms and Privacy links to show the user, the limits, and what to do if they already have an account.

create_account Write · signed out

Inputs
email, displayName (≤ 50), handle (required: 3–20 letters, numbers or _, the user's choice), acceptedTerms: true, ageConfirmed: true. Ask the user for each first; pass the two trues only if the user explicitly said yes. Never fill them in yourself.
Does
The same sign-up as the website and the apps (one server function, the same rules and records): a TravelLog account with that email (no password), the profile and @handle, the record that the user accepted the Terms and confirmed they are 18+ (made by an assistant, with its name). Firebase Authentication then emails a verification link.
Returns
{status: "pending", signupId, claimSecret, handle, expiresAt}. The claimSecret is shown once, valid 24 hours; keep it out of the chat.
Refusals
invalid (fields[]: email, displayName, handle, acceptedTerms, ageConfirmed), handle_taken (with 2–3 free suggestions: let the user choose), account_exists ("an account already exists; sign in instead" — it never says how), rate, unavailable.

check_account_status Write · signed out

Inputs
signupId, claimSecret
Returns
pending until the user taps the email link; then active with accessKey (a Read & write key named "<assistant> (created at sign-up)", 90 days) exactly once and connectionUpgraded; after that claimed; after 24 hours expired. Five wrong secrets lock the claim. Poll at most every 30 seconds.

Signing in later. The account has no password. On the web: /account → Email me a sign-in link, or Forgot password? to set one. In the app: the same email. An account whose email nobody verifies within 72 hours is deleted.

Limits (signed out). create_account: 3 a minute and 10 a day per network, 3 a day per email address (Gmail dots and +tags count as one), and a daily cap for all of TravelLog. check_account_status and get_started: 30 a minute per network. New sessions: 20 a minute per network. Networks and email addresses are counted by salted one-way hashes kept 2 days.

xAI (Grok) API: let the bot sign the user up.

{ "model": "grok-4.7", "input": [{"role": "user", "content": "Set me up with TravelLog. My email is jane@example.com, name Jane, handle jane_travels. I accept the Terms and Privacy Policy and I'm over 18."}], "tools": [{ "type": "mcp", "server_url": "https://travellogapp.web.app/mcp?signup=1", "server_label": "travellog" }] }

Access keys (Grok and custom bots)

Some MCP clients can't run OAuth and only take a fixed bearer token, for example the xAI (Grok) API's remote MCP tool or a bot you write yourself. For those, the user creates a personal access key in the TravelLog app: Settings → Connected apps → Create access key. They name it, choose Read only (trips:read) or Read & write (every scope above), and an expiry (30 days, 90 days, 1 year or never; 90 days by default).

xAI (Grok) API, remote MCP tool. In the Responses API (POST https://api.x.ai/v1/responses), add an mcp tool. xAI documents server_url and server_label (required) and authorization ("a token that will be set in the Authorization header"), plus optional server_description, allowed_tools and headers; xAI's own SDK names the last two allowed_tool_names and extra_headers. Put the key itself in authorization:

{ "model": "grok-4.7", "input": [{"role": "user", "content": "Look at where I've been and what's on my want-to-go list, and suggest my next trip."}], "tools": [{ "type": "mcp", "server_url": "https://travellogapp.web.app/mcp", "server_label": "travellog", "server_description": "My TravelLog trips, places lists and travel history", "authorization": "tlpat_…" }] }

xAI's docs don't say whether authorization should include the word "Bearer". TravelLog accepts the key with or without it, so "tlpat_…" and "Bearer tlpat_…" both work. Use the model name xAI currently lists. To keep a bot read-only, use a Read only key (or allowed_tools).

Other clients that take a server URL plus a header: URL https://travellogapp.web.app/mcp, header Authorization: Bearer tlpat_….

Errors

A tool error is a normal result with isError: true and structuredContent {v, reason, message, …}. The message is a plain sentence the assistant can pass on.

reasonMeaningWhat to do
rateOver a limit below. Includes resetsAt (ISO 8601 UTC) for when the window resets.Tell the user when to try again; don't retry before resetsAt.
not_foundNo such trip or entry, or not visible to this user.Re-read with list_trips / get_itinerary.
forbiddenThe user is a viewer on this shared trip (writes, and get_trip_expenses).Only read it.
not_editableThis field of this entry is managed in the app (tracking times, cruises, a shared stop's status), or the user's places list hasn't synced yet.Tell the user to change it in TravelLog (or open Places once).
invalidBad input. Includes fields[].Fix the named field.
email_unverifiedThe account was created on the website and its email address isn't verified yet (writes only; reads work).Ask the user to open the verification email, or resend it at /account.
account_existscreate_account: that email already has a TravelLog account.Help the user sign in (OAuth on /mcp, or /account).
handle_takencreate_account: the @handle is taken. Includes suggestions[].Let the user pick one, call again.
sign_up_firstA signed-out session called a tool that needs an account.Call get_started.
signed_inA signed-in session called a sign-up tool.Use the full tools.
no_emailrequest_account_deletion: the account has no email address.Delete in the app or at /account.
unavailableTravelLog paused the connector, this tool, this assistant or access keys, or the account is gone.Try later; don't loop.

HTTP-level: 401 (no, expired, revoked or wrong-audience token: re-run OAuth; or an unknown, revoked or expired access key: make a new one in the app), 403 (insufficient_scope, or an Origin that isn't allowed), 400 (-32020 header mismatch, -32022 unsupported protocol version), 405 (GET or DELETE on /mcp), 404 (an unknown or expired Mcp-Session-Id on the sign-up URL, or one whose account was deleted: start a new session), 429 (too many new sessions from one network).

Limits

Data and privacy

What a connected assistant can see and do is set out in section 3a of our Privacy Policy; the connector terms are in section 7a of our Terms. We keep a 90-day record of each request (which assistant, which tool, whether it worked, when) without any trip content.

Security contact

Report a vulnerability or a suspected incident to armgardtj@outlook.com (subject "Security"). We can switch off the connector, a single tool or a single assistant at once without users having to reconnect.

TravelLog · MCP tool reference · Connect your AI assistant · Support and security: armgardtj@outlook.com