Skip to main content
Glama
nanwer
by nanwer

trip-search-mcp

Let Claude plan trips for you, in plain English. Live searches against Google Flights, Google Hotels, vacation rentals, Airbnb, Tripadvisor activities, and event ticket vendors — plus weather forecasts, currency conversion, persistent price watches, and per-property detail drill-downs. Eleven tools, one config block.

You:   Find me round-trip flights Helsinki → Washington DC for May 18,
       returning May 29, one stop or fewer.

Claude: [calls search_flights with WAS auto-expanded to IAD, DCA, BWI;
        merges 3 parallel results, ranks cheapest first, returns a
        summary with "Book on Google Flights" links]

šŸ“‹ FEATURES.md has the full plain-English feature list with paste-ready example prompts for every capability — read that to see what's possible.

šŸ“ TRIP-PLANNING-EXPANSION-SPEC.md tracks the five-track expansion plan (weather, currency, events, activities, drill-down). Weather is shipped; the other four are queued.


Before you start

You need:

  • A computer running macOS, Windows, or Linux

  • Claude Desktop, signed in

  • About 5 minutes the first time

You do NOT need an account anywhere except Claude — unless you also want hotel search, which uses a free SerpAPI key (covered as an optional step below).


Related MCP server: MCP Travel Concierge Server

Install — step by step

Everything below happens in your Terminal app (macOS/Linux) or PowerShell (Windows).

Don't know what a terminal is? macOS: press ⌘+Space, type Terminal, press Enter. Windows: press the Win key, type PowerShell, press Enter.

1. Install Python 3.12 (skip if you already have it)

python3 --version

If you see Python 3.12.x or higher, jump to step 2. Otherwise install uv — one line, brings Python with it:

# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Open a fresh terminal window after the installer finishes so the uv command is on your path.

2. Download the project

git clone https://github.com/nanwer/trip-search-mcp.git
cd trip-search-mcp

Missing git? macOS: run xcode-select --install. Windows: install from git-scm.com and reopen PowerShell.

3. Install the package

uv venv
uv pip install -e .

Creates .venv/ and installs everything. About 30 seconds.

4. Find the absolute path to the venv Python

You'll paste this into Claude Desktop's config in the next step.

# macOS / Linux
echo "$(pwd)/.venv/bin/python"
# Windows
echo "$(Resolve-Path .\.venv\Scripts\python.exe)"

Copy what it prints — looks like /Users/you/trip-search-mcp/.venv/bin/python (macOS) or C:\Users\you\trip-search-mcp\.venv\Scripts\python.exe (Windows).

5. Add a trip-search entry to Claude Desktop's config

This is the one step where you have to edit a text file by hand (or get Claude to edit it for you — see the callout below). The file is JSON; if you've never edited JSON before, just be careful with commas and quotes.

Where is the file?

OS

Path

Quickest way to find it

macOS

~/Library/Application Support/Claude/claude_desktop_config.json

In Finder, hit ⌘+Shift+G and paste ~/Library/Application Support/Claude/. Or run the open -e command below.

Windows

%APPDATA%\Claude\claude_desktop_config.json

In File Explorer, paste %APPDATA%\Claude\ into the address bar. Or run the notepad command below.

Linux

~/.config/Claude/claude_desktop_config.json

Open in your favorite text editor.

Open the config file:

# macOS
open -e "$HOME/Library/Application Support/Claude/claude_desktop_config.json"
# Windows
notepad "$env:APPDATA\Claude\claude_desktop_config.json"

5a. Pick your scenario

The config file is shared across every MCP server Claude Desktop knows about. Read the right scenario below before editing — adding trip-search to a file that already has other servers in it is different from setting it up from scratch.

šŸ›Ÿ Not sure which scenario you're in or worried about breaking things? Take a screenshot of your current config file (or copy-paste its contents) into a Claude chat and ask: "Merge a trip-search block into this MCP config without removing my existing servers. My venv Python path is /PASTE/PATH/FROM/STEP-4/HERE." Claude will hand back the full merged JSON. Paste that back into the file. No JSON wrangling required.


Scenario A — You've never set up an MCP server before (fresh file)

The file probably doesn't exist yet. Create it:

# macOS
mkdir -p "$HOME/Library/Application Support/Claude"
cat > "$HOME/Library/Application Support/Claude/claude_desktop_config.json" << 'JSON'
{
  "mcpServers": {
    "trip-search": {
      "command": "/PASTE/PATH/FROM/STEP-4/HERE",
      "args": ["-m", "trip_search_mcp.server"]
    }
  }
}
JSON
# Windows
New-Item -ItemType Directory -Path "$env:APPDATA\Claude" -Force | Out-Null
@'
{
  "mcpServers": {
    "trip-search": {
      "command": "C:\\PASTE\\PATH\\FROM\\STEP-4\\python.exe",
      "args": ["-m", "trip_search_mcp.server"]
    }
  }
}
'@ | Out-File -Encoding utf8 "$env:APPDATA\Claude\claude_desktop_config.json"

Then open it in a text editor and replace /PASTE/PATH/FROM/STEP-4/HERE with the path you copied in step 4. Windows users: every \ in the path must be doubled to \\.


Scenario B — You already have other MCP servers configured

Your file looks something like this (the names will differ — yours might have Outline, Slack, Figma, etc.):

{
  "mcpServers": {
    "outline": {
      "command": "...",
      "args": [...],
      "env": {...}
    }
  }
}

You need to add the trip-search block alongside your existing one(s). Open the file:

# macOS
open -e "$HOME/Library/Application Support/Claude/claude_desktop_config.json"
# Windows
notepad "$env:APPDATA\Claude\claude_desktop_config.json"

Then add "trip-search": { ... } inside mcpServers. The result should look like this:

{
  "mcpServers": {
    "outline": {
      "command": "...",
      "args": [...],
      "env": {...}
    },
    "trip-search": {
      "command": "/PASTE/PATH/FROM/STEP-4/HERE",
      "args": ["-m", "trip_search_mcp.server"]
    }
  }
}

āš ļø Two things to watch out for:

  1. Don't forget the comma after the closing } of your existing server block, before "trip-search":. Without it, the file is invalid JSON and Claude Desktop will load no MCP servers.

  2. Windows paths: every \ in the command field must be doubled (\\). Example:

    "command": "C:\\Users\\you\\trip-search-mcp\\.venv\\Scripts\\python.exe"

Save the file when done.

Lost the formatting? Paste your file's current contents (and the path from step 4) into a Claude chat and ask it to add the trip-search block for you. Way safer than hand-editing if you're not comfortable with JSON.

6. Fully quit and reopen Claude Desktop

Closing the window isn't enough. Quit from the menu bar (macOS: ⌘Q or right-click the dock icon → Quit) or from the system tray (Windows: right-click the Claude icon → Quit). Then reopen.

7. Test it

Open a new chat in Claude Desktop. Click the hammer/tools icon at the bottom of the message box — you should see trip-search with 7 always-on tools plus 4 more after step 8 below:

Tool

Needs SERPAPI_KEY?

search_flights

No

search_cheapest_dates

No

search_stays with category="airbnb"

No

get_weather_forecast

No

convert_currency

No

watch_flight_price / list_active_watches / cancel_watch

No

search_stays (default / hotels / vacation_rentals)

Yes

get_stay_details

Yes

search_events

Yes

search_activities

Yes

Ask Claude:

"Find me round-trip flights from JFK to LHR, leaving July 12 returning July 22, 1 adult, economy."

If you get a summary with prices and a "Book on Google Flights" link, you're done. Browse FEATURES.md for everything else you can ask.


