Skip to main content
Glama

Search webmasters

search_webmasters
Read-only

Search webmasters (affiliates) — a webmaster is a whole ad NETWORK (landing domains, pixels, pages and geos grouped as one). Copy workflow: search by domain, take the canonical UUID, then search_creatives/search_ads with webmaster_id. Finish with the most reused material: get_webmaster.top_creatives or search_creatives sort_by=total_ads; total_ads is how many ads reuse the same material over its lifetime, not a weekly debut count. Returns total/active counts, top countries and geo counts. Find by name or pixel_id/domain/page_id/resolved_ip. Filter by category, AI category/subcategory, countries, languages, platforms, format, current status, dates and count ranges. For a geo ranking combine countries + min_geo_share + sort_by=relevance. Save returned UUIDs with add_to_favorites. Exact pixel_id/domain/page_id/resolved_ip lookups use the webmaster's CURRENT canonical identifier membership; historical identifiers removed from a webmaster do not count as matches. Exact page_id results report fanpages_status as available, partial, unavailable or not_applicable. When available, the selected page is included in fanpages[] with life_status from the same page-lifetime source as /advertisers (alive/deleted/banned, or unknown when lifetime coverage has no row); when unavailable, an omitted fanpages[] is not evidence that the page is alive. FREE PLAN COUNTS: when the plan's archive window narrows a request, pagination.total_status is "restricted" and meta.plan_window_cutoff names the boundary — the count then describes the window actually searched, NOT the one requested, so a zero means "nothing in the archive window" and never "nothing exists". QUOTA: 1 MCP credit per DELIVERED result from your plan balance (free starter: 500 MCP credits lifetime; paid plans: 40,000/month; short pages auto-refund — you pay only for results you receive). Default page is 20 results = 20 MCP credits; pass limit (1–200) to size it. get_usage is free. Autonomous agents must be linked to a spytrend account to access data — an unlinked agent gets an actionable connect-your-account refusal (create agent credentials at spytrend.com/settings?tab=ai, or a human claims it by client_id), NOT a server error. Calls are rate-limited per authenticated user (deployment defaults: 60/minute and 1000/hour); a rate-limit refusal is an MCP tool error with structured error.code=rate_limit_exceeded, scope, window and retry_after_seconds. CONCURRENCY: heavy analytical calls (get_trends, search_ads, search_creatives) are admitted ONE AT A TIME per account — fanning out 5-10 of them in parallel does not go faster, it returns admission refusals for all but one. Issue heavy calls sequentially; light lookups (get_ad, get_advertiser, get_usage) are not gated. TIKTOK SOURCE: pass source="tiktok" to query the TIKTOK ad corpus instead of Meta. (The platforms filter does NOT do this — platforms are Meta publisher placements.) ⚠️ TIKTOK IS PAID PER ROW at a premium: every DELIVERED TikTok ad row costs 10 MCP credits and every TikTok webmaster row/card costs 10 MCP credits (Meta rows cost 1), deducted from the same MCP credit balance (paid plans: 40,000/month = up to 400 TikTok rows). Request small limits and narrow filters; you are charged only for rows actually delivered (short pages auto-refund; if the balance covers only part of the request, that part is delivered — a 3-row request on a 15-MCP credit balance returns 1 row and charges 10). TikTok requires a Pro-or-higher plan: free/starter callers and unlinked agents get an actionable upgrade refusal, never data and never a charge. TikTok rows have their OWN shape, returned under tiktok_data (verified live 2026-07-30): id/external_id (TikTok ad id), advertiser{id,external_id,name} plus advertiser_id (TikTok BUSINESS id), start_date/end_date (the ad's delivery window) and first_seen_date/last_seen_date (Spytrend indexing), days_active, is_active + status_today, ai_category/ai_subcategory, countries + targeting_geos + targeting_details (OS/age/gender/regions), audience_size, objective, sponsor (the 'paid for by' funder) and registry_location, landing_domain + link_url, body/title/call_to_action, creative_format, creative_id, creative_ad_count (how many ads reuse that creative), is_blurred/is_cloaked, and media[] with media_type + thumbnail_url on media-tt.spytrend.com. Media comes WITH the row — do NOT call get_media for TikTok ids (it serves Meta entities only). There are no TikTok engagement counters (plays/likes/comments/shares) on this surface. The TikTok feed serves the ARCHIVED TikTok corpus — exactly what the spytrend.com /ads TikTok tab shows — and pagination carries has_more/next_cursor plus total with total_status (exact, estimated or unavailable; unavailable is not zero). With source=tiktok rows are TikTok webmaster clusters. Supported TikTok webmaster filters: query, domain, countries (+min_geo_share), exclude_countries, ai_category (dominant), min_total_ads, min_active_ads, date_from/date_to, has_funder, sort_by=total_ads|active_ads|created_at|relevance, sort_order, offset (0-5000), limit (server caps TikTok at 100). Date filters select clusters whose activity window overlaps the requested period. Active means shown within the last 3 calendar days. Meta-only lookups, languages/platforms/creative_format(s), status_today, ai_subcategory, folder_id and saved are REJECTED before charging. Default TikTok limit is 10; each delivered row costs 10 MCP credits. PRICING: price × number of filters set (min 1), on paid plans ×1 after 500 MCP credits/month; one-time consent via accept_pricing_terms; before calling, get the maximum debit from estimate_cost (free) and tell the user.

Input Schema

NameRequiredDescriptionDefault
limitNomax results (default 20; each delivered result costs 1 MCP credit).
queryNofree-text webmaster (affiliate) name to search for
savedNosaved scope selector. Use saved=all to restrict results to webmasters saved in ANY favourites folder.
domainNopoint lookup: webmasters on this landing domain
offsetNopagination offset — skip the first N rows. The /webmasters ranking pages by offset (not a cursor), so this is how a caller reaches page two and beyond. Max 5000.
sourceNowebmaster corpus: meta (default — Facebook/Meta networks, 1 MCP credit per delivered row) or tiktok (TikTok business-id clusters — ⚠️ PAID: 10 MCP credits per DELIVERED row; Pro plan required; default limit drops to 10, server caps TikTok at 100)
date_toNoonly webmasters active on or before this date (YYYY-MM-DD)
page_idNoexact current-ownership lookup by Facebook page id (a pasted facebook.com page link in any form is also accepted and resolved to the canonical page id); fanpages_status reports lifecycle coverage and available results include alive/deleted/banned/unknown lifecycle
sort_byNoorder by: total_ads (default), active_ads or relevance (relevance ranks by ad-VOLUME in the selected countries — geo-relevance; needs countries)
categoryNovertical filter: gambling or other
pixel_idNopoint lookup: webmasters using this Facebook pixel id
countriesNoISO country codes the webmaster's ads ran in
date_fromNoonly webmasters active on or after this date (YYYY-MM-DD)
folder_idNorestrict results to webmasters saved in this favourites folder UUID. Mirrors the /webmasters folder view.
languagesNolanguage codes filter
platformsNoplatform names filter (e.g. facebook, instagram)
has_funderNoTikTok only (source=tiktok): keep only clusters with an EXTERNAL 'paid for by' sponsor (the funded-by filter). Rejected for the Meta corpus.
sort_orderNosort direction: asc or desc (default desc)
ai_categoryNoAI category slug filter (e.g. gambling_and_betting)
resolved_ipNopoint lookup: webmasters whose domain resolves to this IP
status_todayNocurrent status: active, inactive or vanished
min_geo_shareNoGEO-RELEVANCE gate (0-1, the relevant-only toggle): keep only webmasters where the 'countries' you pass are at least this fraction of their ads — i.e. that geo is their DOMINANT geo (e.g. 0.5 = country >=50% of their ads). Without it, countries is a mere 'present-in' match and a webmaster with 0.5% of ads in BR ranks as a 'BR webmaster'. Requires countries. Each returned row's geo field carries the per-country ad-count breakdown so you can read the real exposure.
min_total_adsNoonly webmasters with at least this many total ads
ai_subcategoryNoAI subcategory slug filter
min_active_adsNoonly webmasters with at least this many ACTIVE ads
creative_formatNosingle creative format filter (legacy form). Prefer creative_formats[] — the visible /webmasters control is a multi-select.
creative_formatsNocreative formats to include (video, carousel, single, dynamic). Mirrors the visible /webmasters format multi-select.
exclude_countriesNoISO country codes to EXCLUDE — drops webmasters whose ads run in ANY of them. Mirrors the /webmasters geo EXCLUDE column; combine with countries to keep one market while removing noise markets.

Output Schema

NameRequiredDescriptionDefault
dataNo
metaNo
errorNoPresent only when isError is true: machine-readable failure. The human explanation stays in content.
paramsNoRequest parameter echo: applied = parameters that shaped this result; normalized = parameters rewritten before applying (alias, type coercion, resolved id); ignored = parameters that were accepted but NOT applied, with the reason.
sourceNo
paginationNo
addon_hintsNoAdd-on modules that have additional data for objects of this result (no prices, no paid values). subscription_status=active: the account has the module — use its normal path. Do not push a purchase the user did not ask about.
tiktok_dataNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed11 schema fields changed
    • changedInput schema / properties / limit / description
      Previous value: -"max results (default 20; each delivered result costs 1 token)."New value: +"max results (default 20; each delivered result costs 1 MCP credit)."
    • removedInput schema / properties / sa_country
      Removed value: -{
      -  "description": "ISO-2 codes of the countries the ads are shown in (OR), e.g. US, GB, DE — NOT the visitor country of the site. Implies search_arbitrage.",
      -  "items": {
      -    "type": "string"
      -  },
      -  "type": [
      -    "null",
      -    "array"
      -  ]
      -}
    • removedInput schema / properties / sa_lang
      Removed value: -{
      -  "description": "ISO 639-1 language codes of the search phrases (OR), e.g. en, de, es. Implies search_arbitrage.",
      -  "items": {
      -    "type": "string"
      -  },
      -  "type": [
      -    "null",
      -    "array"
      -  ]
      -}
    • removedInput schema / properties / sa_network
      Removed value: -{
      -  "description": "search-arbitrage network slugs (OR): the search-ads network the site monetises through, e.g. tonic, visymo, medianet, cortex, predicto, codefuel, brandclick, system1. Live list with counts: search_arbitrage_facets. Implies search_arbitrage.",
      -  "items": {
      -    "type": "string"
      -  },
      -  "type": [
      -    "null",
      -    "array"
      -  ]
      -}
    • removedInput schema / properties / sa_phrase
      Removed value: -{
      -  "description": "exact search phrases (OR), lower-case, as returned by search_arbitrage_phrases; a phrase containing a comma cannot be used here. Implies search_arbitrage.",
      -  "items": {
      -    "type": "string"
      -  },
      -  "type": [
      -    "null",
      -    "array"
      -  ]
      -}
    • removedInput schema / properties / sa_pub
      Removed value: -{
      -  "description": "Google publisher account ids (OR), e.g. pub-1902826156353358: the AdSense/AFS account the site earns under — one account usually means one operator. A bare number or ca-pub-… is accepted. Implies search_arbitrage.",
      -  "items": {
      -    "type": "string"
      -  },
      -  "type": [
      -    "null",
      -    "array"
      -  ]
      -}
    • removedInput schema / properties / sa_source
      Removed value: -{
      -  "description": "how the site earns from search (OR): rsoc (Google related-search ads through a partner), afs (Google AdSense for Search), yahoo_feed, bing_feed or other. Implies search_arbitrage.",
      -  "items": {
      -    "enum": [
      -      "rsoc",
      -      "afs",
      -      "yahoo_feed",
      -      "bing_feed",
      -      "other"
      -    ],
      -    "type": "string"
      -  },
      -  "type": [
      -    "null",
      -    "array"
      -  ]
      -}
    • removedInput schema / properties / sa_topic
      Removed value: -{
      -  "description": "topic slugs of the search phrases the sites push (OR); live list with counts: search_arbitrage_facets. Implies search_arbitrage.",
      -  "items": {
      -    "type": "string"
      -  },
      -  "type": [
      -    "null",
      -    "array"
      -  ]
      -}
    • removedInput schema / properties / search_arbitrage
      Removed value: -{
      -  "description": "SEARCH ARBITRAGE filter (confirmed search-arbitrage sites only): true = keep only webmasters (affiliate networks) whose ads lead to search-arbitrage pages — pages that earn from search-ads clicks (Google RSOC/AFS through networks such as Tonic or Visymo, Yahoo/Bing feeds) instead of selling a product. Every sa_* filter implies it. Omit or false = no search-arbitrage filter. Valid sa_* values and counts: search_arbitrage_facets; the phrases behind them: search_arbitrage_phrases.",
      -  "type": "boolean"
      -}
    • changedInput schema / properties / source / description
      Previous value: -"webmaster corpus: meta (default — Facebook/Meta networks, 1 token per delivered row) or tiktok (TikTok business-id clusters — ⚠️ PAID: 100 tokens per DELIVERED row; Pro plan required; default limit drops to 10, server caps TikTok at 100)"New value: +"webmaster corpus: meta (default — Facebook/Meta networks, 1 MCP credit per delivered row) or tiktok (TikTok business-id clusters — ⚠️ PAID: 10 MCP credits per DELIVERED row; Pro plan required; default limit drops to 10, server caps TikTok at 100)"
    • addedOutput schema / properties / addon_hints
      Added value: +{
      +  "description": "Add-on modules that have additional data for objects of this result (no prices, no paid values). subscription_status=active: the account has the module — use its normal path. Do not push a purchase the user did not ask about.",
      +  "items": {
      +    "properties": {
      +      "available_data_types": {
      +        "description": "kinds of data the module confirmedly has for the object",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "info_url": {
      +        "description": "the object (or its limited breakdown) inside the module on spytrend.com",
      +        "type": "string"
      +      },
      +      "module": {
      +        "description": "nutra | crypto | web2app | search_arb | startups",
      +        "type": "string"
      +      },
      +      "object_id": {
      +        "description": "the ad id, or the site domain for site rows",
      +        "type": "string"
      +      },
      +      "preland_downloadable": {
      +        "description": "Nutra / Crypto: the saved pre-lander can be downloaded in the module",
      +        "type": "boolean"
      +      },
      +      "subscription_status": {
      +        "description": "active | not_connected",
      +        "type": "string"
      +      }
      +    },
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  2. Changed4 schema fields changed
    • removedInput schema / properties / sa_evidence_min
      Removed value: -{
      -  "description": "minimum evidence share of the site, 0 to 1 (e.g. 0.8): the fraction of the site's checked pages that showed the network's markers; higher means a more certain verdict. Implies search_arbitrage.",
      -  "maximum": 1,
      -  "minimum": 0,
      -  "type": "number"
      -}
    • removedInput schema / properties / sa_link
      Removed value: -{
      -  "description": "how the ad reaches the site: tracker (a network tracking/redirect link that carries the phrase) or direct (the ad points straight at the monetised site). Omit for both. Implies search_arbitrage.",
      -  "enum": [
      -    "tracker",
      -    "direct"
      -  ],
      -  "type": "string"
      -}
    • removedInput schema / properties / sa_verdict
      Removed value: -{
      -  "description": "detection confidence (OR): confirmed (network markers found on the site) or probable. Omit for both. Implies search_arbitrage.",
      -  "items": {
      -    "enum": [
      -      "confirmed",
      -      "probable"
      -    ],
      -    "type": "string"
      -  },
      -  "type": [
      -    "null",
      -    "array"
      -  ]
      -}
    • changedInput schema / properties / search_arbitrage / description
      Previous value: -"SEARCH ARBITRAGE filter: true = keep only webmasters (affiliate networks) whose ads lead to search-arbitrage pages — pages that earn from search-ads clicks (Google RSOC/AFS through networks such as Tonic or Visymo, Yahoo/Bing feeds) instead of selling a product. Every sa_* filter implies it. Omit or false = no search-arbitrage filter. Valid sa_* values and counts: search_arbitrage_facets; the phrases behind them: search_arbitrage_phrases."New value: +"SEARCH ARBITRAGE filter (confirmed search-arbitrage sites only): true = keep only webmasters (affiliate networks) whose ads lead to search-arbitrage pages — pages that earn from search-ads clicks (Google RSOC/AFS through networks such as Tonic or Visymo, Yahoo/Bing feeds) instead of selling a product. Every sa_* filter implies it. Omit or false = no search-arbitrage filter. Valid sa_* values and counts: search_arbitrage_facets; the phrases behind them: search_arbitrage_phrases."
  3. Changed11 schema fields changed
    • addedInput schema / properties / sa_country
      Added value: +{
      +  "description": "ISO-2 codes of the countries the ads are shown in (OR), e.g. US, GB, DE — NOT the visitor country of the site. Implies search_arbitrage.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedInput schema / properties / sa_evidence_min
      Added value: +{
      +  "description": "minimum evidence share of the site, 0 to 1 (e.g. 0.8): the fraction of the site's checked pages that showed the network's markers; higher means a more certain verdict. Implies search_arbitrage.",
      +  "maximum": 1,
      +  "minimum": 0,
      +  "type": "number"
      +}
    • addedInput schema / properties / sa_lang
      Added value: +{
      +  "description": "ISO 639-1 language codes of the search phrases (OR), e.g. en, de, es. Implies search_arbitrage.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedInput schema / properties / sa_link
      Added value: +{
      +  "description": "how the ad reaches the site: tracker (a network tracking/redirect link that carries the phrase) or direct (the ad points straight at the monetised site). Omit for both. Implies search_arbitrage.",
      +  "enum": [
      +    "tracker",
      +    "direct"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / sa_network
      Added value: +{
      +  "description": "search-arbitrage network slugs (OR): the search-ads network the site monetises through, e.g. tonic, visymo, medianet, cortex, predicto, codefuel, brandclick, system1. Live list with counts: search_arbitrage_facets. Implies search_arbitrage.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedInput schema / properties / sa_phrase
      Added value: +{
      +  "description": "exact search phrases (OR), lower-case, as returned by search_arbitrage_phrases; a phrase containing a comma cannot be used here. Implies search_arbitrage.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedInput schema / properties / sa_pub
      Added value: +{
      +  "description": "Google publisher account ids (OR), e.g. pub-1902826156353358: the AdSense/AFS account the site earns under — one account usually means one operator. A bare number or ca-pub-… is accepted. Implies search_arbitrage.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedInput schema / properties / sa_source
      Added value: +{
      +  "description": "how the site earns from search (OR): rsoc (Google related-search ads through a partner), afs (Google AdSense for Search), yahoo_feed, bing_feed or other. Implies search_arbitrage.",
      +  "items": {
      +    "enum": [
      +      "rsoc",
      +      "afs",
      +      "yahoo_feed",
      +      "bing_feed",
      +      "other"
      +    ],
      +    "type": "string"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedInput schema / properties / sa_topic
      Added value: +{
      +  "description": "topic slugs of the search phrases the sites push (OR); live list with counts: search_arbitrage_facets. Implies search_arbitrage.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedInput schema / properties / sa_verdict
      Added value: +{
      +  "description": "detection confidence (OR): confirmed (network markers found on the site) or probable. Omit for both. Implies search_arbitrage.",
      +  "items": {
      +    "enum": [
      +      "confirmed",
      +      "probable"
      +    ],
      +    "type": "string"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedInput schema / properties / search_arbitrage
      Added value: +{
      +  "description": "SEARCH ARBITRAGE filter: true = keep only webmasters (affiliate networks) whose ads lead to search-arbitrage pages — pages that earn from search-ads clicks (Google RSOC/AFS through networks such as Tonic or Visymo, Yahoo/Bing feeds) instead of selling a product. Every sa_* filter implies it. Omit or false = no search-arbitrage filter. Valid sa_* values and counts: search_arbitrage_facets; the phrases behind them: search_arbitrage_phrases.",
      +  "type": "boolean"
      +}
  4. Changed3 schema fields changed
    • addedOutput schema / properties / error
      Added value: +{
      +  "description": "Present only when isError is true: machine-readable failure. The human explanation stays in content.",
      +  "properties": {
      +    "code": {
      +      "description": "fine-grained, stable failure code (e.g. invalid_arguments, backend_busy)",
      +      "type": "string"
      +    },
      +    "kind": {
      +      "description": "failure class: invalid_argument, not_found, permission_denied, unauthenticated, quota_exceeded, rate_limited, temporarily_unavailable, unavailable_until_ready, unsupported, internal",
      +      "type": "string"
      +    },
      +    "message": {
      +      "description": "the same human text as content[0]",
      +      "type": "string"
      +    },
      +    "outcome": {
      +      "description": "activity-feed outcome class",
      +      "type": "string"
      +    },
      +    "param": {
      +      "description": "the request parameter the failure is about, when known",
      +      "type": "string"
      +    },
      +    "retry_after_seconds": {
      +      "description": "wait this long before retrying",
      +      "type": "integer"
      +    },
      +    "retryable": {
      +      "description": "true when repeating the SAME call can succeed (after retry_after_seconds when present)",
      +      "type": "boolean"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedOutput schema / properties / params
      Added value: +{
      +  "description": "Request parameter echo: applied = parameters that shaped this result; normalized = parameters rewritten before applying (alias, type coercion, resolved id); ignored = parameters that were accepted but NOT applied, with the reason.",
      +  "properties": {
      +    "applied": {
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    },
      +    "ignored": {
      +      "items": {
      +        "properties": {
      +          "param": {
      +            "type": "string"
      +          },
      +          "reason": {
      +            "type": "string"
      +          },
      +          "to": {
      +            "description": "the parameter it was applied as, when renamed",
      +            "type": "string"
      +          },
      +          "value": {
      +            "description": "the value actually applied, when rewritten",
      +            "type": "string"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "normalized": {
      +      "items": {
      +        "properties": {
      +          "param": {
      +            "type": "string"
      +          },
      +          "reason": {
      +            "type": "string"
      +          },
      +          "to": {
      +            "description": "the parameter it was applied as, when renamed",
      +            "type": "string"
      +          },
      +          "value": {
      +            "description": "the value actually applied, when rewritten",
      +            "type": "string"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "type": "array"
      +    }
      +  },
      +  "type": "object"
      +}
    • removedOutput schema / required
      Removed value: -[
      -  "data",
      -  "pagination"
      -]
  5. Changed10 schema fields changed
    • addedOutput schema / properties / data / items / properties / ai_categories
      Added value: +{
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedOutput schema / properties / data / items / properties / domains
      Added value: +{
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedOutput schema / properties / data / items / properties / domains_count
      Added value: +{
      +  "type": "integer"
      +}
    • addedOutput schema / properties / data / items / properties / page_ids
      Added value: +{
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedOutput schema / properties / data / items / properties / stats_refreshed_at
      Added value: +{
      +  "type": "string"
      +}
    • addedOutput schema / properties / data / items / properties / stopped_ads
      Added value: +{
      +  "type": "integer"
      +}
    • addedOutput schema / properties / data / items / properties / top_countries
      Added value: +{
      +  "items": true,
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedOutput schema / properties / data / items / properties / windowed_active_ads
      Added value: +{
      +  "type": "integer"
      +}
    • addedOutput schema / properties / data / items / properties / windowed_stopped_ads
      Added value: +{
      +  "type": "integer"
      +}
    • addedOutput schema / properties / data / items / properties / windowed_total_ads
      Added value: +{
      +  "type": "integer"
      +}
  6. First observed

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false, but the description adds substantial behavioral context beyond them: 1 MCP credit per delivered row, 10 credits per delivered TikTok row, plan-based quota, rate-limit refusal semantics, concurrency gating, and the fact that unlinked agents receive an actionable connect-account refusal rather than a server error. It also discloses subtle correctness semantics — exact identifier lookups use current membership only, and plan-window narrowing makes a zero mean 'nothing in the archive window', not 'nothing exists'.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The opening paragraph is well front-loaded, but the description then balloons into a multi-section manual covering pricing, rate limits, concurrency policies for unrelated tools (get_trends, search_ads, search_creatives), and a long field-by-field description of TikTok AD row shape that does not apply to this tool's webmaster-cluster output. Sentences such as the media-tt thumbnail and 'no TikTok engagement counters' notes do not earn their place here and actively blur what this endpoint returns.

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?

With an output schema present and 100% schema coverage, the description is more than complete on inputs, billing and access requirements. The gap is coherence rather than missing information: the long tiktok_data field enumeration describes ad rows, then the text later corrects itself with 'With source=tiktok rows are TikTok webmaster clusters', leaving the agent briefly unsure which return shape applies.

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

Parameters4/5

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

Schema description coverage is 100%, so the baseline is 3. The description adds cross-parameter semantics the schema does not: sort_by=relevance requires countries, min_geo_share gates geo dominance vs mere presence, and Meta-only filters (languages/platforms/status_today/folder_id/saved) are rejected under source=tiktok before charging. It does not, however, explain every non-obvious parameter (e.g. saved=all vs folder_id) beyond what the schema already says.

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 first sentence gives a specific verb and resource and then defines the entity ('a webmaster is a whole ad NETWORK'), which is essential context an agent cannot get from the name alone. It also names the sibling tools that handle follow-on work (search_creatives, search_ads, get_webmaster.top_creatives, add_to_favorites), so the agent can place this tool in the workflow.

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

Usage Guidelines5/5

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

It states an explicit copy workflow (search by domain, take the canonical UUID, then query search_creatives/search_ads with webmaster_id) and gives selection rules between modes, e.g. 'pass source="tiktok" to query the TIKTOK ad corpus' and 'For a geo ranking combine countries + min_geo_share + sort_by=relevance'. It also warns which filters are rejected in TikTok mode, which is unusable guidance without this text.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.