Skip to main content
Glama

search_cars

Find available rental cars on Turo by specifying location, dates, and optional filters like price range, vehicle type, or seating capacity to match your travel needs.

Instructions

Search for available rental cars on Turo by location, dates, and optional filters such as price range and vehicle type.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
locationYesThe pickup location (city, address, or airport code). Example: 'San Francisco, CA' or 'SFO'
start_dateYesRental start date in YYYY-MM-DD format. Example: '2025-06-15'
end_dateYesRental end date in YYYY-MM-DD format. Example: '2025-06-20'
min_priceNoMinimum daily price filter in USD (optional)
max_priceNoMaximum daily price filter in USD (optional)
vehicle_typeNoFilter by vehicle type: 'car', 'suv', 'truck', 'van', 'minivan', 'convertible', 'luxury', 'electric' (optional)
min_seatsNoMinimum number of seats required (optional)

Implementation Reference

  • The `searchCars` function handles the search logic by navigating to Turo, applying filters, and scraping the result cards from the page.
    export async function searchCars(params: SearchCarsParams): Promise<CarListing[]> {
      const page = await newPage();
    
      try {
        // Build Turo search URL
        const baseUrl = "https://turo.com/us/en/search";
        const searchParams = new URLSearchParams();
        searchParams.set("location", params.location);
        searchParams.set("startDate", params.start_date);
        searchParams.set("endDate", params.end_date);
        searchParams.set("startTime", "10:00");
        searchParams.set("endTime", "10:00");
    
        if (params.vehicle_type) {
          searchParams.set("vehicleType", params.vehicle_type);
        }
    
        const url = `${baseUrl}?${searchParams.toString()}`;
        await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 });
        await waitForNavigation(page);
    
        // Wait for search results to load
        await page.waitForSelector('[data-testid="vehicle-card"], .vehicle-card, [class*="vehicleCard"]', {
          timeout: 15000,
        }).catch(() => null);
    
        await sleep(2000);
    
        // Extract car listings from search results
        const listings = await page.evaluate(
          ({ minPrice, maxPrice }: { minPrice?: number; maxPrice?: number }) => {
            const cards: NodeListOf<Element> = document.querySelectorAll(
              '[data-testid="vehicle-card"], [class*="vehicleCard"], [class*="vehicle-card"]'
            );
    
            const results: {
              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;
            }[] = [];
    
            cards.forEach((card) => {
              try {
                const link = card.querySelector("a") as HTMLAnchorElement | null;
                const href = link?.href || "";
                const idMatch = href.match(/\/vehicles\/(\d+)/);
                const id = idMatch ? idMatch[1] : Math.random().toString(36).slice(2);
    
                const titleEl = card.querySelector(
                  '[class*="title"], [class*="name"], h2, h3'
                ) 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 make = nameParts[0] || "Unknown";
                const model = nameParts.slice(1).join(" ") || "Unknown";
    
                const priceEl = card.querySelector(
                  '[class*="price"], [class*="rate"]'
                ) as HTMLElement | null;
                const priceText = priceEl?.textContent || "";
                const priceMatch = priceText.match(/\$?([\d,]+)/);
                const daily_rate = priceMatch ? parseFloat(priceMatch[1].replace(",", "")) : 0;
    
                if (minPrice !== undefined && daily_rate < minPrice) return;
                if (maxPrice !== undefined && daily_rate > maxPrice) return;
    
                const ratingEl = card.querySelector(
                  '[class*="rating"], [aria-label*="rating"]'
                ) as HTMLElement | null;
                const ratingText = ratingEl?.textContent || ratingEl?.getAttribute("aria-label") || "0";
                const ratingMatch = ratingText.match(/([\d.]+)/);
                const rating = ratingMatch ? parseFloat(ratingMatch[1]) : 0;
    
                const tripEl = card.querySelector(
                  '[class*="trip"], [class*="count"]'
                ) as HTMLElement | null;
                const tripText = tripEl?.textContent || "";
                const tripMatch = tripText.match(/([\d,]+)/);
                const trip_count = tripMatch ? parseInt(tripMatch[1].replace(",", "")) : 0;
    
                const locationEl = card.querySelector(
                  '[class*="location"], [class*="distance"]'
                ) as HTMLElement | null;
                const location = locationEl?.textContent?.trim() || "";
    
                const hostEl = card.querySelector(
                  '[class*="host"], [class*="owner"]'
                ) as HTMLElement | null;
                const host_name = hostEl?.textContent?.trim() || "Host";
    
                const featureEls = card.querySelectorAll('[class*="feature"], [class*="badge"]');
                const features: string[] = [];
                featureEls.forEach((el) => {
                  const text = (el as HTMLElement).textContent?.trim();
                  if (text) features.push(text);
                });
    
                const typeEl = card.querySelector('[class*="type"], [class*="category"]') as HTMLElement | null;
                const vehicle_type = typeEl?.textContent?.trim() || "Car";
    
                results.push({
                  id,
                  make,
                  model,
                  year,
                  daily_rate,
                  rating,
                  trip_count,
                  location,
                  listing_url: href,
                  host_name,
                  features,
                  vehicle_type,
                });
              } catch {
                // Skip malformed cards
              }
            });
    
            return results;
          },
          { minPrice: params.min_price, maxPrice: params.max_price }
        );
    
        return listings as CarListing[];
      } finally {
        await page.close();
      }
    }
Behavior3/5

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

No annotations are provided, so the description carries the full burden. It discloses 'available' (implying real-time availability checking) and 'optional' filters, but lacks details on pagination, rate limits, authentication requirements, or error conditions (e.g., no results found).

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?

Single, well-structured sentence that is front-loaded with the action ('Search'). Every word earns its place—no redundancy or tautology. Efficiently communicates the core function and key parameters without waste.

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 rich input schema (100% coverage, 7 parameters) and lack of output schema, the description adequately covers the input domain by mentioning required fields (location, dates) and examples of optional filters. It could be improved by briefly indicating the return value (e.g., 'returns list of available vehicles').

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

Parameters3/5

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

Schema description coverage is 100%, establishing a baseline of 3. The description adds value by grouping parameters logically (location, dates, optional filters) and specifying the Turo platform context not present in the schema, but does not elaborate on parameter formats beyond what's in 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 uses specific verb 'Search' with clear resource 'available rental cars on Turo', and explicitly mentions the key parameters (location, dates, filters). It clearly distinguishes this as a discovery/search operation from sibling tools like create_booking or get_car_details.

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?

Provides clear context that this tool is for searching with filters by location and dates, establishing it as the entry point for finding vehicles. However, it lacks explicit guidance on when to transition to siblings like get_car_details or create_booking, or when search might fail.

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