Skip to Content

Tools

The Traveler.md MCP (Model Context Protocol) server exposes eight tools across two areas: traveler preferences and trips. Each tool requires a specific OAuth scope. See Authentication for the scope catalog.

The server has no search tool, no pricing tool, and no booking tool. It holds the traveler’s memory. Every tool below either returns what the traveler has already recorded or writes something new into it. Your agent does the recommending and booking after it reads the profile. See It’s memory, not a travel agent.

Scope summary

ToolRequired scopeWhat it allows
read_profileprofile.readRead your Traveler.md
create_profileprofile.createCreate your Traveler.md
update_profileprofile.updateUpdate your Traveler.md
list_tripstrip.listList your trips
read_triptrip.readRead a trip
create_triptrip.createCreate a trip
update_triptrip.updateUpdate a trip
archive_triptrip.updateArchive a trip

archive_trip uses trip.update. It has no archive scope of its own. Archiving is a reversible trip change, so a client that can rewrite a trip can also file it away. A new scope would also be missing from every token already issued, so the capability would disappear until each traveler consented again.

Concepts shared across tools

These conventions apply to every tool:

  • Structured sections. A Traveler.md or Trip.md is a map of section name to array of sentence strings, for example flight_preferences: ["Prefers aisle seats", "Avoids red-eyes"]. Reads return this sections map. Writes send it back in the same shape. The Traveler.md and Trip.md specs fix the section names. The server rejects unknown names.

  • Section-level merge on writes. create_profile, update_profile, create_trip, and update_trip replace the full sentence list of each section you include. They leave every section you omit unchanged. To leave a section alone, omit it.

  • Clearing a section is opt-in. A section sent as an empty list is erased. There is no delete tool and no server-side undo. So update_profile and update_trip reject an empty list unless the same call sets allow_clear_sections: true. The validation error names every section the call would have emptied. The create tools have no such flag, because a first write has nothing to clear.

  • Optimistic concurrency with version_hash. Reads return a version_hash, a 64-character SHA-256 hex string. Every write response also returns the new version_hash. Pass the last value you saw as expected_version_hash on an update. If another writer changed the record in the meantime, the call fails with CONFLICT and the message states the current hash. Read again before you retry. Sections are replaced wholesale, so sending the same body with the new hash overwrites the other writer’s version of every section in it. Retry directly with the new hash only when your write cannot collide with theirs. archive_trip changes no content, so it takes no expected_version_hash.

  • include_markdown (optional, default false). Reads return the structured sections and omit the rendered markdown by default. Set include_markdown: true on any read or content write to also receive rendered_markdown.

  • idempotency_key (optional). Any unique string of 1 to 255 characters on a create or update. For one hour, the same key with the same body returns the byte-identical original response and does not write again. The server rejects a key that contains control characters or only whitespace. The same key with a different body fails with IDEMPOTENCY_MISMATCH. The same key sent while the first call is still running fails with IDEMPOTENCY_IN_PROGRESS. archive_trip takes no key.

  • Sentence caps. Every Traveler.md section accepts up to 100 sentences, except profile_overview, which accepts 10. Every Trip.md section accepts up to 100 sentences, except summary and purpose, which accept 10 each. A write that exceeds a cap fails as a whole with a validation error that names the section and its limit. Nothing is stored. The cap applies to the full section you send, so an existing section plus one new sentence must fit inside it.

  • Sensitive data is rejected. All four write tools check sections and trip events. A write that contains a payment card number, a passport or ID number, a known traveler number, or a door or access code fails with VALIDATION_ERROR, and the message names the section and the kind of data:

    Traveler.md never stores payment card numbers, passport or ID numbers, known traveler numbers, or door and access codes. Remove the number and resend; the sentence may name the passport country, card product, or trusted traveler program by name only.

Errors

A failed tool call returns a normal tool result with isError: true. It does not use an HTTP error status. structuredContent.error holds a code, a message, and sometimes details. code is one of these values:

