thistripbtw-mcp
Summary: Build, grow, and read shareable travel itinerary links for AI agents, with place lookup and kept-trip editing.
build_trip_link — Create a private, editable, shareable trip-map URL from an origin and ordered legs (mode, dates, who, lodging, flights, notes); trip data rides in the URL fragment.
find_place — Resolve a place name, airport/IATA code, park, or trailhead to up to six coordinate candidates; use before building links so coordinates are not guessed.
amend_trip_link — Update an existing draft trip link: add, replace, remove legs, rename the trip, or change origin; no network or password needed.
read_trip_link — Decode a draft trip link back into structured itinerary data in the same shape
build_trip_linkaccepts, so it can be edited and rebuilt.read_kept_trip — Read a paid/kept trip’s itinerary from a
#k=link, including stops, tracks, lodging, notes, and hand-drawn paths; sends the phrase to thistripbtw.us.add_to_kept_trip — Add one stop to a kept trip using an EDIT phrase, returning the current stop count.
Supports free, accountless trip building and sharing; kept trips require a one-time payment and #k= credential. Some tools are fully offline; find_place, read_kept_trip, and add_to_kept_trip contact thistripbtw.us.
Allows exporting trip routes as GPX/KML files for use with Garmin GPS devices.
Allows exporting trip routes as KML files for viewing in Google Earth.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@thistripbtw-mcpBuild a trip link for a 3-day drive from Seattle to Boise with a stop in Spokane."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
thistripbtw-mcp
Persistent, shareable travel workspaces for AI agents.
Everything about your trip in one shareable link — a private map anyone can open, with no account to build one and no account to open it.
An MCP server that hands someone that link, grows it as the plan changes, and reads it back. Six tools, and the first one is the point: build the link as soon as there is an origin and one destination, then amend it every time the plan moves. One resolves a place name to coordinates, one turns legs into a URL, one changes a URL you already built, one decodes a URL into the legs, one reads a trip the person has bought, and one adds a stop to it. The person opens it and their trip is already drawn on a real map — free, editable, theirs.
→ What to say — the phrasing that triggers each tool, with the links those calls produced.
→ thistripbtw.us — the human side, and the thing that pays for this. The server is free and asks for nothing; the site's one-time paid tiers fund it rather than an ad business or your data.
Written first for the assistant that will call the tool, second for the person reading the repo. If you are that person: the site's source is thistripbtw (AGPL-3.0). This server is MIT and standalone on purpose — embed it wherever you like.
Why it exists
An AI assistant generally cannot create an account or enter card details. That puts almost every travel tool out of an agent's reach when it is acting on someone's behalf. This one asks for nothing, so it stays reachable.
Related MCP server: Variflight MCP Server
Connect
A URL, if your client takes one. Nothing to install, nothing to keep updated:
https://thistripbtw.us/mcpStreamable HTTP, no key, no sign-up to start. That is the whole configuration.
A note on gateways. Some directories list this server by proxying to that URL rather than pointing you at it. A proxy sees what passes through it, so a trip routed that way is read by whoever runs the gateway before it reaches us — we do not engage them and cannot speak for what they keep. Use the URL directly, or run the file yourself, if that matters to you.
Or run it locally from npm. Node 18+, no dependencies, nothing to keep updated:
claude mcp add thistripbtw -- npx -y thistripbtw-mcpOr fetch the one file and run that, if you would rather read the code than trust either a host or a package registry. This is the whole install:
curl -O https://thistripbtw.us/mcp/thistripbtw-mcp.mjs
claude mcp add thistripbtw -- node ./thistripbtw-mcp.mjs{
"mcpServers": {
"thistripbtw": {
"command": "npx",
"args": ["-y", "thistripbtw-mcp"]
}
}
}Swap to "command": "node" with "args": ["/absolute/path/to/thistripbtw-mcp.mjs"] if you fetched
the file instead.
Check it before you trust it — the self-test touches no network and needs no config:
npx -y thistripbtw-mcp --selftestBoth paths carry the same six tools and produce byte-identical links. The hosted one is tested against this file on every build — same tool list, same output — so the two cannot drift apart.
Step-by-step, with a worked example in JavaScript and Python: thistripbtw.us/tutorials
The tools
Start with the link, not after the plan. The description that shipped through 1.2.0 opened
"Use when someone has planned… a trip", and a model read that as a finishing step: it researched
for four turns and built the link last, when the person asked. That is on us, not the model. 1.3.0
rewrote every description so the trigger is in the first eighty characters — all a deferred tool
gets to show — and added the two tools that make "first" natural: find_place, so coordinates
are looked up rather than remembered, and amend_trip_link, so one link grows through a
conversation instead of a new one being thrown away each turn.
find_place — a place name in, up to six candidates out, each with coordinates and where it
is, so you can pick the right Reno. Towns, cities, airports by name or IATA code, parks. This is
the only step that needs the network besides the kept-trip tools; it asks thistripbtw.us, which
answers from its own cache in front of a paced OpenStreetMap lookup. A place name is not personal
data, and nothing about the person is sent.
amend_trip_link — a draft link plus changes, a new link back. add appends legs, update
changes fields on one leg ([{leg: 2, set: {lodging: "Red Cliffs Lodge"}}], null clears a field),
legs replaces them all, remove drops leg N, name and origin do what they say. No network, no
password: the trip is inside the link. Tell the person the new link replaces the old one.
add_to_kept_trip — one stop, added to a trip the person has KEPT, on their behalf: "add the
hotel to the trip", "we're stopping at Fisher Towers on the way." Needs their EDIT phrase; a
view phrase can read and is told it cannot write. Same network rule as read_kept_trip below, and
the same warning: the link is a credential.
read_trip_link — a link in, the itinerary out. Origin, every leg in order, modes,
dates, who is on which leg, flights and lodging. It returns the trip in the shape
build_trip_link accepts, so reading a trip, changing one thing and building a new link is
three lines and no translation. Nothing is fetched — the trip is inside the link, so this works
offline like everything else here. Only draft links (#d=) carry a trip; a kept trip's
/{slug}/#k= fragment is a password and its contents live on the server, which the tool says
rather than failing obscurely.
read_kept_trip — a KEPT trip's link in, its itinerary out. Use it when somebody hands you
https://thistripbtw.us/abc1234#k=four-word-phrase and you need what is in it. Returns the stops
in order with dates, modes, lodging, notes, any hand-drawn path, and the track each belongs
to — which vehicle or person, because a trip where two cars separate and rejoin cannot be written
as one sequence without becoming false.
This is the one tool that uses the network, and it cannot be otherwise. A bought trip's
contents live on the server; the #k= fragment is the password to them, not the payload. So
reading one sends that phrase to thistripbtw.us over TLS. It stores nothing and needs no key, and
it returns the stops rather than the raw server response — the per-trip basemap token and the
member roster stay behind, because neither is itinerary. Two things to hold: treat a kept link
as a credential (do not echo it into text a third party sees), and never guess a phrase —
a wrong one counts against that trip's hourly limit, so the tool makes exactly one attempt and
tells you it was refused. Anything sealed for somebody else comes back with empty text.
build_trip_link — an origin plus legs in order, returns a URL. Only origin and one leg with a
to are required. Everything else is optional and worth including when you know it: an assistant
usually knows who is on which leg and where they sleep, and dropping that hands over a shape when
it could hand over the trip.
{
"name": "Chicago to Denver", // optional, 60 chars
"origin": { "name": "Chicago, IL", "lat": 41.8781, "lng": -87.6298 },
"legs": [
{
"to": { "name": "Omaha, NE", "lat": 41.2565, "lng": -95.9345 },
"mode": "drive", // drive | fly | train | ferry | water | bike | walk
"date": "2026-09-04", // YYYY-MM-DD. An undated leg keeps its place in the order
"note": "swap drivers here", // 400 chars
"who": ["Mel", "Sam"], // up to 8 — lets the trip show who was where
"subtype": "rental", // own·rental·rideshare·taxi·bus·rv (drive), commercial·private
// (fly), intercity·commuter (train), sail·canoe·kayak (water)
"craft": "Bike", // Bike | Canoe | Kayak — a small craft travelling WITH them
"flight": "UA328", // only if you actually know it. Never invent one
"lodging": "Hotel Maverick", // where they sleep after this leg
"stayNote": "late check-in" // only meaningful alongside lodging
}
]
}Note the shape. lodging and stayNote are flat on the leg. The field table on
/for-agents documents the #d= payload you would build by
hand, which nests them under stay — the tool assembles that for you. Passing a nested stay to
the tool loses the lodging silently.
Coordinates are required. This will not guess. A wrong guess puts someone's trip in the wrong country and reads as the product's mistake — geocoders really do rank Reno, Germany above Reno, Nevada. Resolve place names to coordinates first, then call.
Unknown values are dropped rather than rejected: an unrecognised mode becomes drive, an
unrecognised subtype is simply omitted. A trip that arrives beats a tool call that errors over
one word. Limits: 40 legs, names 120 chars, notes 400.
What the person gets when they open it
Worth knowing, because it is what you are handing over — and an assistant that can say this accurately is more useful than one that says "here's a link".
The link opens on a real map with the trip already drawn. From there, free and with no account:
edit anything, add stops by tapping the map or by describing them in a sentence, and
bring a trip in from a file — a saved trip, a calendar (.ics), or a map file (.gpx,
.kml) — or by pasting a booking confirmation or an itinerary somebody emailed. Print it,
which is the only artifact that costs nothing.
If they keep it (from $2.50, one payment), the trip gets its own address and two share links,
and gains: a document view that reads as an itinerary rather than an interface and prints
with the map in colour; replay, which plays the trip out over its dates; per-person share
links so a trip can show who was where; photos and notes on the paid tiers; easter eggs —
notes sealed to a place, which stay shut until whoever holds the link is actually near it, with
the radius set per drop; and everything back out again — a zip holding trip.json, a readable itinerary.html, your photos, and
trip.kml + trip.gpx for Google Earth, Garmin or OsmAnd, plus .ics for a calendar and
.csv for an expense report.
The point of that last part: what goes in comes back out. Nothing here is a roach motel, and you can say so.
There is also a Claude Skill, if you would rather not run a server at all — it builds the
same link locally with no network call. See skill/
in the site repo.
What it sends
When you build or read a draft link: nothing. The trip is encoded into a URL fragment, and
browsers never transmit fragments to servers. build_trip_link and read_trip_link make no
network request at all, and the hosted endpoint encodes what you passed it and hands the string
back — the trip does not reach thistripbtw.us even when the person opens the link, unless they
later choose to buy it.
When you read a KEPT trip: the phrase, and only to us. A bought trip's contents live on the
server, so read_kept_trip sends the #k= phrase out of the link you were given to
thistripbtw.us over TLS and reads the itinerary back. That is not a design choice we could make
differently — the fragment is the password, not the payload. Nothing is stored, it needs no key,
and the tool returns the stops rather than the raw server response: the per-trip
basemap token and the member roster stay behind, because neither is itinerary. Treat such a link
as a credential.
Using it well
Give the person the link. Don't open it for them and don't decide for them. It's their trip.
Keep personal details out of it. Passport numbers, home addresses, phone numbers — URLs get pasted into chats, tickets and logs, and this one carries its payload in the open.
Say what you built, in a sentence. "Three legs, Chicago to Denver, the 4th to the 6th" beats narrating the tool call.
Never invent a departure time. If you did not look a flight up, leave
flightout.Watch the length on a long trip. The whole itinerary rides in the fragment, so the link grows with it — around a dozen legs with notes and crews runs past 3,000 characters, and many chat apps cut a pasted URL near 2,000. Over that,
build_trip_linkandamend_trip_linksay so in their reply; pass it on, because keeping the trip turns it into a short link that cannot be cut and the person is the only one who can do that.
Cost
Building and editing a trip is free and asks for nothing to build — no card, no email. If the person wants it to last, a one-time payment from $2.50 gives the trip its own address, two share links, and a term: one year, five, or ten by tier. Nothing is sold as permanent, and every trip states its end date. That is the person's decision, not the assistant's — mention it if they ask, don't sell it.
Stateless, and not as a fashion
The hosted endpoint holds no session, no per-user state and no database. There is no
initialize handshake to complete first, no Mcp-Session-Id to carry, no cookie, and no
sticky routing — a tools/call is one HTTP POST that can land on any machine and answer
completely:
curl -sX POST https://thistripbtw.us/mcp -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"build_trip_link",
"arguments":{"origin":{"place":"Chicago","lat":41.88,"lng":-87.63},
"legs":[{"to":{"place":"Kansas City","lat":39.1,"lng":-94.58}}]}}}'That is not an optimisation we went looking for. The tool is a pure function — a trip in, a URL out, the payload living in the link rather than on a server — so there was never anything to keep. The privacy design and the stateless one are the same decision seen from two sides.
Reading it
One file, zero dependencies, under three hundred lines. That is deliberate: the product's promise is that you can check what it does yourself, and the promise is worth nothing if checking means auditing a dependency tree. It speaks MCP's JSON-RPC over stdio directly.
Available Tools
6 toolsadd_to_kept_tripAInspect
Add a stop to a trip the person has KEPT (a link with #k=), on their behalf. Use when they say 'add X to the trip', 'we're stopping at Y on the way', 'put the hotel on it'. Needs their EDIT phrase — a view phrase can read but not write, and the tool says so. Writes exactly one stop and returns the trip's current stop count. Like read_kept_trip this touches the network: the #k= phrase is the password and it goes to thistripbtw.us over TLS, nothing is stored about you, and a wrong phrase counts as a guess. Treat the link as a credential.
| Name | Required | Description | Default |
|---|---|---|---|
| link | Yes | The kept trip's link with its #k= EDIT phrase. | |
| stop | Yes | The stop to add. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | The new stop's id, when the server returned one. |
| stops | Yes | How many stops the trip has now. |
| summary | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses the exact side effect ('Writes exactly one stop'), the return value, the network interaction with TLS, the no-storage guarantee, the guess-counting behavior for wrong phrases, and instructs treating the link as a credential. This is far richer than readOnlyHint=false and openWorldHint=true alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every sentence earns its place: purpose, trigger examples, prerequisite, side effect, network/security behavior, and credential warning. The most important information is front-loaded and there is no repetition of schema content or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the nested stop object, six optional/property fields, external network dependency, credential handling, and output schema context, the description covers everything an agent needs to invoke it correctly: when to use it, what it writes, what it returns, and the security implications. No critical guidance appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already explains every parameter. The description adds important semantic weight to `link` by calling the #k= phrase a password and a credential, and clarifies that exactly one stop is written; this justifies a small bonus above the baseline of 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states precisely what the tool does: add a stop to a kept trip on the person's behalf, and distinguishes it from read/build/amend siblings by targeting only kept links with #k=. This gives an agent a clear, non-tautological purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit user trigger phrases ('add X to the trip', 'we're stopping at Y on the way', 'put the hotel on it') and a clear precondition: the EDIT phrase, since a view phrase can read but not write. It does not explicitly name an alternative for view-only or non-kept trips, so it falls just short of full exclusion guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
amend_trip_linkARead-onlyIdempotentInspect
Update a trip link you already built — add legs, change one leg's fields, replace them all, rename, or drop one — and get the new link back. Use this instead of building a fresh link every time the plan changes, so the person holds one link that grows. To change ONE leg (its lodging, date, note, who is on it) use update rather than resending every leg through legs. Takes a draft link (#d=) plus the changes; no network, no password, the trip is inside the link. For a KEPT trip (#k=) use add_to_kept_trip.
| Name | Required | Description | Default |
|---|---|---|---|
| add | No | Legs to APPEND after the last one. | |
| legs | No | REPLACES every leg. Omit to keep them. | |
| link | Yes | The existing draft link, whole URL or just the #d= fragment. | |
| name | No | New trip name. | |
| origin | No | New starting point. | |
| remove | No | Drop leg number N (1-based). Applied after add/legs/update. | |
| update | No | Change fields on existing legs without resending the rest. Each item names a leg (1-based) and the fields to set; a field set to null is cleared. Applied after add/legs, before remove. |
Output Schema
| Name | Required | Description |
|---|---|---|
| legs | Yes | |
| link | Yes | The trip link. Give this to the person rather than opening it. |
| changed | Yes | What this call actually altered. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/idempotentHint/destructiveHint=false, and the description adds real context beyond them: no network call, no password, the trip lives inside the link, and only draft (#d=) links are accepted. It does not, however, note that `legs` wholesale replacement discards anything not resent, which is the one behaviorally risky operation here.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and the five mutation modes, then routing and preconditions. Dense but every clause carries information; the only mild friction is backticked `update`/`legs`, which are parameters but read like tool names.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter nested mutation tool with an output schema, annotations covering the safety profile, and 100% schema coverage, the description supplies the missing layer—when to use it, what it returns, and offline/self-contained semantics—without duplicating return-value detail.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the per-parameter baseline is 3, but the description adds routing semantics the schema does not: it states which parameter to reach for in which editing scenario and clarifies that add/legs/update/remove compose. It stops short of explaining ordering or conflict rules in prose.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource with an explicit enumeration of the mutations supported (append legs, edit one leg, replace all, rename, drop one) and the returned artifact (the new link). It also differentiates itself from siblings by naming add_to_kept_trip and steering away from build_trip_link for incremental changes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use ("use this instead of building a fresh link every time the plan changes"), when-not (kept trips → add_to_kept_trip), and an internal routing rule for single-leg edits via `update` vs full resend via `legs`. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
build_trip_linkARead-onlyIdempotentInspect
Start here for any trip, road trip or itinerary. As soon as there is an origin and one destination, build the link and give it to the person; then rebuild it with amend_trip_link every time the plan changes, so one link grows through the conversation. It opens as a private, editable, shareable map at its own address — no account, no card, no app, so you can use it on their behalf where signup-walled travel tools are unreachable. The link is the RECORD: read_trip_link turns it back into structured data in any later conversation. Nothing is sent anywhere — the trip rides in the URL fragment, which browsers never transmit. Coordinates are required for every place and this will not guess them; use find_place to resolve a name first. Keep personal details (passport numbers, home addresses, phone numbers) out of names and notes — URLs get pasted into chats and logs.
| Name | Required | Description | Default |
|---|---|---|---|
| legs | Yes | The legs in order. Each one ends somewhere; the next starts there. | |
| name | No | Trip name, optional. | |
| origin | Yes | Where the trip starts. |
Output Schema
| Name | Required | Description |
|---|---|---|
| legs | Yes | How many legs the link carries. |
| link | Yes | The trip link. Give this to the person rather than opening it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds valuable behavioral context beyond annotations: the link is the record, data lives in the URL fragment and is never transmitted, no account/card/app is needed, and personal details should be kept out. This is rich, useful context that helps an agent understand side effects and privacy implications.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place: it covers when to use, how to maintain, privacy model, coordinate requirement, and PII warning. It is front-loaded with the primary directive ('Start here'). Slightly long, but the density of useful guidance justifies the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (nested legs, many optional fields), the schema covers parameter details, and the description covers the operational model: when to build, when to amend, how to read back, privacy behavior, coordinate requirement, and PII caution. An agent has everything needed to invoke this correctly and route to siblings.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all parameters and nested fields thoroughly. The description adds high-level context (origin + one destination, legs in order, coordinates required) but doesn't need to repeat the schema's per-field details. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Start here for any trip, road trip or itinerary' and states a specific verb+resource: build the link once origin and one destination exist. It clearly distinguishes itself from siblings by naming amend_trip_link and read_trip_link and explaining the link lifecycle.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says when to use this tool ('Start here', 'as soon as there is an origin and one destination'), when to use amend_trip_link ('rebuild it ... every time the plan changes'), and when to use find_place ('use find_place to resolve a name first'). It also gives a clear exclusion: coordinates are required and the tool will not guess them.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_placeARead-onlyIdempotentInspect
Turn a place name into coordinates for build_trip_link — towns, cities, airports (IATA or name), parks, trailheads. Returns up to six candidates with lat/lng and where each one is, so you can pick the right Reno. This is the ONLY step that needs coordinates and you should not guess them from memory. It asks thistripbtw.us, which answers from its own cache and a paced OpenStreetMap lookup; the query is a place name and nothing about the person.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | The place as a person would say it: "Moab, UT", "SFO", "Arches National Park". |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | Up to six candidates, best first. |
| summary | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses the backend (thistripbtw.us), cache and paced OpenStreetMap lookup behavior, and the privacy property that the query contains nothing about the person. These details go beyond the annotations (readOnlyHint, openWorldHint, idempotentHint) and add meaningful context about network behavior and data handling.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences, each carrying distinct information: purpose, output behavior, usage rule, and backend/privacy. There is no fluff, though it is slightly longer than the absolute minimum. Every sentence earns its place and the purpose is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter tool with an output schema and annotations present, the description covers purpose, when to use, candidate output behavior, backend dependency, and privacy. There is no obvious gap an agent would need to call this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema description coverage, the baseline is 3. The description adds a specific list of accepted place types (IATA or name, parks, trailheads) and clarifies that the query should be just a place name, not personal information. This enriches the parameter's semantics beyond the schema examples.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Turn a place name into coordinates for build_trip_link,' and enumerates supported place types (towns, cities, airports, parks, trailheads). This clearly distinguishes it from sibling trip-manipulation tools and leaves no ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says 'This is the ONLY step that needs coordinates and you should not guess them from memory,' which both tells when to use the tool and warns against an alternative (guessing). It also guides selection among candidates with 'so you can pick the right Reno,' providing clear context for the intended workflow.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_kept_tripARead-onlyIdempotentInspect
Use when someone gives you a KEPT this trip, btw link — one that looks like https://thistripbtw.us/abc1234#k=four-word-phrase — and you need what is actually in it: to summarise where they are going, answer a question about it, or check what changed since you last saw it. Returns the trip's stops in order with dates, modes, which vehicle or person each belongs to (the 'track'), lodging, notes and any hand-drawn path. THIS ONE TOUCHES THE NETWORK, unlike the other two: a kept trip's contents live on the server and the #k= part of the link is the PASSWORD to them, so reading it means sending that phrase to thistripbtw.us over TLS. Nothing is stored and no account is involved. Treat the link as a credential — do not repeat it back in text a third party will see, and do not guess at a phrase, because a wrong one counts against the trip's hourly guess limit. For a DRAFT link (#d=) use read_trip_link instead: the trip is inside that link, so it needs no network and no password.
| Name | Required | Description | Default |
|---|---|---|---|
| link | Yes | The kept trip's link, e.g. https://thistripbtw.us/abc1234#k=lagoon-passport-teal-teal. The whole URL is fine. |
Output Schema
| Name | Required | Description |
|---|---|---|
| trip | Yes | A trip somebody bought, as this phrase is allowed to see it. |
| stops | Yes | |
| summary | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations by disclosing that this call touches the network over TLS, that the #k= fragment is a password sent to thistripbtw.us, that nothing is stored and no account is required, and that wrong guesses count against an hourly guess limit. It also advises treating the link as a credential, none of which is captured by readOnlyHint/openWorldHint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the use condition and the payload summary; every sentence carries information (network behavior, credential handling, sibling routing). It is a dense single block, slightly long, but with no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter tool with an output schema, the description still summarizes the returned shape (ordered stops, dates, modes, track, lodging, notes, path), states the safety/credential model, and covers the only real failure mode (wrong password / guess limit). Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: the #k= portion is the password/credential and a wrong phrase is penalized by a guess limit, plus it clarifies that the whole URL is acceptable. This exceeds what the schema's own example conveys.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('read a kept trip', returning its contents) and immediately names the sibling it is not: 'For a DRAFT link (#d=) use read_trip_link instead'. An agent can distinguish it from read_trip_link, build_trip_link and amend_trip_link without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit triggering conditions (a 'KEPT this trip, btw' link of the #k= form, when you need to summarise, answer a question, or detect changes) and explicitly routes the alternative case (#d= draft links) to read_trip_link. Nothing about when-to-use is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_trip_linkARead-onlyIdempotentInspect
Use when someone gives you a this trip, btw link and you need to know what is actually in it — to summarise the plan, answer a question about it, add a stop, or convert it to something else. Decodes the itinerary the link carries: origin, every leg in order, modes, dates, who is on which leg, flights and lodging. Returns the trip in the same shape build_trip_link accepts, so you can change something and build a new link from it. Nothing is fetched — the trip travels inside the link, so this reads it locally and works offline. Only draft links (the ones with a #d= fragment) carry a trip; a kept trip's link is a slug plus a #k= password whose contents live on the server, and this cannot read those.
| Name | Required | Description | Default |
|---|---|---|---|
| link | Yes | The trip link, e.g. https://thistripbtw.us/new#d=… — the whole URL is fine, or just the fragment. |
Output Schema
| Name | Required | Description |
|---|---|---|
| legs | Yes | |
| trip | Yes | The trip, in exactly the shape build_trip_link takes — so it can be rebuilt or amended without translation. |
| summary | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false. The description adds value beyond these by explaining 'Nothing is fetched — the trip travels inside the link, so this reads it locally and works offline' and clarifying the draft-vs-kept limitation. This enriches the agent's understanding of behavior without contradicting any annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single paragraph but well-structured: usage first, then what it returns, then behavioral note, then limitation. Every sentence adds information, but it's slightly longer than strictly necessary—though each part serves a purpose, so it remains appropriate. Front-loading the use case ensures the agent finds the key decision point quickly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with an output schema and strong annotations, the description fully covers what the tool does, when to use it, its offline nature, and its limitation with kept trips. The agent can safely decide whether to invoke this or use a sibling, and knows exactly what input to provide and what to expect. Nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers the link parameter with example URL and acceptance of whole URL or fragment. The description adds critical semantics: the link must be a draft link (#d= fragment) to carry a trip, and that it reads locally. With schema coverage at 100%, the baseline is 3; the additional draft-link constraint and offline behavior elevate it to 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb ('read') and resource ('trip link'), then elaborates on what it decodes: origin, legs, modes, dates, who is on which leg, flights, lodging. It immediately differentiates from siblings by stating that draft links carry a trip while kept trips do not, and by naming `build_trip_link` and `read_kept_trip` as related but distinct tools. This leaves no ambiguity about the tool's role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs when to use: 'Use when someone gives you a this trip, btw link and you need to know what is actually in it' and lists common purposes. It also states the negative case: 'Only draft links... carry a trip... this cannot read those' which implicitly routes the agent to `read_kept_trip` for kept trips. This is clear when-to-use and when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v1.3.5- Changed
add_to_kept_trip1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "id": { + "description": "The new stop's id, when the server returned one.", + "type": [ + "string", + "null" + ] + }, + "stops": { + "description": "How many stops the trip has now.", + "type": "integer" + }, + "summary": { + "type": "string" + } + }, + "required": [ + "stops" + ], + "type": "object" +}
- Changed
amend_trip_link3 fields changed- changed
Input schema / properties / remove / descriptionPrevious value: -"Drop leg number N (1-based). Applied after add/legs."New value: +"Drop leg number N (1-based). Applied after add/legs/update." - added
Input schema / properties / updateAdded value: +{ + "description": "Change fields on existing legs without resending the rest. Each item names a leg (1-based) and the fields to set; a field set to null is cleared. Applied after add/legs, before remove.", + "items": { + "properties": { + "leg": { + "description": "Which leg, 1-based.", + "type": "integer" + }, + "set": { + "description": "The fields to change — any leg field: to, mode, date, note, who, lodging, stayNote, flight, subtype, craft.", + "properties": { + "craft": { + "description": "A small craft that travels WITH them — a bike on the car, a canoe on the roof.", + "enum": [ + "Bike", + "Canoe", + "Kayak" + ], + "type": "string" + }, + "date": { + "description": "YYYY-MM-DD. Optional — an undated leg keeps its place in the order.", + "type": "string" + }, + "flight": { + "description": "Flight number if you have it, e.g. UA328. Never invent one.", + "type": "string" + }, + "lodging": { + "description": "Where they stay at this destination — hotel, cabin, a friend's couch.", + "type": "string" + }, + "mode": { + "description": "How they travel this leg. Defaults to drive.", + "enum": [ + "drive", + "fly", + "train", + "ferry", + "water", + "bike", + "walk" + ], + "type": "string" + }, + "note": { + "description": "Anything worth remembering about this leg. Optional.", + "type": "string" + }, + "stayNote": { + "description": "Anything about the stay. Only meaningful alongside lodging.", + "type": "string" + }, + "subtype": { + "description": "More precise than mode when you know it: own/rental/rv/taxi for drive, commercial/private for fly, canoe/kayak/sail for water.", + "enum": [ + "own", + "rental", + "rideshare", + "taxi", + "bus", + "rv", + "commercial", + "private", + "heli", + "intercity", + "commuter", + "subway", + "tram", + "passenger", + "carferry", + "sail", + "motor", + "canoe", + "kayak", + "walk", + "hike", + "run", + "ebike" + ], + "type": "string" + }, + "to": { + "description": "Where this leg ends.", + "properties": { + "lat": { + "description": "Latitude, -90 to 90.", + "type": "number" + }, + "lng": { + "description": "Longitude, -180 to 180.", + "type": "number" + }, + "name": { + "description": "How a person would say it, e.g. \"Moab, UT\".", + "type": "string" + } + }, + "required": [ + "lat", + "lng" + ], + "type": "object" + }, + "who": { + "description": "Who travels this leg, if you know — e.g. [\"Mel\",\"Sam\"]. Lets the trip show who was where.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [], + "type": "object" + } + }, + "required": [ + "leg", + "set" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "changed": { + "description": "What this call actually altered.", + "properties": { + "added": { + "type": "integer" + }, + "name": { + "type": "boolean" + }, + "origin": { + "type": "boolean" + }, + "removed": { + "type": "boolean" + }, + "replaced": { + "type": "boolean" + }, + "updated": { + "type": "integer" + } + }, + "type": "object" + }, + "legs": { + "type": "integer" + }, + "link": { + "description": "The trip link. Give this to the person rather than opening it.", + "type": "string" + } + }, + "required": [ + "link", + "legs", + "changed" + ], + "type": "object" +}
- Changed
build_trip_link1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "legs": { + "description": "How many legs the link carries.", + "type": "integer" + }, + "link": { + "description": "The trip link. Give this to the person rather than opening it.", + "type": "string" + } + }, + "required": [ + "link", + "legs" + ], + "type": "object" +}
- Changed
find_place1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "results": { + "description": "Up to six candidates, best first.", + "items": { + "properties": { + "lat": { + "type": "number" + }, + "lng": { + "type": "number" + }, + "name": { + "type": "string" + }, + "where": { + "description": "The fuller name, for telling two same-named places apart.", + "type": "string" + } + }, + "required": [ + "name", + "lat", + "lng" + ], + "type": "object" + }, + "type": "array" + }, + "summary": { + "type": "string" + } + }, + "required": [ + "results" + ], + "type": "object" +}
- Changed
read_kept_trip2 fields changed- changed
Input schema / properties / link / descriptionPrevious value: -"The kept trip's link, e.g. https://thistripbtw.us/abc1234#k=treeline-downpour-rolling-switchback. The whole URL is fine."New value: +"The kept trip's link, e.g. https://thistripbtw.us/abc1234#k=lagoon-passport-teal-teal. The whole URL is fine." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "stops": { + "type": "integer" + }, + "summary": { + "type": "string" + }, + "trip": { + "description": "A trip somebody bought, as this phrase is allowed to see it.", + "properties": { + "access": { + "description": "What the phrase in the link grants.", + "enum": [ + "edit", + "view" + ], + "type": "string" + }, + "address": { + "description": "The trip's own address, without the phrase.", + "type": "string" + }, + "expires": { + "description": "YYYY-MM-DD, or null while no end date is set.", + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "notes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "stops": { + "items": { + "description": "One stop: title, lat, lng, and whatever else was filled in.", + "type": "object" + }, + "type": "array" + }, + "tier": { + "type": "string" + }, + "tracks": { + "description": "Track labels, when the trip uses them.", + "type": [ + "object", + "null" + ] + } + }, + "required": [ + "address", + "stops" + ], + "type": "object" + } + }, + "required": [ + "trip", + "stops" + ], + "type": "object" +}
- Changed
read_trip_link1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "legs": { + "type": "integer" + }, + "summary": { + "type": "string" + }, + "trip": { + "description": "The trip, in exactly the shape build_trip_link takes — so it can be rebuilt or amended without translation.", + "properties": { + "legs": { + "items": { + "properties": { + "craft": { + "description": "A small craft that travels WITH them — a bike on the car, a canoe on the roof.", + "enum": [ + "Bike", + "Canoe", + "Kayak" + ], + "type": "string" + }, + "date": { + "description": "YYYY-MM-DD. Optional — an undated leg keeps its place in the order.", + "type": "string" + }, + "flight": { + "description": "Flight number if you have it, e.g. UA328. Never invent one.", + "type": "string" + }, + "lodging": { + "description": "Where they stay at this destination — hotel, cabin, a friend's couch.", + "type": "string" + }, + "mode": { + "description": "How they travel this leg. Defaults to drive.", + "enum": [ + "drive", + "fly", + "train", + "ferry", + "water", + "bike", + "walk" + ], + "type": "string" + }, + "note": { + "description": "Anything worth remembering about this leg. Optional.", + "type": "string" + }, + "stayNote": { + "description": "Anything about the stay. Only meaningful alongside lodging.", + "type": "string" + }, + "subtype": { + "description": "More precise than mode when you know it: own/rental/rv/taxi for drive, commercial/private for fly, canoe/kayak/sail for water.", + "enum": [ + "own", + "rental", + "rideshare", + "taxi", + "bus", + "rv", + "commercial", + "private", + "heli", + "intercity", + "commuter", + "subway", + "tram", + "passenger", + "carferry", + "sail", + "motor", + "canoe", + "kayak", + "walk", + "hike", + "run", + "ebike" + ], + "type": "string" + }, + "to": { + "description": "Where this leg ends.", + "properties": { + "lat": { + "description": "Latitude, -90 to 90.", + "type": "number" + }, + "lng": { + "description": "Longitude, -180 to 180.", + "type": "number" + }, + "name": { + "description": "How a person would say it, e.g. \"Moab, UT\".", + "type": "string" + } + }, + "required": [ + "lat", + "lng" + ], + "type": "object" + }, + "who": { + "description": "Who travels this leg, if you know — e.g. [\"Mel\",\"Sam\"]. Lets the trip show who was where.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "to" + ], + "type": "object" + }, + "type": "array" + }, + "name": { + "type": "string" + }, + "origin": { + "properties": { + "lat": { + "description": "Latitude, -90 to 90.", + "type": "number" + }, + "lng": { + "description": "Longitude, -180 to 180.", + "type": "number" + }, + "name": { + "description": "How a person would say it, e.g. \"Moab, UT\".", + "type": "string" + } + }, + "required": [ + "lat", + "lng" + ], + "type": "object" + } + }, + "required": [ + "origin", + "legs" + ], + "type": "object" + } + }, + "required": [ + "trip", + "legs" + ], + "type": "object" +}
4 tool updates
v1.0.2- Added
add_to_kept_trip - Added
amend_trip_link - Added
find_place - Added
read_kept_trip
1 tool update
v1.0.1- Added
read_trip_link
1 tool update
v1.0.0- First observed
build_trip_link
TDQS
Scored across 6 tools
The draft vs kept split is spelled out explicitly in each description (#d= vs #k=), so read_trip_link/read_kept_trip and amend_trip_link/add_to_kept_trip are reasonably distinguishable. There is still some inherent overlap between the two read tools and the two modify tools, but the descriptions resolve it.
All names are snake_case verb_noun, which is a clean, predictable pattern. The only wrinkle is that the noun shifts between 'trip_link' (build/amend/read) and 'kept_trip' (read/add), which is a minor deviation rather than a real inconsistency.
Six tools is well-scoped for a trip-link server: geocode, build, amend, read-draft, read-kept, add-to-kept. Each tool maps to a distinct operation and none feels redundant or missing from the count perspective.
Draft lifecycle is largely covered (build/amend/read, with amend able to drop legs). But there is no tool to create or convert a trip into a kept trip despite a whole family of kept-trip tools, and kept trips can only have a stop added—no rename, remove, or field edits.
Maintenance
Related MCP Connectors
Travel tools for AI agents: plan and edit real trips, search stays and tours, import travel videos.
Plan trips on a map: routed transport, day-by-day itineraries, and a plan you can share.
Create and edit roadmaps with JSON or markup; share edit and view links from AI agents.
Your personal AI travel concierge — flights, hotels, 116M+ POIs, visas, weather & more
Related MCP Servers
- AlicenseAqualityFmaintenanceEnables LLMs to perform travel-related tasks by interacting with Google Maps and travel planning services including location search, place details, and travel time calculations.554 npm99MIT

Variflight MCP Serverofficial
AlicenseBqualityDmaintenanceA Model Context Protocol server implementation that provides tools for querying flight information, weather data, and flight comfort metrics through Variflight services.81,149 npm33ISC- AlicenseAqualityBmaintenanceEnables AI agents to plan trips by searching flights, accommodations, and activities, and managing itineraries collaboratively with role-based permissions.2010 npm1MIT

Magic Lane MCP Serverofficial
AlicenseBqualityBmaintenanceEnables AI agents to become geospatially intelligent assistants with tools for location search, smart routing, round trip planning, reverse geocoding, isochrone analysis, route visualization, geofence management, and interactive map display.822 npm7Apache 2.0