An MCP server that scrapes real estate listings from websites using Playwright and processes extracted HTML through an LLM to return structured data.
The MCP WebScraper has a single tool, scrape_listings, with a clear purpose but significant gaps in definition quality. The tool name follows verb_noun convention and has a reasonable description (186 chars), but the description lacks critical guidance about prerequisites, failure modes, and dependencies on external services. The input schema is present with proper type definitions (string types) and parameter descriptions, but lacks constraints (no enums, ranges, or format specifications). The output schema (ListingResponse) is documented via Pydantic models but not directly described in the tool definition. Error handling returns a fallback empty ListingResponse on failure, providing no guidance to the LLM about retry strategies or what went wrong. The tool is read-only (appropriately marked as READ_ONLY) but lacks security-focused descriptions around secret handling (OpenAI API key loaded from environment). The implementation shows external dependencies (Playwright, OpenAI, BeautifulSoup) that create fragility, timeouts, rate limits, and API failures are possible but undocumented in the tool interface.
Scrapes real estate listings based on a city query and user instructions. Args: query: City or location to search listings for (e.g., "Bogota"). instructions: LLM instructions for how to extract data from the HTML. Returns: A structured ListingResponse with the extracted results.
Output schema (ListingResponse) is defined in code but not documented in the tool description. LLMs cannot infer the structure of returned ListingItem fields (title, location, price, bedrooms, etc.) from the tool definition alone.
Tool description lacks critical context about prerequisites, failure modes, and error recovery. It does not state that it requires a valid OpenAI API key (OPENAI_API_KEY env var), may timeout, or how the LLM should handle failures. No guidance on retryability.
Parameter 'instructions' is a free-form string with no constraints or format guidance. The tool sends this directly to an LLM system prompt, a prompt injection vector. No validation, escaping, or safeguards documented.
No pagination or result limiting mechanism. The tool can return an unbounded list of ListingItem objects, risking context window exhaustion. No limit parameter or documentation of maximum results.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 56 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 37 | - | v1 |
Error handling returns a bare ListingResponse(listings=[]) on all exceptions. The LLM receives no information about what failed (network error, timeout, LLM parsing failure, invalid query). No recovery guidance.
Tool hardcodes a specific URL pattern (fincaraiz.com.co for Colombian real estate). The 'query' parameter is described as 'city or location' but the implementation treats it as a URL slug segment. No documentation of accepted query formats or failure cases for invalid cities.
External API dependencies (OpenAI GPT-4o-mini, Playwright, fincaraiz.com.co website) are not documented in the tool description. The LLM has no visibility into rate limits, availability, or cost implications.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint) present despite being marked READ_ONLY in the metadata. The tool is idempotent (no state changes) but this is not declared via the MCP spec annotations mechanism.