CodeMeaning
CONFLICTA stale expected_version_hash (details.currentHash holds the current hash), a create_profile for a profile that already exists, or a trip slug already in use (no hash in the message).
NOT_FOUNDThe trip does not exist, is not visible to this client, or is archived.
VALIDATION_ERRORThe arguments failed the schema: an unknown section, a sentence cap, an empty section without allow_clear_sections, sensitive data, an invalid cursor, or an end_date before the start_date.
IDEMPOTENCY_MISMATCHThe idempotency_key was already used with a different request body.
IDEMPOTENCY_IN_PROGRESSAnother request with the same idempotency_key is still running.
UNAUTHORIZEDThe traveler revoked this connection or narrowed its scopes after consent, or a share grant was revoked or expired (details.reason).
FORBIDDENThe token never had the required scope (details.required_scope), or the traveler withheld a section from this client (details.disallowedSections).
SERVICE_UNAVAILABLEA temporary server problem. Retry after a short wait.
INTERNALAn unexpected server error.

Some rejections happen at the HTTP transport, before any tool runs:

  • 401 for a missing or invalid access token.
  • 429 with the code RATE_LIMITED, a retry-after header, and x-ratelimit-limit, x-ratelimit-remaining, and x-ratelimit-reset headers.
  • 503 with the code SERVICE_UNAVAILABLE and retry-after: 2, while a server instance shuts down or when the server cannot check rate limits.

See Debugging for what to do about each one.

Traveler preferences

read_profile

Returns the authenticated traveler’s Traveler.md as structured sections.

FieldTypeNotes
sectionsarrayOptional. Section names to return, at least one. Omit for every section.
include_markdownbooleanOptional (default false). Also return the markdown.

Required scope: profile.read

Returns: sections (the parsed section map), a version_hash to pass back on a later update, and spec_version and renderer_version. rendered_markdown is included only when include_markdown is true.

If no profile exists yet, the call succeeds with a null version_hash and an empty sections map. That null is the signal to create the profile. It is not an error. Do not decide that no profile exists only because sections is empty.

The sections input narrows the returned sections map only. Use it when a request needs one or two of the twenty profile sections:

  • version_hash still covers the whole document, so a filtered read is a safe basis for an update that names other sections.
  • rendered_markdown, when requested, is always the complete document.
  • A section the traveler has withheld from this client stays absent whether or not you name it.

create_profile

Creates a Traveler.md for the authenticated traveler. Use it when read_profile returns a null version_hash. It is for the first write only. If a profile already exists, it fails with CONFLICT and the message names the current hash. Use update_profile with that hash instead.

FieldTypeNotes
sectionsobjectRequired. Section name to array of sentences.
idempotency_keystringOptional. Unique string for safe retries.
include_markdownbooleanOptional (default false).

Required scope: profile.create

Returns: The new profile as sections, its version_hash, and spec_version and renderer_version.

update_profile

Updates the traveler’s Traveler.md. Sections you include are replaced wholesale. Sections you omit are unchanged.

FieldTypeNotes
sectionsobjectRequired. Section name to array of sentences.
expected_version_hashstring or nullRequired. The version_hash from your last read or write of the profile. Send null to create the profile when none exists yet.
allow_clear_sectionsbooleanOptional (default false). Required to send any section as an empty list.
idempotency_keystringOptional. Unique string for safe retries.
include_markdownbooleanOptional (default false).

Required scope: profile.update. An expected_version_hash of null also requires profile.create.

Returns: The full profile after the write, in the same sections shape read_profile returns, so you do not need a follow-up read. It also returns a new version_hash. When the server has the earlier state to compare against, the response includes changes, a per-section diff of what this write added, updated, or removed. A wrong expected_version_hash fails with CONFLICT and writes nothing. See Debugging for write history.

Trips

list_trips

Lists the traveler’s trips, most recently updated first by default. Archived trips never appear.

