Skip to main content
Glama
JavidGlyv

Turbo.az MCP Server

by JavidGlyv

search_cars

Search for vehicles on Turbo.az using filters like make, model, price range, year, fuel type, and transmission to find matching car listings.

Instructions

Car search on Turbo.az. Search by make, model, price range, year, etc.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
makeNoCar make (e.g. BMW, Mercedes, Toyota)
modelNoCar model (e.g. X5, E-Class, Camry)
price_minNoMinimum price (AZN)
price_maxNoMaximum price (AZN)
year_minNoMinimum year of manufacture
year_maxNoMaximum year of manufacture
fuel_typeNoFuel type: petrol, diesel, gas, electric, hybrid
transmissionNoTransmission: automatic, manual
limitNoResult count limit (default: 20)

Implementation Reference

  • The handler function search_cars which performs the web scraping of car listings.
    async def search_cars(
        self,
        make: Optional[str] = None,
        model: Optional[str] = None,
        price_min: Optional[int] = None,
        price_max: Optional[int] = None,
        year_min: Optional[int] = None,
        year_max: Optional[int] = None,
        fuel_type: Optional[str] = None,
        transmission: Optional[str] = None,
        limit: int = 20
    ) -> dict:
        """Searches for cars (gets ID from make/model name and builds full URL)."""
        fuel_mapping = {
            "benzin": 1, "dizel": 2, "qaz": 3, "elektrik": 6, "hibrid": 7, "plug-in hibrid": 8,
        }
        transmission_mapping = {
            "mexaniki": 1, "avtomat": 2, "robot": 3, "variator": 4,
        }
        fuel_id = fuel_mapping.get((fuel_type or "").lower()) if fuel_type else None
        transmission_id = transmission_mapping.get((transmission or "").lower()) if transmission else None
    
        def _scrape():
            driver = self._get_driver()
            results = []
            make_id = None
            model_id = None
            try:
                if make:
                    driver.get(f"{BASE_URL}/autos")
                    WebDriverWait(driver, 20).until(
                        EC.presence_of_element_located((By.CSS_SELECTOR, '.tz-dropdown[data-id="q_make"]'))
                    )
                    time.sleep(0.3)
                    try:
                        driver.find_element(By.CSS_SELECTOR, '.tz-dropdown[data-id="q_make"] .tz-dropdown__selected').click()
                        time.sleep(0.2)
                    except Exception:
                        pass
                    make_opts = self._parse_tz_dropdown_options(driver, "q_make")
                    make_lower = make.strip().lower()
                    for val, label in make_opts:
                        txt = label.lower()
                        if txt == make_lower or txt.startswith(make_lower + " ") or txt.startswith(make_lower + "(") or make_lower in txt:
                            make_id = val
                            break
                    if not make_id:
                        all_makes = [label for _, label in make_opts[:20]]
                        logger.warning("Make not found. Sample options: %s", all_makes)
                        return {"success": False, "error": f"Make not found: {make}"}
                    if model:
                        # Open make dropdown and click make option so model list is populated
                        try:
                            make_cont = driver.find_element(By.CSS_SELECTOR, '.tz-dropdown[data-id="q_make"]')
                            make_cont.find_element(By.CSS_SELECTOR, ".tz-dropdown__selected").click()
                            time.sleep(0.3)
                            opts_el = make_cont.find_elements(By.CSS_SELECTOR, ".tz-dropdown__list .tz-dropdown__option")
                            for el in opts_el:
                                if (el.get_attribute("data-val") or "").strip() == make_id:
                                    el.click()
                                    break
                            time.sleep(0.5)
                        except Exception:
                            pass
                        # Open model dropdown so options are in DOM
                        try:
                            driver.find_element(By.CSS_SELECTOR, '.tz-dropdown[data-id="q_model"] .tz-dropdown__selected').click()
                            time.sleep(0.2)
                        except Exception:
                            pass
                        model_opts = self._parse_tz_dropdown_options(driver, "q_model")
                        model_lower = model.strip().lower()
                        for val, label in model_opts:
                            txt = label.lower()
                            if txt == model_lower or txt.startswith(model_lower + " ") or txt.startswith(model_lower + "("):
                                model_id = val
                                break
                url = self._build_search_url(
                    make_id=make_id,
                    model_id=model_id,
                    price_min=price_min,
                    price_max=price_max,
                    year_min=year_min,
                    year_max=year_max,
                    fuel_id=fuel_id,
                    transmission_id=transmission_id,
                )
                logger.info(f"Searching: {url}")
                driver.get(url)
                WebDriverWait(driver, 20).until(
                    EC.presence_of_element_located((By.CLASS_NAME, "products-i"))
                )
    
                # Find listings
                items = driver.find_elements(By.CLASS_NAME, "products-i")[:limit]
                
                for item in items:
                    try:
                        car = {}
    
                        # Title and link
                        link_elem = item.find_element(By.CLASS_NAME, "products-i__link")
                        car["url"] = link_elem.get_attribute("href")
                        car["id"] = car["url"].split("/")[-1].split("-")[0]
    
                        # Image
                        try:
                            img = item.find_element(By.CSS_SELECTOR, ".products-i__top img")
                            car["image"] = img.get_attribute("src")
                        except NoSuchElementException:
                            car["image"] = None
    
                        # Title (make + model)
                        try:
                            title = item.find_element(By.CLASS_NAME, "products-i__name")
                            car["title"] = title.text.strip()
                        except NoSuchElementException:
                            car["title"] = "N/A"
    
                        # Price
                        try:
                            price = item.find_element(By.CLASS_NAME, "products-i__price")
                            car["price"] = price.text.strip()
                        except NoSuchElementException:
                            car["price"] = "N/A"
    
                        # Additional info (year, engine, mileage)
                        try:
                            attrs = item.find_elements(By.CLASS_NAME, "products-i__attributes")
                            if attrs:
                                attr_text = attrs[0].text.strip()
                                parts = [p.strip() for p in attr_text.split(",")]
                                if len(parts) >= 1:
                                    car["year"] = parts[0]
                                if len(parts) >= 2:
                                    car["engine"] = parts[1]
                                if len(parts) >= 3:
                                    car["mileage"] = parts[2]
                        except NoSuchElementException:
                            pass
  • src/server.py:87-115 (registration)
    Registration of the search_cars tool in the MCP server using the list_tools decorator.
    Tool(
        name="search_cars",
        description="Car search on Turbo.az. Search by make, model, price range, year, etc.",
        inputSchema={
            "type": "object",
            "properties": {
                "make": {
                    "type": "string",
                    "description": "Car make (e.g. BMW, Mercedes, Toyota)"
                },
                "model": {
                    "type": "string",
                    "description": "Car model (e.g. X5, E-Class, Camry)"
                },
                "price_min": {
                    "type": "integer",
                    "description": "Minimum price (AZN)"
                },
                "price_max": {
                    "type": "integer",
                    "description": "Maximum price (AZN)"
                },
                "year_min": {
                    "type": "integer",
                    "description": "Minimum year of manufacture"
                },
                "year_max": {
                    "type": "integer",
                    "description": "Maximum year of manufacture"
Behavior2/5

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

With no annotations provided, the description carries full burden but lacks behavioral details. It doesn't disclose whether this is a read-only operation, if it requires authentication, rate limits, pagination behavior (beyond the 'limit' param), or what the output format looks like (e.g., list of cars with basic info). For a search tool with 9 parameters, this is insufficient.

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 a single, efficient sentence that front-loads the core purpose. It avoids redundancy and wastes no words, though it could be slightly more structured by explicitly mentioning the platform ('Turbo.az') earlier.

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

Completeness2/5

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

Given the complexity (9 parameters, no annotations, no output schema), the description is incomplete. It doesn't explain the return values, error conditions, or behavioral traits needed for effective use. For a search tool, this leaves significant gaps in understanding how results are formatted or limited.

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%, so the schema fully documents all 9 parameters with descriptions and defaults. The description adds minimal value by listing example criteria ('make, model, price range, year, etc.') but doesn't provide additional semantics beyond what's in the schema, such as how parameters interact or search logic.

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

Purpose4/5

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

The description clearly states the action ('search') and resource ('cars on Turbo.az'), specifying it's a search operation with example criteria. It distinguishes from sibling tools like 'get_car_details' (detail retrieval) and 'get_makes_models' (metadata), though not explicitly. However, it doesn't fully differentiate from potential 'get_trending' if that also returns cars.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives like 'get_car_details' or 'get_makes_models'. It mentions search criteria but doesn't specify prerequisites, exclusions, or contextual triggers for choosing this over siblings, leaving the agent to infer usage.

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/JavidGlyv/Turboaz-MCP'

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