Skip to main content
Glama

get_host_profile

Retrieve detailed Turo host profiles including ratings, reviews, response rates, and vehicle listings to evaluate rental reliability.

Instructions

View a Turo host's profile including their ratings, reviews, response rate, and all their vehicle listings.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
host_idYesThe Turo host/driver ID. Found in the host profile URL: turo.com/us/en/drivers/{host_id}

Implementation Reference

  • The handler function `getHostProfile` which navigates to a Turo driver profile page, scrapes profile information (name, rating, listings, reviews, etc.) using `page.evaluate`, and returns the profile object.
    export async function getHostProfile(hostId: string): Promise<HostProfile> {
      const page = await newPage();
    
      try {
        const url = `https://turo.com/us/en/drivers/${hostId}`;
        await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 });
        await waitForNavigation(page);
        await sleep(2000);
    
        const profile = await page.evaluate((id: string) => {
          const getText = (selector: string): string => {
            const el = document.querySelector(selector) as HTMLElement | null;
            return el?.textContent?.trim() || "";
          };
    
          // Name
          const nameEl = document.querySelector(
            'h1, [class*="driverName"], [class*="profileName"]'
          ) as HTMLElement | null;
          const name = nameEl?.textContent?.trim() || "Host";
    
          // Joined date
          const joinedEl = document.querySelector(
            '[class*="joined"], [class*="member"]'
          ) as HTMLElement | null;
          const joinedText = joinedEl?.textContent?.trim() || "";
          const joined_date = joinedText || "Unknown";
    
          // Rating
          const ratingEl = document.querySelector(
            '[class*="rating"], [aria-label*="rating"]'
          ) as HTMLElement | null;
          const ratingText = ratingEl?.getAttribute("aria-label") || ratingEl?.textContent || "0";
          const ratingMatch = ratingText.match(/([\d.]+)/);
          const rating = ratingMatch ? parseFloat(ratingMatch[1]) : 0;
    
          // Trip count
          const tripEl = document.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;
    
          // Response rate & time
          const responseEls = document.querySelectorAll('[class*="response"]');
          let response_rate = "N/A";
          let response_time = "N/A";
          responseEls.forEach((el) => {
            const text = (el as HTMLElement).textContent?.trim() || "";
            if (text.includes("%")) response_rate = text;
            else if (text.includes("hour") || text.includes("minute")) response_time = text;
          });
    
          // About
          const aboutEl = document.querySelector(
            '[class*="about"], [class*="bio"]'
          ) as HTMLElement | null;
          const about = aboutEl?.textContent?.trim() || undefined;
    
          // All-star host / verified
          const allStarEl = document.querySelector('[class*="allStar"], [class*="all-star"]');
          const all_star_host = !!allStarEl;
    
          const verifiedEl = document.querySelector('[class*="verified"], [aria-label*="verified"]');
          const verified = !!verifiedEl;
    
          // Listings
          const listings: {
            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;
          }[] = [];
    
          document
            .querySelectorAll('[class*="vehicleCard"], [class*="vehicle-card"], [class*="listing"]')
            .forEach((card, idx) => {
              if (idx >= 20) return;
              try {
                const link = card.querySelector("a") as HTMLAnchorElement | null;
                const href = link?.href || "";
                const idMatch = href.match(/\/vehicles\/(\d+)/);
                const listingId = idMatch ? idMatch[1] : `listing-${idx}`;
    
                const titleEl2 = card.querySelector(
                  '[class*="title"], [class*="name"], h2, h3'
                ) as HTMLElement | null;
                const titleText = titleEl2?.textContent?.trim() || "";
                const yearMatch = titleText.match(/(\d{4})/);
                const yr = yearMatch ? parseInt(yearMatch[1]) : 0;
                const parts = titleText.replace(/\d{4}\s*/, "").trim().split(" ");
    
                const priceEl = card.querySelector('[class*="price"]') as HTMLElement | null;
                const priceText = priceEl?.textContent || "";
                const priceMatch = priceText.match(/\$?([\d,]+)/);
                const dr = priceMatch ? parseFloat(priceMatch[1].replace(",", "")) : 0;
    
                const rEl = card.querySelector('[class*="rating"]') as HTMLElement | null;
                const rText = rEl?.textContent || "0";
                const rMatch = rText.match(/([\d.]+)/);
    
                listings.push({
                  id: listingId,
                  make: parts[0] || "Unknown",
                  model: parts.slice(1).join(" ") || "Unknown",
                  year: yr,
                  daily_rate: dr,
                  rating: rMatch ? parseFloat(rMatch[1]) : 0,
                  trip_count: 0,
                  location: "",
                  listing_url: href,
                  host_name: name,
                  features: [],
                  vehicle_type: "Car",
                });
              } catch {
                // skip
              }
            });
    
          // Reviews
          const reviews: Review[] = [];
          document
            .querySelectorAll('[class*="review"], [class*="Review"]')
            .forEach((reviewEl, idx) => {
              if (idx >= 10) return;
              const authorEl = reviewEl.querySelector(
                '[class*="author"], [class*="name"]'
              ) as HTMLElement | null;
              const ratingEl2 = reviewEl.querySelector('[class*="rating"]') as HTMLElement | null;
              const dateEl = reviewEl.querySelector(
                '[class*="date"], time'
              ) as HTMLElement | null;
              const commentEl = reviewEl.querySelector(
                '[class*="comment"], [class*="text"], p'
              ) as HTMLElement | null;
    
              const rText = ratingEl2?.getAttribute("aria-label") || ratingEl2?.textContent || "0";
              const rMatch = rText.match(/([\d.]+)/);
    
              reviews.push({
                id: `host-review-${idx}`,
                author: authorEl?.textContent?.trim() || "Guest",
                rating: rMatch ? parseFloat(rMatch[1]) : 0,
                date:
                  dateEl?.getAttribute("datetime") || dateEl?.textContent?.trim() || "",
                comment: commentEl?.textContent?.trim() || "",
              });
            });
    
          return {
            id,
            name,
            joined_date,
            rating,
            trip_count,
            response_rate,
            response_time,
            about,
            all_star_host,
            verified,
            listings,
            reviews,
          };
        }, hostId);
    
        return profile as HostProfile;
      } finally {
        await page.close();
      }
    }
Behavior3/5

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

No annotations provided, so description carries full burden. It discloses the data scope returned (ratings, reviews, response rate, listings), which compensates partially for missing output schema. However, omits operational details like error handling (invalid host_id), pagination behavior for listings, or idempotency characteristics that annotations would typically cover.

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 with information-dense structure. Front-loaded with action verb ('View'), follows with resource identification, and concludes with specific inclusion clause. No redundant or wasted words.

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?

Excellent completeness for a single-parameter tool. Compensates effectively for missing output schema by enumerating return data fields (ratings, reviews, response rate, listings). Combined with 100% parameter schema coverage, provides sufficient context for agent selection and invocation.

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%, providing detailed guidance on host_id format and location in URLs. Description mentions 'host's profile' reinforcing the parameter purpose but does not add syntax or semantic details beyond the schema. Baseline score appropriate for high schema coverage.

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?

Description uses specific verb 'View' with clear resource 'Turo host's profile' and enumerates specific data returned (ratings, reviews, response rate, vehicle listings). Distinct from siblings which focus on bookings (create_booking, manage_booking), car details (get_car_details), or search (search_cars).

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 about the tool's function (retrieving host profile data including specific fields), allowing agents to infer when to use it (when host credibility or listing aggregation is needed). However, lacks explicit 'when not to use' guidance or direct comparison to siblings like search_cars.

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