FieldTypeNotes
statusstringOptional. One of Dreaming, Planning, Booking, Booked, Trip In Progress, Completed, Cancelled, On Hold.
exclude_statusesarrayOptional. 1 to 8 statuses. Drops trips in any of them.
querystringOptional. Case-insensitive substring searched across the trip title and the trip’s section content.
starts_afterstringOptional. Inclusive ISO YYYY-MM-DD lower bound on start_date. Excludes trips with no start date.
starts_beforestringOptional. Inclusive ISO YYYY-MM-DD upper bound on start_date. Excludes trips with no start date.
sortstringOptional. recently_updated (default), or start_date for soonest departure first with undated trips last.
limitintegerOptional (1 to 100, default 20).
cursorstringOptional. The opaque next_cursor from a previous page. Pass it through unchanged. Do not parse it.

Required scope: trip.list

Returns: items and next_cursor. Each item has trip_id, slug, title, status, start_date, end_date, and updated_at, plus card_color when the traveler picked a color for the trip card and matched_sections when query matched section content.

matched_sections names the sections where the phrase was found. It carries section names only, never the matching text. It omits any section this client is not allowed to read. Fetch the content with read_trip. So query: "ryokan" finds the trip whose accommodation section mentions one, even when its title does not.

“What is the traveler’s next trip” is a single call: sort: "start_date", starts_after set to today, exclude_statuses: ["Cancelled", "Completed"], and limit: 1. Always send exclude_statuses for a question about upcoming trips. A cancelled trip keeps its future dates, so without it the soonest trip can be one the traveler called off.

Paging rules:

  • The filters run in the query. next_cursor is set only when more rows exist. An empty items list means there are no more results.
  • A cursor is valid only for the sort and filters it was issued under. Changing sort or a filter mid-pagination fails with an invalid-cursor validation error. Start again with no cursor.

read_trip

Returns a single trip by ID as structured sections, with its dated bookings.

FieldTypeNotes
trip_idstringRequired. The trip’s UUID.
include_markdownbooleanOptional (default false). Also return the markdown.
include_itinerarybooleanOptional (default false). Also return the day-by-day summary.

Required scope: trip.read

Returns:

  • The trip envelope: trip_id, slug, title, status, start_date, and end_date.
  • sections, version_hash (can be null), spec_version, and renderer_version.
  • events, always: the trip’s dated bookings in timeline order, undated ones last. Each event carries its event_id and the sentence it writes into the trip.
  • itinerary, only when include_itinerary is true: the events grouped into days, with each day’s location. It is read-only. No tool accepts it.
  • unstructured_fact_sentences, when present: sentences in a bookings section that have no event record. Resend each one through events to make it a booking.
  • card_color, only when the traveler picked a color for the trip card.

The call fails with NOT_FOUND when the trip does not exist, is not visible to this client, or is archived.

create_trip

Creates a new Trip.md for the authenticated traveler. Create one as soon as the traveler mentions a place they want to go, even loosely, with status Dreaming. Call list_trips first so you extend an existing trip and do not create a duplicate.

FieldTypeNotes
titlestringRequired. Short trip name, for example “Tokyo, March”.
statusstringRequired. One of the trip statuses listed under list_trips.
sectionsobjectOptional (default {}). Section name to array of sentences.
eventsarrayOptional. Dated bookings to add. See Trip events.
slugstringOptional. Derived from title if omitted.
start_datestring or nullOptional. ISO YYYY-MM-DD.
end_datestring or nullOptional. ISO YYYY-MM-DD. The server rejects an end_date before start_date.
idempotency_keystringOptional. Unique string for safe retries.
include_markdownbooleanOptional (default false).

Required scope: trip.create

Returns: The new trip with its stable UUID, its sections, and a version_hash. The response can also include events and unstructured_fact_sentences.

update_trip

Updates an existing Trip.md. Like update_profile, it merges section by section. It can also change envelope fields (title, status, slug, dates) and add, edit, or remove events.

trip_id and expected_version_hash are the only required fields. An envelope-only change, such as setting a trip to Booked or correcting a date, does not need a sections map.