Hotels, vacation rentals, events, activities, and get_stay_details use SerpAPI — free tier 100 searches/month. The flight tools, the Airbnb category, weather, currency, and watches all work without it.

  1. Sign up at serpapi.com (Google login works).

  2. Copy your key from serpapi.com/manage-api-key.

  3. Open your config file again and add an env block to the trip-search entry. Two scenarios:

    If trip-search is your only MCP server, your file becomes:

    {
      "mcpServers": {
        "trip-search": {
          "command": "/PASTE/PATH/FROM/STEP-4/HERE",
          "args": ["-m", "trip_search_mcp.server"],
          "env": {
            "SERPAPI_KEY": "paste-your-key-here"
          }
        }
      }
    }

    If you have other MCP servers alongside trip-search, only modify the trip-search block — leave the others untouched:

    {
      "mcpServers": {
        "outline": { ... },        // leave alone
        "slack":   { ... },        // leave alone
        "trip-search": {
          "command": "/PASTE/PATH/FROM/STEP-4/HERE",
          "args": ["-m", "trip_search_mcp.server"],
          "env": {                    // ← add this block
            "SERPAPI_KEY": "paste-your-key-here"
          }
        }
      }
    }

    āš ļø Don't forget the comma after "args": [...] before "env": — without it, the JSON is invalid.

  4. ⌘Q and reopen Claude Desktop. The four SerpAPI-gated tools (search_stays hotels mode, get_stay_details, search_events, search_activities) now work.

šŸ›Ÿ Again, if you'd rather not hand-edit JSON, paste your current config plus the API key into a Claude chat and ask it to add the SerpAPI env block for you. Faster than chasing missing commas.


If something doesn't work

Symptom

Fix

The trip-search server doesn't appear in Claude's tools menu

You forgot to fully quit. ⌘Q (or quit from the system tray on Windows), then reopen.

search_stays says "SERPAPI_KEY is not set"

The env block is missing or you reopened Claude before saving the config. Re-check step 8, then ⌘Q + reopen.

Claude says "the tool call timed out"

A previous Claude Desktop quit may have left a stale MCP subprocess running. Run pgrep -f trip_search_mcp — if more than 2 PIDs show up, run pkill -f trip_search_mcp.server (macOS/Linux) or End Task on every Claude process in Task Manager (Windows), then ⌘Q + reopen.

ModuleNotFoundError: No module named 'trip_search_mcp'

The command path in your config points to the wrong Python. Re-run step 4 and paste that exact path.

Airbnb search returns an upstream_error

Airbnb sometimes pushes back on scraping during high traffic. Wait a few minutes and retry. If it keeps failing, pyairbnb may need a release.

docs/SETUP.md has a longer, verbose walkthrough.


Card / button rendering — baked into the server

The MCP server publishes server-level instructions at handshake time that tell Claude:

  1. Render multi-result tool output as an HTML artifact with one card per result (not as prose).

  2. Every booking partner gets its own button, side-by-side.

These instructions load once when Claude Desktop connects to the server and persist for the whole chat — you don't have to remember to add anything to your prompts.

If you still see prose-with-markdown-links for a specific query (Claude has discretion), you can reinforce with:

"Render every multi-result tool output as an HTML/React artifact card with prominent buttons — don't summarize as prose."

Or for a single combined trip-plan artifact:

"Put the final trip plan in a single HTML artifact. Each item is a card with a big rounded 'Book on X' button — not a markdown link."

The directive lives in src/trip_search_mcp/server.py as _SERVER_INSTRUCTIONS. Edit it there if you want to tune the behavior for your own use.


Updating to the latest version

Claude Desktop spawns the MCP subprocess once at launch and keeps running it. Pulling new code doesn't reload the running process — you have to ⌘Q and reopen Claude Desktop after every update.

Recent install (within the last few weeks)

cd /path/to/trip-search-mcp
git pull
uv pip install -e .          # reinstalls in case dependencies changed

Then ⌘Q Claude Desktop and reopen.

Verify:

.venv/bin/python -c "from trip_search_mcp.server import mcp; print(mcp.name)"
# → trip-search-mcp

Updating from an older version (before the flights-mcp → trip-search-mcp rename)

Older installs used the module name flights_mcp (now trip_search_mcp). If your Claude Desktop config still says -m flights_mcp.server, the server will fail to start with ModuleNotFoundError after the update. Three things to fix:

  1. Pull and reinstall:

    cd /path/to/trip-search-mcp     # path is unchanged; GitHub redirects the old repo URL
    git pull
    uv pip install -e .              # picks up new deps including pyairbnb
  2. Edit your Claude Desktop config. Open ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows) and update the args array:

    - "args": ["-m", "flights_mcp.server"]
    + "args": ["-m", "trip_search_mcp.server"]

    Optionally rename the JSON key from "flights" to "trip-search" so the entry in Claude Desktop's tools menu matches the new docs.

  3. ⌘Q Claude Desktop and reopen.

Common gotchas during an update

Symptom

Cause / Fix

ModuleNotFoundError: No module named 'trip_search_mcp'

Your config still points at the old module name. See "Updating from an older version" above.

ModuleNotFoundError: No module named 'pyairbnb'

New dependency added since your install. Run uv pip install -e . to pick it up.

trip-search server shows "running" but new tools (search_stays, get_stay_details, watch_flight_price, …) don't appear

You didn't fully quit. Closing the window doesn't kill the subprocess on macOS or Windows. Use ⌘Q (macOS) or the system-tray Quit (Windows).

Updates seem to apply but a specific tool times out

Two MCP subprocesses may be running (Claude Desktop occasionally fails to kill the old one). Check with pgrep -f trip_search_mcp.server — if you see more than 2 PIDs, run pkill -f trip_search_mcp.server and reopen Claude Desktop.

The Claude Code CLI (not Desktop) doesn't see the updates

claude mcp commands cache server metadata. Restart your Claude Code session, or remove and re-add the server: claude mcp remove trip-search && claude mcp add trip-search -- /ABSOLUTE/PATH/TO/.venv/bin/python -m trip_search_mcp.server


For developers

.venv/bin/pytest -q          # 350 tests, all fixture-driven, no live API calls

Source layout:

src/trip_search_mcp/
ā”œā”€ā”€ server.py                FastMCP entry point — registers 7 tools
ā”œā”€ā”€ models.py                Pydantic I/O models
ā”œā”€ā”€ cache.py                 TTL response cache (tool-namespaced keys)
ā”œā”€ā”€ cities.py                City code → airport list map (27 cities)
ā”œā”€ā”€ errors.py                ErrorCode enum, ToolError, envelope helpers
ā”œā”€ā”€ logging_config.py        JSON-line file logger
ā”œā”€ā”€ tools/
│   ā”œā”€ā”€ search_flights.py
│   ā”œā”€ā”€ search_cheapest_dates.py
│   ā”œā”€ā”€ search_stays.py
│   ā”œā”€ā”€ get_stay_details.py
│   ā”œā”€ā”€ watch_flight_price.py
│   ā”œā”€ā”€ list_active_watches.py
│   └── cancel_watch.py
ā”œā”€ā”€ fli_backend/             flights — via fli library, no auth
ā”œā”€ā”€ serpapi_hotels_backend/  hotels + vacation rentals — SerpAPI
ā”œā”€ā”€ serpapi_events_backend/  concerts + festivals + sports — SerpAPI google_events
ā”œā”€ā”€ tripadvisor_backend/     things-to-do — SerpAPI Tripadvisor (ssrc=A)
ā”œā”€ā”€ airbnb_backend/          Airbnb direct — pyairbnb + Nominatim geocoding
ā”œā”€ā”€ open_meteo_backend/      weather forecasts — Open-Meteo, no auth
ā”œā”€ā”€ ecb_backend/             currency conversion — ECB daily feed, no auth
└── monitoring/              SQLite-backed price watches (lazy refresh)

Capture fresh real-data fixtures (uses live APIs — burns 1 call each):

.venv/bin/python scripts/verify_fli.py                  # flights
.venv/bin/python scripts/verify_serpapi_hotels.py       # hotels
.venv/bin/python scripts/verify_vacation_rentals.py     # rentals
.venv/bin/python scripts/verify_property_details.py     # property details

Further docs:

  • FEATURES.md — every capability, plain English, with example prompts and combined-workflow scenarios.

  • docs/SETUP.md — verbose install + troubleshooting.

  • AGENTS.md — notes for AI coding agents working on this repo (topology, gotchas, hallucination traps).

  • BACKLOG.md — completed items + new follow-ups surfaced during the work.


License

MIT.

Available Tools

11 tools
cancel_watchA

