Skip to main content
Glama

plan_trip

Create a day-by-day travel itinerary integrating weather, attractions, dining, and flights based on your destination, dates, origin, budget, and interests.

Instructions

Generate a complete day-by-day travel itinerary combining weather, attractions, dining, and travel options.

Args: destination: Travel destination city (e.g. "Tokyo", "Paris") start_date: Trip start date in YYYY-MM-DD format end_date: Trip end date in YYYY-MM-DD format origin: Departure city for flight search (e.g. "New York", "London") total_budget: Total trip budget in USD — enables budget planning and suggested allocation (optional) preferences: Comma-separated interests to tailor the itinerary (e.g. "food, history, beaches")

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
destinationYes
start_dateYes
end_dateYes
originYes
total_budgetNo
preferencesNo

Implementation Reference

  • Core handler: the `plan()` function that executes the plan_trip tool logic. It gathers weather, POI attractions/restaurants/activities/nightlife, flight and hotel options, then builds a day-by-day itinerary combined with optional budget allocation.
    def plan(
        destination: str,
        start_date: str,
        end_date: str,
        origin: str,
        total_budget: float | None = None,
        preferences: str | None = None,
    ) -> dict:
        try:
            start = datetime.strptime(start_date, "%Y-%m-%d")
            end = datetime.strptime(end_date, "%Y-%m-%d")
        except ValueError:
            return {"error": "Invalid date format. Use YYYY-MM-DD."}
    
        days = (end - start).days + 1
        if days < 1:
            return {"error": "end_date must be after start_date."}
    
        # Gather supporting data
        weather = query_forecast(destination, start_date, end_date)
        attractions = query_poi(destination, "attractions", 10)
        restaurants = query_poi(destination, "restaurants", 10)
        activities = query_poi(destination, "activities", 8)
        nightlife = query_poi(destination, "nightlife", 4)
        flight_opts = query_flights(origin, destination, start_date, end_date, 1)[:3]
        hotel_opts = query_hotels(destination, start_date, end_date, 1, None)[:3]
    
        pref_tags = [p.strip().lower() for p in preferences.split(",")] if preferences else []
    
        # Build day-by-day itinerary
        itinerary = []
        for i in range(days):
            date = start + timedelta(days=i)
            day_weather = next((w for w in weather if w["date"] == date.strftime("%Y-%m-%d")), None)
    
            day: dict = {
                "day": i + 1,
                "date": date.strftime("%Y-%m-%d"),
                "day_of_week": date.strftime("%A"),
            }
    
            if day_weather:
                day["weather"] = {
                    "condition": day_weather["condition"],
                    "high": f"{day_weather['high_celsius']}°C / {day_weather['high_fahrenheit']}°F",
                    "low": f"{day_weather['low_celsius']}°C / {day_weather['low_fahrenheit']}°F",
                    "rain_chance": f"{day_weather['rain_chance_pct']}%",
                }
    
            if i == 0:
                day["theme"] = "Arrival Day"
                day["morning"] = "Arrive and check into your hotel. Rest and freshen up after the journey."
                day["afternoon"] = attractions[:1] if attractions else []
                day["evening"] = restaurants[:1] if restaurants else []
            elif i == days - 1:
                day["theme"] = "Departure Day"
                day["morning"] = "Final breakfast and last-minute souvenir shopping. Check out of hotel."
                day["afternoon"] = []
                day["evening"] = "Head to the airport for your return flight."
            else:
                inner_day = i - 1
                day["theme"] = f"Explore {destination}"
                attr_slice = attractions[inner_day % max(len(attractions), 1): (inner_day % max(len(attractions), 1)) + 2]
                day["morning"] = attr_slice if attr_slice else (attractions[:2] if attractions else [])
                day["afternoon"] = activities[inner_day % max(len(activities), 1)] if activities else None
                rest_idx = (i * 2) % max(len(restaurants), 1)
                day["evening"] = {
                    "dinner": restaurants[rest_idx] if restaurants else None,
                    "nightlife": nightlife[inner_day % max(len(nightlife), 1)] if nightlife and i % 2 == 0 else None,
                }
    
            itinerary.append(day)
    
        result: dict = {
            "trip": {
                "destination": destination,
                "origin": origin,
                "start_date": start_date,
                "end_date": end_date,
                "duration_days": days,
                "preferences": pref_tags or "general",
            },
            "flight_options": flight_opts or [{"message": f"No direct routes found from {origin} to {destination} in mock data."}],
            "hotel_suggestions": hotel_opts or [{"message": f"No hotels found for {destination} in mock data."}],
            "itinerary": itinerary,
        }
    
        if total_budget is not None:
            trip_id = f"{destination.lower().replace(' ', '-')}-{start_date}"
            flight_price = flight_opts[0]["price_per_person"] if flight_opts and "price_per_person" in flight_opts[0] else 500
            nightly_rate = hotel_opts[0]["price_per_night"] if hotel_opts and "price_per_night" in hotel_opts[0] else 150
            allocation = _suggest_allocation(total_budget, days - 1, flight_price, nightly_rate)
            budget_module.create(trip_id, total_budget)
            result["budget"] = {
                "trip_id": trip_id,
                "total_budget": total_budget,
                "currency": "USD",
                "suggested_allocation": allocation,
                "note": f"Use trip_id '{trip_id}' with add_expense and get_budget_summary to track spending.",
            }
    
        return result
  • MCP tool registration: the `plan_trip` function decorated with @mcp.tool() defines the tool's schema (parameters: destination, start_date, end_date, origin, total_budget, preferences) and delegates to itinerary.plan().
    @mcp.tool()
    def plan_trip(
        destination: str,
        start_date: str,
        end_date: str,
        origin: str,
        total_budget: Optional[float] = None,
        preferences: Optional[str] = None,
    ) -> dict:
        """
        Generate a complete day-by-day travel itinerary combining weather, attractions, dining, and travel options.
    
        Args:
            destination: Travel destination city (e.g. "Tokyo", "Paris")
            start_date: Trip start date in YYYY-MM-DD format
            end_date: Trip end date in YYYY-MM-DD format
            origin: Departure city for flight search (e.g. "New York", "London")
            total_budget: Total trip budget in USD — enables budget planning and suggested allocation (optional)
            preferences: Comma-separated interests to tailor the itinerary (e.g. "food, history, beaches")
        """
        return itinerary.plan(destination, start_date, end_date, origin, total_budget, preferences)
  • HTTP API route registration: @mcp.custom_route('/api/tools/plan_trip') — the `api_plan_trip` handler that parses JSON request body and calls itinerary.plan(), returning a JSONResponse.
    @mcp.custom_route("/api/tools/plan_trip", methods=["POST"])
    async def api_plan_trip(request: Request) -> JSONResponse:
        body = await request.json()
        result = itinerary.plan(
            body["destination"], body["start_date"], body["end_date"], body["origin"],
            body.get("total_budget"), body.get("preferences"),
        )
        return JSONResponse(result)
  • Helper function `_suggest_allocation()` used by `plan()` when total_budget is provided. It calculates suggested budget splits across categories (flights, hotels, food, activities, transport, shopping, misc).
    def _suggest_allocation(total: float, nights: int, flight_price: float, nightly_rate: float) -> dict:
        flight_cost = min(flight_price * 2, total * 0.35)  # round-trip estimate, capped at 35%
        hotel_cost = min(nightly_rate * nights, total * 0.35)
        remaining = max(total - flight_cost - hotel_cost, total * 0.30)
        return {
            "flights": round(flight_cost, 2),
            "hotels": round(hotel_cost, 2),
            "food": round(remaining * 0.38, 2),
            "activities": round(remaining * 0.28, 2),
            "transport": round(remaining * 0.14, 2),
            "shopping": round(remaining * 0.12, 2),
            "misc": round(remaining * 0.08, 2),
        }
  • Input schema for plan_trip: defines parameters via type hints — destination (str), start_date (str), end_date (str), origin (str), total_budget (Optional[float]), preferences (Optional[str]). Also returns dict.
    def plan_trip(
        destination: str,
        start_date: str,
        end_date: str,
        origin: str,
        total_budget: Optional[float] = None,
        preferences: Optional[str] = None,
    ) -> dict:
Behavior3/5

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

No annotations are provided, so the description must cover behavior. It describes the tool's function but does not disclose side effects, dependencies, or whether it is read-only. Lacks details on performance or error handling.

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 concise and front-loaded with the main purpose, followed by a clear Args list. Each sentence adds value, though the Args section is moderately detailed.

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 the tool's complexity and lack of output schema, the description covers purpose and parameters well. However, it does not describe the output format or any limitations, leaving some gaps about what the agent can expect.

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 has 0% description coverage, but the description's Args section explains each parameter with format and examples. Adds significant meaning beyond the schema, especially for optional parameters like total_budget and preferences.

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 generates a complete day-by-day travel itinerary combining weather, attractions, dining, and travel options. It uses specific verbs ('generate') and resource ('itinerary'), distinguishing it from sibling tools like get_weather or search_flights.

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

Usage Guidelines3/5

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

The description implies usage for comprehensive trip planning but does not explicitly state when to use this tool versus individual search tools (e.g., search_flights). No exclusions or alternatives are mentioned.

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

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ismailrz/travel-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server