FieldTypeNotes
trip_idstringRequired. The trip’s UUID.
expected_version_hashstringRequired. The version_hash from your last read or write of the trip. It covers the whole call.
sectionsobjectOptional (default {}). Section name to array of sentences.
eventsarrayOptional. Dated bookings to add, edit, or remove. See Trip events.
titlestringOptional. Rename the trip.
statusstringOptional. One of the trip statuses.
slugstringOptional.
start_datestring or nullOptional. ISO YYYY-MM-DD.
end_datestring or nullOptional. ISO YYYY-MM-DD. The server rejects an end_date before start_date.
allow_clear_sectionsbooleanOptional (default false). Required to send any section as an empty list.
idempotency_keystringOptional. Unique string for safe retries.
include_markdownbooleanOptional (default false).

Required scope: trip.update

Returns: The full trip after the write, with the envelope and the same sections shape read_trip returns, and a new version_hash. The response can also include events and unstructured_fact_sentences. When the server has the earlier state to compare against, it includes changes, a per-section diff of what this write added, updated, or removed.

Two different situations both return CONFLICT, and the message tells them apart. A stale expected_version_hash states the current hash. A slug that another of the traveler’s trips already uses names no hash. A new hash cannot fix a slug collision. Send a different slug or title.

Trip events

Dated bookings go in events, not in sections. A booking is a flight, a stay, a train, a restaurant or activity reservation, a car hire, or anything else the traveler has arranged for a date. Narrative, preferences, and open questions stay in sections.

  • Each item is op: "upsert" or op: "remove". An upsert without an event_id creates an event. An upsert with an event_id edits that event. A remove deletes it.
  • A call accepts up to 25 events, applied atomically with any sections in the same call.
  • The server writes a sentence for each event into the matching section of the trip. Do not also write the booking as prose.
  • start and end are objects, for example {"date": "2027-03-28", "time": "19:30", "tz": "Europe/Lisbon"}. Add a time or an end only when the traveler stated it.
  • idempotency_key is a field of the call. Never put it inside an event.

Events require the traveler to have given this client access to the trip itinerary. Without that access, a call that sends events fails with FORBIDDEN, details.disallowedSections contains trip:events, and nothing is written. A write from that client that does not send events succeeds, and the response carries itinerary_write_forbidden: true to show that no booking reached the itinerary.

archive_trip

Files a trip away. An archived trip leaves list_trips and can no longer be read or updated through this server. It is not deleted. The traveler keeps it in the archive section of their Traveler.md app, where only they can restore it or delete it.

Use this when the traveler wants a trip out of their active list because it is over, abandoned, or clutter. Ask the traveler before you call it. It is the only tool whose effect the traveler has to undo in the app. Do not use it to delete a trip. There is no delete tool, and archiving destroys nothing.

FieldTypeNotes
trip_idstringRequired. The trip’s UUID.

Required scope: trip.update

Returns: The trip_id and archived: true.

Notes:

  • No expected_version_hash, no idempotency_key. Archiving does not change trip content, so it cannot conflict with a concurrent edit.
  • Not idempotent. A call on a trip that is already archived returns NOT_FOUND. So does a trip that does not exist or is not visible to this client.
  • There is no unarchive tool. Archived trips are invisible to this server, so a client cannot find one to restore. Send the traveler to the archive in their app.

Prompts and resources

The server also publishes three MCP prompts. A client can offer them to the traveler as ready-made requests.

PromptWhat it does
plan_a_tripStarts or continues planning a trip from the traveler’s saved preferences. Optional destination and when arguments.
my_next_tripBriefs the traveler on their soonest upcoming trip: dates, stays, what is booked, and what still needs a decision.
remember_preferenceRecords a lasting travel preference on the traveler’s profile. Optional preference argument, in the traveler’s own words.

Two resources hold the file specs:

  • traveler://spec/traveler-md/v1.0.0, the Traveler.md spec.
  • traveler://spec/trip-md/v1.0.0, the Trip.md spec.

Agent behavior

Most agents call read_profile once per session and keep the result. Writes to the traveler’s own profile and trips proceed without a confirmation step. Ask the traveler before you archive a trip.