Cancel a previously created flight price watch by its watch_id. Use when the user says "stop watching that route", "cancel the Lisbon watch", "I already booked, take it off the list".

The watch is marked cancelled (not deleted), so it can still appear in list_active_watches(include_cancelled=true) if the user asks "what did I cancel?".

Returns {"watch_id": ..., "status": "cancelled"} on success.

If the user knows the route but not the watch_id, call list_active_watches first to find it, then pass the matching watch_id here.

ParametersJSON Schema
NameRequiredDescriptionDefault
watch_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavior: watch is marked cancelled (not deleted), return format on success, and implication of being able to retrieve cancelled watches. This goes beyond minimal disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

All sentences are valuable, front-loaded with core action, then examples, return format, and additional guidance. No waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple tool with 1 param and no annotations, the description covers purpose, usage, behavior, parameter meaning, and return format comprehensively. Output schema is described in-line.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter watch_id is explained beyond the schema: it is the ID from watch_flight_price or list_active_watches. Since schema coverage is 0%, the description compensates fully.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool cancels a flight price watch by watch_id, with specific verb and resource. It distinguishes from siblings like watch_flight_price and list_active_watches.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit usage scenarios (e.g., 'stop watching that route'), tells when to use list_active_watches first, and clarifies that cancelled watches are not deleted and can be listed with include_cancelled=true.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

convert_currencyA

Convert a numeric amount between two ISO 4217 currencies using the European Central Bank's daily reference rates.

USE THIS TOOL WHEN:

  • The user asks for a conversion ("how much is Ā„30,000 in euros?", "what's $200 in pounds?")

  • You're presenting mixed-currency trip totals (flights in EUR + hotel in USD + activity in GBP) and want to give one consolidated number

  • The user wants to compare prices across vendors quoting in different currencies

Inputs:

  • amount (float, > 0) — the numeric value to convert.

  • from_currency (3-letter ISO 4217 code, uppercase) — e.g. "EUR", "USD", "JPY".

  • to_currency (3-letter ISO 4217 code, uppercase) — e.g. "EUR", "USD", "GBP".

Returns:

  • converted_amount — the result, rounded to 2 decimal places in your response (the raw float is precise).

  • rate — the effective rate (1 from_currency = rate to_currency).

  • rate_date — the ISO date of ECB's published rates. ECB updates daily around 16:00 CET. Weekend / holiday queries return the previous business day's rates — disclose this if the gap is more than 3 days.

  • source — always "ECB".

Powered by the European Central Bank's daily reference rates feed (free, no API key). 29+ currencies covered (USD, EUR, JPY, GBP, CAD, AUD, CHF, SEK, NOK, DKK, INR, MXN, BRL, SGD, KRW, CNY, THB, HKD, NZD, CZK, HUF, IDR, ILS, ISK, MYR, PHP, PLN, RON, TRY, ZAR). If the user names a currency we don't recognize, we return invalid_input.

RESULT PRESENTATION: inline prose, not an artifact. Example: "Ā„30,000 = €182.45 (rate as of 13 May 2026 via ECB)." For a multi-line trip-cost summary, include the rate_date once at the bottom rather than per-line.

ParametersJSON Schema
NameRequiredDescriptionDefault
amountYes
to_currencyYes
from_currencyYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully carries the burden. It discloses important behaviors: weekend/holiday rate staleness, daily ECB updates at 16:00 CET, 29+ currencies, error handling (invalid_input), and return field details. This exceeds basic expectations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is well-structured with clear sections (USE THIS TOOL WHEN, Inputs, Returns, Powered by, RESULT PRESENTATION). However, it is somewhat verbose; a few redundant phrases could be trimmed without losing clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has 3 params and no annotations, the description covers all essential aspects: inputs, outputs, behavior, source, limitations (weekend rates), and presentation guidelines. The presence of an output schema reduces the need for return value details, but the description still adds useful context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema has 0% description coverage, so the description must compensate. It provides full semantics: amount is numeric >0, from_currency and to_currency are 3-letter ISO uppercase with examples. Also states error condition for unrecognized currencies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it converts a numeric amount between two ISO 4217 currencies using ECB rates. The verb 'convert' is specific and resource is well-defined. No sibling tool performs conversion, so distinction is clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly lists three usage scenarios ('USE THIS TOOL WHEN') including direct conversion requests and mixed-currency totals. It does not provide negative guidance (when not to use) or alternatives, but given sibling tools are unrelated, this is sufficient.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_stay_detailsA

šŸŽÆ RENDERING DIRECTIVE — READ FIRST. Render the result as a single rich HTML/React artifact card. The booking_partners array is the headline payload — surface one prominent "Book on [partner]" button per partner, side-by-side in a row, styled as HTML buttons (NOT inline markdown links). Show price per partner on the button or directly under it. The user is drilling into ONE property to book; the card should make the booking-partner choice feel like a real product page, not a wall of text.

Fetch rich per-property details for a single stay (hotel or vacation rental) the user has already seen in a search_stays result.

Takes a property_token (copied from any offer in a search_stays response) plus the same check_in/check_out dates and party size used for the original search. Returns a single StayDetails object — NOT a list.

USE THIS TOOL WHEN: the user has narrowed down to a specific property from a previous search_stays result and wants more detail before booking — typical phrasings: "tell me more about [hotel name]", "what's it like inside", "show me the booking options", "what's near it", "is breakfast included".

DO NOT USE THIS TOOL WHEN: the user is still browsing or hasn't specified a property. Use search_stays first.

Returns:

  • description: long-form prose (rentals: usually 1–2 paragraphs; hotels: 1–3 sentences).

  • booking_partners: list of OTAs offering this property with link (direct deep-link to the partner's booking flow), price_per_night, total_price, official (true if the property's own site), free_cancellation. This is the key payload — surface these as prominent "Book on X" buttons.

  • nearby_places: up to ~14 entries (airports, transit stations, restaurants, landmarks) each with name, category, latitude, longitude. Use to answer "what's nearby" questions.

  • amenities / excluded_amenities: the full lists (no top-3 truncation).

  • check_in_time / check_out_time: e.g. "3:00 PM" / "11:00 AM".

  • star_rating, review_score (0–5), review_count, location_rating.

address is NOT in the response. SerpAPI's property_details endpoint doesn't carry a postal address. Use the GPS coordinates + nearby_places to communicate location.

Costs 1 SerpAPI quota call per invocation. Cached aggressively (TTL ~5 min by default) — repeat calls for the same (token, dates) tuple are free.

RESULT PRESENTATION: Render as a single rich card with the booking_partners list prominently displayed (one button per partner, "Book on [name] — €X/night, free cancellation: yes/no"). If the user asked about a specific aspect (location, breakfast, refundability), lead with that. Surface the GPS coordinates on a small map link if you have that capability.

ParametersJSON Schema
NameRequiredDescriptionDefault
adultsNo
currencyNoEUR
check_in_dateYes
check_out_dateYes
property_tokenYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It discloses: costs 1 SerpAPI quota call, caching with TTL ~5 min, that address is NOT in response, and returns a single object not a list. No contradictions with annotations (none exist).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is relatively long but well-structured: starts with rendering directive, then purpose, usage guidelines, return fields, and caveats. Every sentence adds value, though the rendering directive could be considered separate guidance for the agent's response rather than tool behavior.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (5 params, extensive output schema with nested objects, caching, quota), the description covers all essential aspects: input parameters, output fields, usage boundaries, and behavioral notes. It also mentions what is NOT returned (address). Output schema exists but description adds context beyond it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but description explains that property_token is 'copied from any offer in a search_stays response' and that dates and adults should match the original search. It provides meaning beyond schema for key parameters, though it doesn't explicitly describe currency (default provided in schema).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states: 'Fetch rich per-property details for a single stay (hotel or vacation rental) the user has already seen in a search_stays result.' It uses specific verb (fetch) and resource (per-property details for a single stay), and distinguishes itself from sibling search_stays by specifying it is for a single property already selected.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use: 'USE THIS TOOL WHEN: the user has narrowed down to a specific property...', and when not to use: 'DO NOT USE THIS TOOL WHEN: the user is still browsing or hasn't specified a property. Use search_stays first.' It provides clear context and an explicit alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_weather_forecastA

