Scrapes the public Facebook Ad Library (no API, no token) for competitive ad research. Works for commercial ads in any country. Returns structured ad cards: advertiser, run date, creative count, landing domain, CTA, headline and body copy.
The server defines 2 tools with mostly complete schemas and reasonable descriptions. However, descriptions lack depth and guidance for LLM selection. Tool names are action-oriented (search_, scrape_), and both have documented input schemas with parameter types. Output schemas are inferred from code but not explicitly declared. Error handling is minimal, the code catches exceptions but provides no recovery guidance to the LLM. Security is reasonable (read-only operations, no credentials exposed), but the server lacks idempotence documentation and lacks structured output schema declarations. The implementation shows solid technical effort (browser automation, markdown parsing), but the tool definitions need refinement for production LLM agent use.
Render a specific Ad Library URL and return its ads as structured records.
Search the public Facebook Ad Library for a keyword in a given country (works for commercial ads, any country — no token). Renders the SPA with a headless browser, scrolls past the first page, and returns structured ad cards: advertiser, run date, creative count, landing domain, CTA button, headline and body copy. Pass advertiser_page_id to pull every ad from one Page.
Output schema not formally documented. The code returns a dict with fields like 'library_id', 'ad_details_url', 'started_running', 'ads_using_creative', 'advertiser', 'advertiser_handle', 'landing_url', 'landing_domain', 'cta', 'creative_image', 'link_text', 'body', 'platforms', but the tool definitions do not include a formal JSON Schema output specification. LLMs cannot determine what fields to expect or plan downstream calls without explicit output schema.
Descriptions lack LLM-specific guidance. Both tool descriptions explain what the tool does, but lack guidance on WHEN to use it vs. alternatives, WHAT the return structure is, and HOW to handle edge cases (e.g., 'If results are empty, increase wait_seconds').
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | C | 69 | 2026-07-28+ | v2 |
Parameter descriptions for 'wait_seconds' and 'scroll_rounds' are terse and lack actionable guidance. 'how long to let the SPA hydrate before scraping' does not tell the LLM what to do if content is missing or explain the tradeoff (longer = more complete results but slower). A description like 'Time in seconds to wait for page load; raise to 15+ if results empty (default 8)' is more actionable.
Error handling is implicit and provides no recovery guidance. If _render_ad_library() throws an exception (e.g., headless browser times out, Ad Library returns 403), the error bubbles to the LLM with no actionable recovery message. The code comments note 'Facebook flags the headless browser and answers HTTP 403, but still serves the rendered ad cards', this critical detail is not in the tool description or error handling, forcing the LLM to guess.
advertiser_page_id parameter is marked optional but has important semantics not documented: 'if set, target that Page's "all ads" view instead of a keyword search. This view is heavier client-rendered and does not always hydrate headless, a keyword search of the advertiser's name is the more reliable path.' This critical dependency (advertiser_page_id is less reliable) should be in the parameter description, not just in the tool docstring.
No idempotence declaration. The tools perform headless browser renders and parse results; these operations are not explicitly stated to be idempotent. An LLM retrying a failed call may not know if duplicate results will be returned or if the Ad Library pagination state will be consistent across retries.
Result limit not documented. The code returns a list of ads up to 'scroll_rounds * ~24' cards, but no explicit cap or pagination scheme. An LLM might request a 'deep sweep' with scroll_rounds=100 and receive thousands of results, bloating the context window. The tool should document: 'Results typically 20-500 ads depending on scroll_rounds; no pagination; for large sweeps, use multiple queries with different keywords.'