Skip to main content
Glama

manage_booking

View, modify, or cancel existing Turo car rental bookings by providing the booking ID and desired action.

Instructions

View, modify, or cancel an existing Turo booking. Use action='view' to see booking details, 'cancel' to cancel, or 'modify' to change dates.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
booking_idYesThe Turo booking/reservation ID
actionYesThe action to perform: 'view', 'cancel', or 'modify'
new_start_dateNoNew rental start date in YYYY-MM-DD format (required for 'modify' action)
new_end_dateNoNew rental end date in YYYY-MM-DD format (required for 'modify' action)
messageNoOptional message to include with a modification or cancellation request

Implementation Reference

  • The main handler function for 'manage_booking' which uses browser automation to perform view, cancel, or modify actions on Turo bookings.
    export async function manageBooking(params: ManageBookingParams): Promise<BookingDetails | { success: boolean; message: string }> {
      const page = await newPage();
    
      try {
        const reservationUrl = `https://turo.com/us/en/reservation/${params.booking_id}`;
        await page.goto(reservationUrl, { waitUntil: "domcontentloaded", timeout: 30000 });
        await waitForNavigation(page);
        await sleep(2000);
    
        if (params.action === "view") {
          const details = await page.evaluate((bookingId: string) => {
            const getText = (selector: string): string => {
              const el = document.querySelector(selector) as HTMLElement | null;
              return el?.textContent?.trim() || "";
            };
    
            const parsePrice = (text: string): number => {
              const match = text.match(/\$?([\d,]+\.?\d*)/);
              return match ? parseFloat(match[1].replace(",", "")) : 0;
            };
    
            // Status
            const statusEl = document.querySelector('[class*="status"]') as HTMLElement | null;
            const status = statusEl?.textContent?.trim() || "unknown";
    
            // Car details
            const titleEl = document.querySelector('h1, h2, [class*="vehicleName"]') as HTMLElement | null;
            const titleText = titleEl?.textContent?.trim() || "";
            const yearMatch = titleText.match(/(\d{4})/);
            const year = yearMatch ? parseInt(yearMatch[1]) : 0;
            const nameParts = titleText.replace(/\d{4}\s*/, "").trim().split(" ");
    
            const carLinkEl = document.querySelector('a[href*="/vehicles/"]') as HTMLAnchorElement | null;
            const carHref = carLinkEl?.href || "";
            const carIdMatch = carHref.match(/\/vehicles\/(\d+)/);
            const carId = carIdMatch ? carIdMatch[1] : "";
    
            const priceEl = document.querySelector('[class*="dailyRate"], [class*="daily-rate"]') as HTMLElement | null;
            const daily_rate = parsePrice(priceEl?.textContent || "");
    
            // Dates
            const dateEls = document.querySelectorAll('[class*="date"], time');
            let start_date = "";
            let end_date = "";
            if (dateEls.length >= 2) {
              start_date = (dateEls[0] as HTMLElement).getAttribute("datetime") || (dateEls[0] as HTMLElement).textContent?.trim() || "";
              end_date = (dateEls[1] as HTMLElement).getAttribute("datetime") || (dateEls[1] as HTMLElement).textContent?.trim() || "";
            }
    
            // Total price
            const totalEl = document.querySelector('[class*="total"]') as HTMLElement | null;
            const total_price = parsePrice(totalEl?.textContent || "");
    
            // Price breakdown
            const breakdownItems: Record<string, number> = {};
            document.querySelectorAll('[class*="breakdown"] li, [class*="price"] li').forEach((item) => {
              const labelEl = item.querySelector('span:first-child') as HTMLElement | null;
              const valueEl = item.querySelector('span:last-child') as HTMLElement | null;
              if (labelEl && valueEl) {
                const label = labelEl.textContent?.trim().toLowerCase() || "";
                breakdownItems[label] = parsePrice(valueEl.textContent || "");
              }
            });
    
            const breakdown: {
              daily_rate: number;
              days: number;
              subtotal: number;
              turo_fee: number;
              insurance_fee?: number;
              taxes: number;
              total: number;
            } = {
              daily_rate,
              days: breakdownItems["days"] || 1,
              subtotal: breakdownItems["subtotal"] || total_price,
              turo_fee: breakdownItems["turo fee"] || breakdownItems["service fee"] || 0,
              insurance_fee: breakdownItems["insurance"] || breakdownItems["protection"] || undefined,
              taxes: breakdownItems["taxes"] || breakdownItems["tax"] || 0,
              total: total_price,
            };
    
            // Host info
            const hostEl = document.querySelector('[class*="hostName"], [class*="host-name"]') as HTMLElement | null;
            const host_name = hostEl?.textContent?.trim() || "Host";
    
            const hostPhoneEl = document.querySelector('[class*="phone"], [href^="tel:"]') as HTMLElement | null;
            const host_phone = hostPhoneEl?.textContent?.trim() || hostPhoneEl?.getAttribute("href")?.replace("tel:", "") || undefined;
    
            // Pickup instructions
            const pickupEl = document.querySelector('[class*="pickup"], [class*="instruction"]') as HTMLElement | null;
            const pickup_instructions = pickupEl?.textContent?.trim() || undefined;
    
            // Created/modified dates
            const createdEl = document.querySelector('[class*="created"], [class*="booked"]') as HTMLElement | null;
            const created_at = createdEl?.getAttribute("datetime") || createdEl?.textContent?.trim() || new Date().toISOString();
    
            const modifiedEl = document.querySelector('[class*="modified"], [class*="updated"]') as HTMLElement | null;
            const modified_at = modifiedEl?.getAttribute("datetime") || modifiedEl?.textContent?.trim() || undefined;
    
            const listing: {
              id: string;
              make: string;
              model: string;
              year: number;
              daily_rate: number;
              rating: number;
              trip_count: number;
              location: string;
              listing_url: string;
              host_name: string;
              features: string[];
              vehicle_type: string;
            } = {
              id: carId,
              make: nameParts[0] || "Unknown",
              model: nameParts.slice(1).join(" ") || "Unknown",
              year,
              daily_rate,
              rating: 0,
              trip_count: 0,
              location: "",
              listing_url: carHref,
              host_name,
              features: [],
              vehicle_type: "Car",
            };
    
            return {
              booking_id: bookingId,
              status,
              listing,
              start_date,
              end_date,
              total_price,
              breakdown,
              host_name,
              host_phone,
              pickup_instructions,
              created_at,
              modified_at,
            };
          }, params.booking_id);
    
          return details as BookingDetails;
        }
    
        if (params.action === "cancel") {
          // Find cancel button
          const cancelButton = await page.$(
            '[data-testid="cancel-button"], button[class*="cancel"], a[class*="cancel"]'
          );
    
          if (!cancelButton) {
            return { success: false, message: "Cancel option not found. Booking may not be cancellable at this time." };
          }
    
          await cancelButton.click();
          await waitForNavigation(page);
          await sleep(2000);
    
          // Confirm cancellation if a confirmation dialog appears
          const confirmButton = await page.$(
            '[data-testid="confirm-cancel"], button[class*="confirm"]'
          );
          if (confirmButton) {
            await confirmButton.click();
            await waitForNavigation(page);
            await sleep(2000);
          }
    
          const result = await page.evaluate(() => {
            const successEl = document.querySelector('[class*="success"], [class*="cancelled"]') as HTMLElement | null;
            return { success: !!successEl, message: successEl?.textContent?.trim() || "Cancellation processed" };
          });
    
          return result;
        }
    
        if (params.action === "modify") {
          if (!params.new_start_date || !params.new_end_date) {
            return { success: false, message: "new_start_date and new_end_date are required for modify action" };
          }
    
          // Find modify/change dates button
          const modifyButton = await page.$(
            '[data-testid="modify-button"], button[class*="modify"], button[class*="change"], a[class*="modify"]'
          );
    
          if (!modifyButton) {
            return { success: false, message: "Modify option not found. Booking may not be modifiable at this time." };
          }
    
          await modifyButton.click();
          await waitForNavigation(page);
          await sleep(2000);
    
          // Try to update dates in the modification form
          const startInput = await page.$(
            'input[name*="start"], input[placeholder*="start"], input[type="date"]'
          );
          if (startInput) {
            await startInput.fill(params.new_start_date);
          }
    
          const endInput = await page.$(
            'input[name*="end"], input[placeholder*="end"], input[type="date"]:last-of-type'
          );
          if (endInput) {
            await endInput.fill(params.new_end_date);
          }
    
          if (params.message) {
            const messageBox = await page.$('textarea[class*="message"], textarea[placeholder*="message"]');
            if (messageBox) {
              await messageBox.fill(params.message);
            }
          }
    
          // Submit modification
          const submitButton = await page.$(
            'button[type="submit"], [data-testid="submit-modify"], button[class*="submit"]'
          );
          if (submitButton) {
            await submitButton.click();
            await waitForNavigation(page);
            await sleep(2000);
          }
    
          const result = await page.evaluate(() => {
            const successEl = document.querySelector('[class*="success"], [class*="modified"], [class*="confirmed"]') as HTMLElement | null;
            return {
              success: !!successEl,
              message: successEl?.textContent?.trim() || "Modification request submitted",
            };
          });
    
          return result;
        }
    
        return { success: false, message: `Unknown action: ${params.action}` };
      } finally {
        await page.close();
      }
    }
Behavior3/5

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

With no annotations provided, the description carries full responsibility for behavioral disclosure. While it explains the three operational modes, it fails to mention critical safety aspects like whether cancellation is reversible, if modifications incur fees, or required permissions/authorization levels for these mutations.

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?

The description consists of exactly two sentences with zero redundancy. The first establishes scope, the second provides actionable parameter guidance—efficiently front-loaded with no wasted words.

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 100% schema coverage and clear action mapping, the description adequately covers the tool's functionality for a multi-mode operation. However, the absence of output schema and annotations leaves gaps regarding return values and side effects that the description could have partially addressed.

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 100%, establishing a baseline of 3. The description adds value by explaining the semantic meaning of each action ('see booking details', 'change dates') beyond the schema's literal enumeration, though it omits mention of the optional 'message' parameter.

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 performs three specific operations (view, modify, cancel) on an existing Turo booking. The phrase 'existing Turo booking' effectively distinguishes it from sibling tool create_booking.

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 maps each action value to its specific outcome ('view' to see details, 'cancel' to cancel, 'modify' to change dates), providing clear usage patterns. However, it lacks explicit guidance on when to choose this over create_booking or whether modifications have prerequisites.

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/markswendsen-code/mcp-turo'

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