šŸŽÆ RENDERING DIRECTIVE — READ FIRST. When this tool returns 3+ forecast days, render them as an HTML/React artifact — a horizontal day-strip or 7-day card grid with one tile per day (day name + emoji icon + high/low + precip%), NOT a paragraph or table-in-prose. 1-2 days may be prose. If used as context inside a trip plan, embed the strip inside the plan's artifact.

Get a 7-day weather forecast for a city or specific coordinates. Powered by Open-Meteo (free, global, no API key required).

USE THIS TOOL WHEN:

  • The user is planning a trip and packing or scheduling decisions hinge on weather ("will it rain in Lisbon next week", "what's the weather like in Tokyo for the second week of March")

  • The user is comparing dates and wants to bias toward sunnier ones

  • You're already showing flight or stay options and want to enrich them with a weather context line ("FYI, expect rain Thursday — bias indoor activities")

Inputs:

  • location (string) — free-text city or neighborhood. Resolved to coordinates via OpenStreetMap Nominatim. Examples: "Tampere, Finland", "Notting Hill, London".

  • OR latitude + longitude (floats) — direct coordinates, skip the geocoding step.

  • start_date (YYYY-MM-DD) — optional. Defaults to today (UTC).

  • end_date (YYYY-MM-DD) — optional. Defaults to start_date + 6 days. Hard cap: forecast horizon is 7 days from today.

  • units — "metric" (default, °C, km/h) or "imperial" (°F, mph).

Returns a GetWeatherForecastResult with:

  • location — echoed/resolved label

  • latitude, longitude, timezone — the resolved coordinates and IANA timezone

  • units — "metric" or "imperial"

  • days[] — list of WeatherDay (date, high_temp, low_temp, temp_unit, condition_summary, weather_code, precipitation_probability_percent, sunrise, sunset)

PRE-CALL ELICITATION:

  • For "weather in X" with no date hint, default to a 7-day forecast starting today.

  • For a specific date ("weather in Tokyo on Friday"), set both start_date and end_date to that date.

  • For a range ("weather in Lisbon next week"), infer the Monday→Sunday range from "next week".

  • If the user gives only a country ("weather in Italy"), ask for a specific city.

RESULT PRESENTATION:

  • For 4+ days, render as a small artifact: one row per day with date, high/low (with unit symbol), condition + a small WMO-driven emoji (ā˜€ļø partly cloudy, šŸŒ§ļø rain, ā›ˆļø thunderstorm, ā„ļø snow, ā˜ļø overcast), precip%.

  • For 1-3 days, prose is fine.

  • Always disclose units once at the top ("All temperatures in °C.").

  • If trip planning is in flight, lead with the rainy days the user should plan around.

ParametersJSON Schema
NameRequiredDescriptionDefault
unitsNometric
end_dateNo
latitudeNo
locationNo
longitudeNo
start_dateNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, but description fully carries burden: explains data source (Open-Meteo), no API key needed, geocoding via Nominatim, default date behavior, hard 7-day cap, output structure, and rendering rules. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is very long (800+ words) with redundancy between rendering directive and result presentation. However, it uses clear sections and bullet points, so readability is okay but conciseness suffers.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the output schema exists, description still thoroughly explains return fields, rendering guidelines, and pre-call behavior. Covers all key aspects for correct tool invocation and result usage.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 0% description coverage, but description compensates by detailing each parameter (location, lat/lon, start/end dates, units) with defaults, examples, and constraints (e.g., hard cap). Adds significant meaning beyond schema structure.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states it fetches a 7-day weather forecast for a city or coordinates. Distinct from siblings like search_activities, search_events. Includes specific verb and resource.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'USE THIS TOOL WHEN' section with three clear scenarios (trip planning, date comparison, enriching context). Also has 'PRE-CALL ELICITATION' with date handling defaults, providing strong guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_active_watchesA

šŸŽÆ RENDERING DIRECTIVE — READ FIRST. When 2+ watches come back, render them as an HTML/React artifact — one card per watch with a clear "Cancel" button (callable via cancel_watch(watch_id)) and, for alerted watches, a "Book now" button to the flight booking URL. Alerted watches must be visually flagged (e.g. green badge or šŸŽÆ callout). 1 watch may be prose.

List the user's active flight price watches. Re-runs any watch whose latest check is older than refresh_after_hours (default 6h) and flips status to "alerted" when the latest price is at or below the watch's threshold.

USE THIS TOOL WHEN: the user asks "any deals?", "what's the price of [route] looking like?", "show my watches", "anything trigger yet?", "did the Lisbon trip get cheaper?". Also use it proactively at the start of a session if you know the user has watches set up.

Returns a list of watch objects, each with:

  • watch_id, route (formatted "ORIGIN → DESTINATION"), departure_date, return_date

  • threshold_price, currency

  • status: "active" (no alert) or "alerted" (price hit threshold during refresh)

  • last_price / last_currency / last_offer_id: the latest observed price

  • last_checked_at: timestamp of the latest refresh

  • alerted_at: when the alert fired (null if not alerted)

  • note: the user's optional note from creation time

  • gap: numeric last_price - threshold_price (negative = below threshold, positive = above). Use this to summarize "X EUR below your target" or "still Y EUR above target".

Each refresh costs ONE fli call. Refresh frequency is bounded by refresh_after_hours — repeated calls within the cutoff window are free (no fli traffic). If you want a forced refresh, pass refresh_after_hours=0.

RESULT PRESENTATION: If any watch is alerted, lead with it (a small "šŸŽÆ Deal!" callout works well). For non-alerted watches, show the current gap ("currently 53 EUR above target, last checked 2h ago"). For never-checked watches, say so.

If include_cancelled=true, also include watches the user cancelled — useful when they ask "show me everything" or "what did I cancel last week?".

ParametersJSON Schema
NameRequiredDescriptionDefault
include_cancelledNo
refresh_after_hoursNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully explains behavior: re-runs watches, flips status, costs one call per refresh, bounded by refresh_after_hours, forced refresh option. It also details return fields and side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is overly long with rendering directives and result presentation tips that could be separate. It is well-structured but not concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers all aspects: purpose, usage, parameters, behavior, return fields, and even rendering suggestions. It is complete given the lack of annotations and output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Despite 0% schema coverage, the description explains both parameters: include_cancelled for listing cancelled watches, and refresh_after_hours for refresh frequency, including the forced refresh usage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool lists active flight price watches, re-runs old checks, and flips status on threshold hits. It distinguishes itself from siblings like cancel_watch and watch_flight_price.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit usage examples are given: user queries like 'any deals?' or 'show my watches', and proactive use at session start. It provides clear context for when to invoke.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_activitiesA

šŸŽÆ RENDERING DIRECTIVE — READ FIRST. When this tool returns 2+ activities, you MUST present them as an interactive HTML/React artifact (Artifacts block, not flowing prose). Each activity is a visually distinct CARD with a prominent "Find on Tripadvisor" button styled as an HTML button, NOT an inline markdown link. Include the activity-type badge (Sight / Experience) prominently. If the call is part of a larger trip plan, the plan itself should be an artifact containing the activity cards. Single-result responses may use prose.

Search Tripadvisor's "Things to Do" (sights + bookable experiences/tours) for a location, optionally filtered by free-text query, place type, and minimum rating.

DISTINCT FROM search_events: activities are ongoing (visit a museum, take a cooking class anytime); events are date-specific (a concert on June 21). Use this for "what should I do in X"; use search_events for "what's happening while I'm there".

USE THIS TOOL WHEN:

  • The user asks "what should I do in X" / "things to do in X" / "tours in X"

  • They name an activity type ("cooking classes", "boat tours", "museums", "wine tasting")

  • They want recommendations based on their preferences

Inputs:

  • location (string, required) — free-text city or neighborhood. Combined with query into a single Tripadvisor search.

  • query (string, optional) — free-text filter on activity type. Natural language works: "cooking class", "boat tours", "wine tasting", "free walking tour".

  • place_type_filter (enum, optional, default "both") — one of "sights" (free attractions like museums, viewpoints), "experiences" (bookable tours), or "both" (default).

  • min_rating (float, optional) — minimum review score 0.0-5.0. Results without a rating are excluded when this is set.

  • max_results (int, optional, default 15) — 1-50.

Returns ActivityOffer entries each with:

  • offer_id — Tripadvisor's place_id, stable per activity. Use this to drill in via get_activity_details (when implemented).

  • name — activity name.

  • activity_type — "sight" (free, non-bookable) or "experience" (bookable tour).

  • rating, review_count — 0-5 scale (Tripadvisor's native).

  • description — short prose (often missing on generic city searches; usually present on specific-activity searches).

  • location — text "City, Country".

  • thumbnail — URL (NOT hotlink-safe — don't render as a photo element).

  • highlighted_review — {text, mention_count} — a relevant review snippet.

  • booking_url — Tripadvisor listing URL. For experiences, this is the path to Viator tickets; for sights, it's the info page.

No coordinates and no price. Tripadvisor's search endpoint surfaces neither. Use get_activity_details(offer_id) (when implemented) to get price + duration + a direct Viator URL for bookable experiences.

PRE-CALL ELICITATION — three branches:

Branch 1: User names a specific activity type. "Find cooking classes in Lisbon." → query="cooking class". Search immediately.

Branch 2: User asks for a recommendation. "What should I do in Lisbon?" — before searching, infer the user's interests from conversation context + your own memory of them ("they love food and wine", "they're into history"). Bake the interest into query. NOTE: The MCP tool does NOT read Claude's memory — you (Claude) do the inference and pass the resulting query string. If memory yields nothing actionable, fall to Branch 3.

Branch 3: User is vague and you have no preference signal. "Things to do in Lisbon?" with empty conversation context. Ask ONE clarifying question: "Any particular interest — food, history, outdoors, nightlife?" Then search.

RESULT PRESENTATION:

  • Card-based artifact, one card per result.

  • For Branch 2 (memory-driven), preamble: "Based on your interest in food and wine, here are top-rated experiences in Lisbon." — makes the inference legible.

  • Card content: name, activity_type badge (Sight / Experience), rating + review_count, location, the highlighted_review.text as a 1-line testimonial, "Find on Tripadvisor" button → booking_url.

  • Do NOT render thumbnail as a photo element (Tripadvisor's CDN hotlink-protects). Same no-photos rule as stays/events.

  • For a single result, prose is fine.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNo
locationYes
min_ratingNo
max_resultsNo
place_type_filterNoboth

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses limitations (no coordinates/price), rendering directive, pre-call elicitation strategy, and return format details beyond the input schema, fully compensating for missing annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with front-loaded rendering directive, sections for usage, inputs, returns, and presentation. Every sentence adds value; no fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Completely covers rendering, parameter details, return field descriptions, limitations, and pre-call behavior, making it self-sufficient for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Despite 0% schema coverage, each parameter is explained with context and examples (e.g., query accepts natural language, place_type_filter enum values, min_rating excludes unrated results).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states it searches Tripadvisor's 'Things to Do' with specific verb 'Search' and distinguishes from sibling tool `search_events` by contrasting ongoing activities vs date-specific events.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly lists when to use ('what should I do in X', 'things to do in X', etc.) and when not to (use search_events for date-specific events), plus three detailed pre-call branches.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_cheapest_datesA

šŸŽÆ RENDERING DIRECTIVE — READ FIRST. When this tool returns 5+ entries, render them as an HTML/React artifact — a small price-grid or chart, NOT a long flowing list. For 1-4 entries, prose is fine. The cheapest 1-2 dates should be visually highlighted. Offer to deep-dive into the cheapest date with search_flights once the user picks one.

Find which travel dates are cheapest across a flexible range, using Google Flights data.

Returns a list of (departure_date, return_date, price) entries sorted cheapest first. Does not return flight times, airlines, or layover details — for that, use search_flights once the user picks a date.

USE THIS TOOL WHEN: the user is flexible on travel dates and wants to know which dates within a range are cheapest. Typical phrasings: "any week in May", "next month sometime", "around the second week of June", "is it cheaper if I shift my trip a few days?".

USE search_flights INSTEAD WHEN: the user has specific dates and wants flight details, airlines, departure times, layovers, and bookable offers.

The currency Google Flights returns is determined by the request region and is surfaced in each entry's currency field; do not assume USD.

For round-trip date searches, trip_duration (in days) is required — it determines each candidate return date. The tool returns a (departure_date, departure_date + trip_duration) pair per result. For one-way, return_date in each result is null.

Filter parameters mirror search_flights:

  • max_stops: one of ANY (default), NON_STOP, ONE_STOP_OR_FEWER, TWO_OR_FEWER_STOPS. "Or fewer" semantics.

  • departure_window: a "HH-HH" string in 24-hour local time, applied to the outbound departure. Hours are inclusive of the start and EXCLUSIVE of the end — "8-20" matches 08:00 through 19:59 local time.

  • airlines: an optional list of IATA airline codes. Shows date entries where AT LEAST ONE of the listed airlines operates ANY segment. For example, ["FI"] returns dates with options operated entirely or partly by Icelandair; it does NOT restrict to Icelandair-only itineraries. Omit or pass null for no airline filter.

PRE-CALL ELICITATION: Before calling this tool, ensure the user has expressed:

  • Date range: a clear earliest acceptable departure (start_date) and latest acceptable departure (end_date). If they said "next month" or "sometime in May" without bounds, ask. The wider the range, the slower and noisier the result.

  • Trip duration (round-trip only): the number of nights/days they want to be away. "About 10 days" needs to become a concrete trip_duration integer.

  • What flexibility actually means to the user: are they only flexible on departure date, or also on trip length? If trip length is flexible, run this tool multiple times with different trip_duration values; this tool only varies departure within one duration.

RESULT PRESENTATION: Render the results as a sorted list with the cheapest entries highlighted, or a small date grid if the range is short. Each entry shows the departure date, the return date (if round-trip), and the total price with currency. Lead with the cheapest. Offer to deep-dive into a specific date with search_flights once the user picks one.

ParametersJSON Schema
NameRequiredDescriptionDefault
originYes
airlinesNo
end_dateYes
max_stopsNoANY
passengersNo
start_dateYes
cabin_classNoECONOMY
destinationYes
is_round_tripNo
trip_durationNo
departure_windowNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It discloses result format (sorted list of date combinations and price), what is not returned (flight details), parameter semantics (e.g., departure_window exclusivity, airline filter behavior), currency behavior, and performance implications. It does not explicitly state the tool is read-only, but the context implies it, and the level of detail is high.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with section headers and bullet points, front-loading the purpose. However, it is somewhat verbose, particularly the rendering directive and pre-call elicitation sections, which could be condensed without losing essential guidance. Nonetheless, every part serves a clear purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (11 parameters, output schema present), the description is complete. It covers purpose, usage, parameter details, output format (list of entries with date and price), rendering guidance, and follow-up actions. The presence of an output schema reduces the burden, but the description still explains the output structure adequately.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema provides only types and defaults with 0% description coverage. The description compensates thoroughly by explaining every parameter's meaning, format, and behavior, including edge cases like trip_duration requirement for round trips, departure_window exclusivity, airlines filter semantics, and default values. This adds significant value beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Find which travel dates are cheapest across a flexible range' and distinguishes it from the sibling tool 'search_flights' by explicitly stating what it does not return and providing usage scenarios. The verb 'find' and resource 'cheapest travel dates' are specific and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description includes explicit sections 'USE THIS TOOL WHEN' and 'USE search_flights INSTEAD WHEN' with typical user phrasings, and provides a 'PRE-CALL ELICITATION' section detailing required user inputs. This offers comprehensive guidance on when to invoke this tool versus alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_eventsA

šŸŽÆ RENDERING DIRECTIVE — READ FIRST. When this tool returns 2+ events, you MUST present them as an interactive HTML/React artifact (Artifacts block, not flowing prose). Each event is a visually distinct CARD with one "Tickets on [vendor]" button per ticket_sources entry (or a single button on ticket_url if no extras), styled as HTML buttons, NOT inline markdown links. If the call is part of a larger trip plan, the plan itself should be an artifact containing event cards. Single-result responses may use prose.

Search Google for time-bound events — concerts, festivals, sports games, comedy shows, conferences — happening at a location, optionally filtered by event type and date range.

DISTINCT FROM search_activities: events are time-bound (a specific date or window); activities (tours, attractions) are ongoing. Use this tool for "what's on while I'm there"; use search_activities for "what should I do".

USE THIS TOOL WHEN:

  • The user asks "what's happening in X" / "events in X" / "any concerts in X" / "is BTS playing anywhere I'm going"

  • They mention a specific event type ("concerts", "festivals", "sports", "comedy", "theatre")

  • They're planning a trip and want time-bound options to anchor the dates around

Inputs:

  • location (string, required) — free-text city. Combined with query into the SerpAPI search string.

  • query (string, optional) — event-type filter. Examples: "concerts", "festivals", "sports", "comedy", "theatre", or a specific artist/team ("BTS", "Coldplay").

  • date_filter (enum, optional) — one of "today", "tomorrow", "week", "weekend", "next_week", "month", "next_month". SerpAPI's named-range filter; do NOT pass arbitrary date strings. If the user wants a specific calendar month, bake the month name into query instead (e.g. query="concerts June 2026").

  • max_results (int, optional, default 15) — 1-50.

Returns up to max_results EventOffer entries, each with:

  • offer_id — stable hash for downstream reference

  • title — event name

  • start_date_raw — SerpAPI's "Jun 21" style string (month + day, no year — when_text carries the year)

  • when_text — full formatted display string: "Fri, Jul 17, 8 – 11 PM GMT+2"

  • venue_name, venue_rating, venue_review_count — venue info if available

  • address — flattened single string ("B.Leza Club, Cais do GĆ”s 1, Lisbon, Portugal")

  • description — short text from Google

  • thumbnail, image — URLs (NOT hotlink-safe — same rule as stays, don't render as photo elements)

  • ticket_url — primary deep-link to the ticket vendor (Viagogo, Eventbrite, Spotify Concerts, venue site — varies per event)

  • ticket_sources — list of additional ticket vendors with {source, link} per entry. Surface all of these as "Tickets on X" buttons so the user can comparison-shop.

PRE-CALL ELICITATION:

  • If the user names an event type, set query. "Concerts in Lisbon" → query="concerts".

  • If they mention a relative date ("this weekend", "next week", "this month"), set date_filter to the matching enum.

  • If they mention a specific calendar month + year, bake it INTO the query string ("concerts June 2026") instead of using date_filter.

  • If they're vague ("things happening in Lisbon"), call with no query and no date_filter — default upcoming events.

RESULT PRESENTATION: card-based artifact, one card per event. Lead with title + when_text + venue_name. Show "Tickets on [source]" buttons (one per ticket_sources entry); for events with no ticket_sources, surface the primary ticket_url as a single button. Do NOT render thumbnail/image as photo elements (same hotlink-protection issue as stays). For a single result, prose is fine.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNo
locationYes
date_filterNo
max_resultsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses the tool's behavior: data source (Google via SerpAPI), output structure (EventOffer fields), rendering constraints (artifact vs prose), image handling (hotlink warning), and pre-call logic. No contradictions and exceeds the burden for safe invocation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections and a front-loaded rendering directive. However, it is quite verbose; some instructions could be condensed without losing clarity. Still, every sentence serves a purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity and the presence of an output schema (described in text), the description is exceptionally complete: covers purpose, usage, parameters, output fields, rendering behavior, sibling differentiation, and pre-call elicitation. No obvious gaps remain for an agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description compensates fully: explains `location` as free-text, `query` with examples, `date_filter` with enum values and usage warnings, and `max_results` with default and range. Pre-call elicitation clarifies mapping from user requests to parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with a specific verb ('Search Google for') and resource ('time-bound events'). It explicitly distinguishes from the sibling tool `search_activities` by contrasting time-bound events with ongoing activities, leaving no ambiguity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description includes a dedicated 'USE THIS TOOL WHEN' section with specific user query examples, and explicitly contrasts with `search_activities`. Pre-call elicitation rules further guide parameter selection, making usage conditions extremely clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_flightsA

šŸŽÆ RENDERING DIRECTIVE — READ FIRST. When this tool returns 2+ flight offers, you MUST present them as an interactive HTML/React artifact (an Artifacts block, not flowing prose). Each offer is a visually distinct CARD with a prominent "Book on Google Flights" button styled as an HTML button or large rounded link, NOT an inline markdown hyperlink. Plain prose with [text](url) links is NOT acceptable for multi-result responses — the user can't click a paragraph. If this call is part of a larger trip-plan response, the trip plan ITSELF should be an artifact and contain the flight cards. Single-result responses may use prose.

Search live flight offers for a given route and date range using Google Flights data.

Returns a ranked list of flight options with prices, airlines, segment details, and total trip durations. Does not book flights, only searches.

Times in the response are local to the departure or arrival airport, with the airport's IATA code attached so the timezone can be derived. Do not perform timezone math on these times without first converting them.

Origin and destination accept 3-letter IATA airport codes (HEL, JFK, LHR) AND city codes (WAS, NYC, LON, PAR, TYO, LAX/QLA, BOS, …). City codes auto-expand to the metro's busiest 3 airports and search them in parallel; results merge under one ranked list (cheaper variant wins on dedup). Use the airport code when the traveler insists on a specific airport. The currency Google Flights returns is determined by the request region and is surfaced in each offer's currency field; do not assume USD.

Filter parameters:

  • max_stops: one of ANY (default), NON_STOP, ONE_STOP_OR_FEWER, TWO_OR_FEWER_STOPS. The names mean "this many stops or fewer".

  • departure_window: a "HH-HH" string in 24-hour local time, e.g. "8-20" to restrict to outbound departures between 8am and 8pm local. Hours are inclusive of the start and EXCLUSIVE of the end — "8-20" matches 08:00 through 19:59 local time; a 20:00 or 20:30 departure does NOT match. Applies to the outbound leg only. Google Flights' native filter does not control the return leg.

  • inbound_window: a separate "HH-HH" window for the return leg. Same format and same inclusive-start/exclusive-end semantics as departure_window. Has no effect on one-way searches. When set, offers whose return-leg first segment departs outside this window are filtered out post-hoc.

  • airlines: an optional list of IATA airline codes. Shows offers where AT LEAST ONE of the listed airlines operates ANY segment of the itinerary. For example, ["FI"] returns options operated entirely or partly by Icelandair; it does NOT restrict to Icelandair-only itineraries. Omit or pass null for no airline filter.

Results from identical searches are cached for up to 5 minutes. If the user is about to act on a specific offer, re-run the search before committing to a number.

Dates must be today or future in UTC. The tool rejects past dates with an invalid_input error — if a user gives a date that may already be past in their local timezone, advance to the next valid day before calling.

Several fields are commonly null with this data source: baggage_allowance, last_ticketing_date, and seats_available. A null baggage_allowance means "the carrier did not surface this information," not "no checked bag is included." Do not state that a fare excludes checked bags based on a null value.

PRE-CALL ELICITATION: Before calling this tool, ensure the user has expressed preferences on the following. If any are unspecified, ask the user before searching. Do not assume defaults; results vary materially based on these.

  • Baggage: carry-on only, or checked bag needed (affects fare class and final price)

  • Connections: non-stop preferred, or okay with stops (sets max_stops)

  • Time of day: red-eye okay, hard arrival deadlines, preferred outbound departure window (sets departure_window), preferred return departure window (sets inbound_window)

  • Airline preferences: any airlines to prefer (loyalty programs) or avoid (sets airlines)

RESULT PRESENTATION: When returning 2 or more results to the user, render them as an interactive artifact rather than a text list. Each offer is a card showing:

  • Total price, prominent

  • Airlines (IATA codes)

  • Total trip duration and stop count for each leg

  • Departure and arrival times for outbound and inbound, labeled with airport codes

  • A "Book on Google Flights" button linking to the offer's booking_url, opening in a new tab

Sort cards by price ascending. For a single result, prose is fine.

ParametersJSON Schema
NameRequiredDescriptionDefault
adultsNo
originYes
infantsNo
airlinesNo
childrenNo
max_stopsNoANY
cabin_classNoECONOMY
destinationYes
max_resultsNo
return_dateNo
departure_dateYes
inbound_windowNo
departure_windowNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully carries the behavioral transparency burden. It discloses caching behavior (5-minute cache), date validation (rejects past dates), meaning of null fields (baggage_allowance, etc.), timezone handling (local times with IATA codes), city code expansion, and detailed filter semantics. No contradictions with annotations since none exist.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections but is overly long, including a rendering directive and pre-call elicitation that could be separated. While every sentence adds value, the length reduces conciseness. It front-loads the most important info but could be more succinct.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (13 parameters, 3 required, output schema exists), the description covers all critical aspects: parameter semantics, behavioral nuances, error handling, caching, and result presentation. It is comprehensive and leaves no major gaps for an AI agent to misuse the tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It explains critical parameters like origin/destination (IATA and city codes), max_stops (with exact meanings), departure_window and inbound_window (inclusive-start/exclusive-end semantics), and airlines filter behavior. However, it does not explain simpler parameters like adults, children, infants, cabin_class, and max_results, leaving some gaps.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Search live flight offers for a given route and date range using Google Flights data.' It uses a specific verb ('search') and resource ('flight offers'), and implicitly distinguishes from sibling tools like search_cheapest_dates and watch_flight_price by focusing on live searches for specific routes and dates.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides extensive 'PRE-CALL ELICITATION' guidance on what user preferences to collect before calling, which indirectly informs when to use the tool. However, it does not explicitly contrast with sibling tools or state when not to use this tool, missing a direct exclusion statement.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_staysA

šŸŽÆ RENDERING DIRECTIVE — READ FIRST. When this tool returns 2+ stay offers, you MUST present them as an interactive HTML/React artifact (Artifacts block, not flowing prose). Each stay is a visually distinct CARD with a prominent "Book on Google Hotels" button styled as an HTML button, NOT an inline markdown hyperlink. Plain prose with [text](url) links is NOT acceptable for multi-result responses — the user can't click a paragraph. If this call is part of a larger trip-plan response, the trip plan ITSELF should be an artifact containing the stay cards. Single-result responses may use prose.

Search Google's hotel AND vacation rental listings for a city, date range, and party size, returning a ranked unified list of available places to stay.

Returns ranked stay offers — each with name, photos, star rating (hotels only), review score, price (per-night and total), top amenities, GPS coordinates, a category badge ("hotel" or "vacation_rental"), per-property OTA price comparison via sources, and a Google Hotels deep link. Does NOT book — the booking_url opens the specific property's Google Hotels entity page with the user's check-in/check-out pre-filled, where they can click through to a booking partner.

category selector:

  • "all" (default) — fans out to TWO SerpAPI calls in parallel: one for hotels, one for vacation rentals. Merges, dedupes, sorts. Latency is ~3s (parallel, not summed). Costs 2 SerpAPI calls instead of 1 per query — burns SERPAPI quota twice as fast.

  • "hotels" — only hotel-class properties. One SerpAPI call.

  • "vacation_rentals" — only short-term rentals via SerpAPI's aggregation. Surfaces OTAs (Booking.com, Hotels.com, Bluepillow.com, Vrbo.com when available). Airbnb is NOT in Google's aggregation — use category="airbnb" for that.

  • "airbnb" — bypasses SerpAPI entirely and queries Airbnb directly (via the pyairbnb library). Use this when the user specifically asks for "Airbnb" / "AirBNB" / "stuff on Airbnb". Costs NO SerpAPI quota but is slower and more fragile (Airbnb may block scraping during high traffic). Filter parameters supported: min_bedrooms, min_bathrooms, min_review_score, max_price_per_night. min_rating is ignored (Airbnb listings have no hotel-class star rating).

sources is a per-offer list of (name, price_per_night) entries showing the same property listed across different booking partners. Empty list for hotels in the current data (SerpAPI doesn't surface partner prices for hotels in our queries). Populated for vacation rentals.

Prices come back in EUR by default (matches the flights tool's typical response currency for European-IP users). Pass currency (ISO 4217, e.g. "USD", "JPY", "GBP") to override per call. The currency field on each offer reflects what was actually requested.

Filter scoping (important — the wrong filter on the wrong category is silently dropped):

  • min_rating (1-5 stars) applies only to hotels. When category="all", it filters the hotel side; vacation rentals pass through unfiltered (they have no hotel class).

  • min_bedrooms and min_bathrooms apply only to vacation rentals. Filter the rental side; hotels pass through.

  • min_review_score, max_price_per_night, required_amenities, sort_by, max_results, currency apply uniformly.

address is always null on offers — SerpAPI's google_hotels list endpoint doesn't carry per-property addresses. Use latitude/longitude for location.

The review score is Google's native 0-5 scale (e.g., 4.6 / 5), NOT a 0-10 scale.

sort_by accepts: BEST (preserve SerpAPI's returned order; for the merged path this falls back to price-ascending as the tie-breaker since neither response has a globally meaningful rank), PRICE_LOW, PRICE_HIGH, RATING (star rating descending; hotels-only signal), REVIEW_SCORE (review_score descending, review_count tie-break).

PRE-CALL ELICITATION: Before calling this tool, confirm with the user:

  • Type of stay (category): default to "all" unless the user signals otherwise. "Find me a place to stay in Lisbon" stays at "all". "Find me a nice hotel in Lisbon" → "hotels". "Find me a rental in Lisbon" → "vacation_rentals". "Find me an Airbnb in Lisbon" → "airbnb" (this hits Airbnb directly; SerpAPI doesn't include Airbnb listings).

  • Location: specific city or neighborhood — "Tampere" works, "Notting Hill, London" works, "somewhere in Europe" does not. Ask if vague.

  • Check-in and check-out dates: both required and check_out must be strictly after check_in. Confirm UTC-today or later.

  • Party size: adults, children, and number of rooms. Default is 2 adults / 0 children / 1 room — don't assume; ask if not stated.

  • Budget: any per-night ceiling? If the user said "cheap" or "affordable", ask for a concrete number to set max_price_per_night.

  • Must-have amenities: wifi, breakfast, parking, gym, pool, pet-friendly? Don't assume; ask.

  • Star rating or review score floor: "at least 4 stars", "well-reviewed (8+)"? Map to min_rating or min_review_score (remember review_score is 0-5, so "8+" should become min_review_score=4.0 or you should ask for clarification).

  • Rental size: if the user mentioned bedrooms or bathrooms ("a 2-bedroom apartment"), set min_bedrooms / min_bathrooms. These constrain the vacation-rental side only.

  • Sort priority: cheapest first, highest-rated, best location? Map to sort_by.

  • Currency: infer from the user's stated location or budget. "I'm in Tokyo, budget Ā„30000/night" → currency="JPY", max_price_per_night=30000. "$200/night in NYC" → currency="USD". Default "EUR" if the user gives no signal. Always pass the currency that matches the units the user spoke in for max_price_per_night — mixing currencies silently corrupts the budget filter.

RESULT PRESENTATION: When returning 2+ stays, render them as an interactive artifact with one card per offer. Each card shows:

  • The stay name, prominent and large at the top of the card (it carries the card's visual hierarchy in the absence of a photo).

  • A small category badge at the top: Hotel or Vacation rental, taken from the category field.

  • Star rating (hotels only — render as filled stars if you can) and review_score with review_count: "4.6 / 5 (686 reviews)".

  • For vacation rentals, surface bedrooms / bathrooms / sleeps inline if present (e.g. "2 BR Ā· 2 BA Ā· sleeps 6").

  • Price per night with the total alongside in smaller text, in the response currency.

  • For offers with a non-empty sources array, show "from €X on [cheapest source]" with a smaller "also on [other sources]" note when 2+ sources are present.

  • Top 3-4 amenities pulled from the amenities list.

  • Short description if present (hotels only — rentals leave this null and surface essential_info via the bedrooms/bathrooms/sleeps fields above).

  • A "Book on Google Hotels" button linking to booking_url, opening in a new tab.

Do NOT render the images field as photo elements. Hotel image CDNs (Google's signed gps-cs-s URLs, hotelbeds, trvl-media, bstatic, giata) use hotlink protection that breaks these URLs outside their intended hosts; broken images degrade the card more than missing photos. The images field stays on the response model for future use (e.g., a server-side image proxy layer), but card rendering should be text-only. Compensate for the missing visual hierarchy by making the stay name larger and prominent at the top of each card.

If the response has a non-empty warnings array, surface them verbatim above the cards (e.g., "Note: vacation rental data was unavailable for this query; showing hotels only."). Do NOT silently swallow them.

Sort cards by the same sort_by the user requested. For a single result, prose is fine.

ParametersJSON Schema
NameRequiredDescriptionDefault
roomsNo
adultsNo
sort_byNoBEST
categoryNoall
childrenNo
currencyNoEUR
locationYes
min_ratingNo
max_resultsNo
min_bedroomsNo
check_in_dateYes
min_bathroomsNo
check_out_dateYes
min_review_scoreNo
required_amenitiesNo
max_price_per_nightNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Since no annotations are provided, the description carries the full burden of disclosing behavioral traits. It extensively covers: use of SerpAPI with parallel calls for 'all' category (~3s latency, 2x quota cost), airbnb category being slower and more fragile, default currency (EUR), filter scoping (min_rating only hotels, etc.), address always null, review scale 0-5, sort_by options, rendering requirements (artifact with cards, no images due to hotlink protection), and warnings handling. No contradictions are present.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very long and verbose, containing extensive sections on pre-call elicitation and result presentation that could be abbreviated or separated. While well-organized (rendering directive first, then purpose, parameters, usage context), its length may overwhelm quick parsing. Not every sentence is essential; some repetition occurs (e.g., category descriptions appear in multiple places). Conciseness is moderately compromised.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (16 parameters, 4 categories, filter scoping, rendering rules, output schema exists), the description is exceptionally complete. It covers input semantics, behavioral traits, output form, rendering directives, pre-call elicitation, and warning handling. The presence of an output schema reduces the need to describe return values. Sibling differentiation is clear.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage, so the description must fully document parameters. It does so comprehensively: explains each category value, sort_by options, filter applicability (min_rating for hotels, min_bedrooms/min_bathrooms for rentals), currency override, and max_price_per_night. It also clarifies that 'address is always null', 'review_score is 0-5', and provides conversion guidance. All 16 parameters are effectively covered.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: 'Search Google's hotel AND vacation rental listings for a city, date range, and party size, returning a ranked unified list of available places to stay.' It explicitly distinguishes itself from siblings like search_flights and get_stay_details by specifying it searches for stays across hotels and vacation rentals, and it does not book reservations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides detailed guidance on when to use this tool, including a 'PRE-CALL ELICITATION' section that instructs the agent to confirm parameters like category, location, dates, etc., with the user. It differentiates between categories (all, hotels, vacation_rentals, airbnb) and specifies when each is appropriate. However, it does not explicitly mention that get_stay_details should be used for more details on a specific property, which would strengthen usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

watch_flight_priceA

Register a persistent watch on a specific flight route, departure date, and price threshold. When the user later asks "any deals?", list_active_watches re-runs the search and reports whether the latest price has dropped to or below the threshold.

USE THIS TOOL WHEN: the user says something like "watch this route", "tell me if the price drops below X", "alert me if Y becomes cheaper", "monitor flights from A to B around date Z".

DO NOT USE THIS TOOL FOR ONE-OFF SEARCHES — use search_flights for those.

Inputs are the same shape as search_flights (origin, destination, departure_date, optional return_date, etc.) plus:

  • threshold_price: numeric ceiling in the currency you also pass. The watch "fires" (status='alerted') when a refresh observes price ≤ threshold.

  • currency: ISO 4217 currency code (e.g. "EUR", "USD"). Must match the units of threshold_price.

  • note: optional free-text reminder ("for parents' anniversary", "cap to budget for Q3").

Returns the new watch's watch_id (a 12-character hex string). Hand it back to the user so they can cancel later with cancel_watch(watch_id).

The watch persists across restarts (it's in SQLite under ~/.trip-search-mcp/watches.db). Closing Claude Desktop doesn't lose your watches.

PRE-CALL ELICITATION:

  • Confirm the route and dates the user wants to watch.

  • Confirm the threshold price AND its currency explicitly — mixing currencies silently breaks the alert logic. Example: "I want to fly to Tokyo if it drops below 800 EUR" → threshold_price=800, currency="EUR".

  • If the user said "any time" or "flexible dates", offer to use search_cheapest_dates first to pick a candidate date, then watch THAT specific date.

The watch makes ONE fli call when refreshed (per active watch). Refresh frequency is controlled by list_active_watches.refresh_after_hours (default 6h).

ParametersJSON Schema
NameRequiredDescriptionDefault
noteNo
adultsNo
originYes
currencyNoEUR
max_stopsNoANY
cabin_classNoECONOMY
destinationYes
return_dateNo
departure_dateYes
threshold_priceYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, but the description fully covers behavioral traits: persistence across restarts via SQLite, refresh frequency controlled by list_active_watches, single API call per refresh, and alerting condition (price ≤ threshold).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is relatively long but well-structured with clear sections (USE THIS, DO NOT USE, Inputs, Returns, PRE-CALL ELICITATION). It is front-loaded with the main purpose. Slightly verbose but still effective.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 10 parameters, 4 required, and no schema descriptions, the description covers crucial aspects: watch behavior, parameter semantics for threshold and currency, return value (watch_id), persistence, and pre-call elicitation. Could elaborate on more parameters, but the reference to search_flights mitigates this.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description explains key parameters: threshold_price, currency, and note. It states inputs are the same shape as search_flights for origin, destination, etc., though not all 10 parameters are individually described. The cross-reference is acceptable.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool registers a persistent watch on a flight route, departure date, and price threshold. It distinguishes itself from sibling tools like search_flights (one-off) and list_active_watches (listing/refreshing).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly provides when-to-use scenarios (e.g., 'watch this route') and when-not-to (one-off searches, redirecting to search_flights). Also suggests alternatives like search_cheapest_dates for flexible dates.

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.

  1. 11 tool updatesv0.2.0
    • First observedcancel_watch
    • First observedconvert_currency
    • First observedget_stay_details
    • First observedget_weather_forecast
    • First observedlist_active_watches
    • First observedsearch_activities
    • First observedsearch_cheapest_dates
    • First observedsearch_events
    • First observedsearch_flights
    • First observedsearch_stays
    • First observedwatch_flight_price

TDQS

A4.6/5.0

Scored across 11 tools

Disambiguation5/5

Each tool targets a distinct domain or action: flight searching, price watching, stays, activities, events, weather, and currency conversion. There is no overlap between tools; even the flight-related tools (search_flights, search_cheapest_dates, watch_flight_price, list_active_watches, cancel_watch) have clearly differentiated purposes.

Naming Consistency3/5

Tool names use a mix of verb styles: 'search_' for five tools, 'get_' for two, and individual verbs like 'cancel_', 'convert_', 'list_', and 'watch_'. While each name is clear, the lack of a uniform pattern makes the set slightly less predictable.

Tool Count5/5

With 11 tools, the server covers essential trip planning needs—flight search, price monitoring, stay search, activities, events, weather, and currency conversion—without unnecessary bloat. The count is well-scoped for its domain.

Completeness4/5

The tool surface covers core trip planning tasks comprehensively. Minor gaps exist: get_activity_details is referenced but not implemented, and there is no tool for booking flights or stays (though that may be out of scope). Overall, agents can accomplish most planning workflows without dead ends.

Maintenance

ActivityInactive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    F
    maintenance
    Enables comprehensive travel planning by integrating Google Travel Services and Amadeus GDS for dual flight and hotel searches, plus event discovery, weather forecasting, currency conversion, and location services. Combines consumer-friendly search with professional travel industry data for optimal trip planning.
    6
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables Claude to search and compare flights across multiple providers (Skyscanner, Google Flights, Kiwi.com) with smart caching, parallel queries, and flexible filtering.
    1
    MIT