Skip to main content
Glama

LeadMarina

Query the lead library

query_leads
Read-only

Search ALL leads the account has ever collected (across every search) with filters and sorting — no new search is run and no leads are spent. A Google listing found more than once appears once, with its most recent data. Up to 1,000 leads per call (default 25). When next_cursor is set, call again with cursor = next_cursor for the next page: the cursor carries its filters and sort, so it can be sent alone. Pass search_id to query one search's leads instead of the whole library. Example — plumbers in Austin rated 4.5 or higher with a safe email, most-reviewed first: filters=[{field:'category',operator:'contains',value:'plumb'},{field:'city',operator:'is',value:'Austin',conj:'and'},{field:'rating',operator:'gte',value:'4.5',conj:'and'},{field:'email_status',operator:'is',value:'safe',conj:'and'}], sort=[{id:'reviews',dir:-1}]. A field, operator or value the tool cannot use comes back as an error that names it and lists what works. Pass campaign_id instead of search_id to query one advanced search's leads.

Input Schema

NameRequiredDescriptionDefault
sortNoSort keys in priority order, e.g. [{id:'reviews',dir:-1}] for most-reviewed first. id is any filter field id (the column ids email_1, email_1_status, phone_1, phone_1_type and phone_1_carrier work too); dir is 1 ascending (the default) or -1 descending. A lead with no value for a key (null) sorts after the leads that have one, in either direction — except social_facebook, social_instagram, social_linkedin, social_x, social_youtube, social_tiktok, social_yelp, gbp_url, logo_url, main_image_url, which sort by whether a lead has one, not by the link: ascending puts the leads WITHOUT one first, so use dir -1 to put the leads that have one first. Without a sort the order is arbitrary but stable.
limitNoMax leads to return (default 25, max 1,000).
cursorNonext_cursor from the previous call, for the next page. It carries that call's filters and sort: send it alone, or with the same filters and sort — different ones are refused. Leave it out to start a new query.
filtersNoConditions, each {field, operator, value, conj}; omit to match every lead. text fields [operators: contains, not_contains, is, is_not, is_empty, is_not_empty]: name, category, city, state, country, postal_code, address, website, primary_email (or email_1), email_2, email_3, primary_phone (or phone_1), phone_carrier (or phone_1_carrier), phone_2, phone_2_carrier, phone_3, phone_3_carrier, owner_name, contact_name, contact_title, social_facebook, social_instagram, social_linkedin, social_x, social_youtube, social_tiktok, social_yelp, gbp_url, google_cid, logo_url, main_image_url. number fields [operators: eq, neq, gt, gte, lt, lte, is_empty, is_not_empty]: rank, rating, reviews, people. yes/no fields [operators: is_true, is_false, is_empty, is_not_empty]: claimed. choice fields [operators: is, is_not, is_empty, is_not_empty]: website_status take none (No website), live, unreachable, blocked (Blocked our crawler), unknown, pending; email_status (or email_1_status), email_2_status, email_3_status take safe, risky, invalid, unknown; phone_line_type (or phone_1_type), phone_2_type, phone_3_type take mobile, fixed_line (Landline), fixed_line_or_mobile (Landline or mobile), voip, non_fixed_voip, toll_free (Toll-free), premium_rate, shared_cost, personal_number (Personal), pager, uan, voicemail, unknown. value is one string (a number as text, e.g. '4.5'); is_empty, is_not_empty, is_true and is_false take none. Text matching ignores case. Phone fields compare digits, so '(512) 555' finds +15125550100; website is / is_not compare domains when given a domain ('acme.com', or 'https://www.acme.com/'), and whole addresses when given a path, a query or a fragment ('acme.com/locations/austin'), either way ignoring http(s)://, www. and a closing slash. conj ('and' or 'or') joins a condition to the one before it, and 'and' binds tighter: A and B or C means (A and B) or C. To match any of several values, send one condition per value joined with conj 'or', repeating the other conditions in each group.
search_idNoOnly this search's leads (a search_id from search_leads or list_searches) — e.g. to narrow one search before export_leads with the same search_id and filters. Omit to query the whole library.
campaign_idNoOnly this advanced search's leads (an id from run_advanced_search or list_advanced_searches). Not with search_id.

Output Schema

NameRequiredDescriptionDefault
resultsYes
returnedYes
next_cursorNoPass back as 'cursor' for the next page (it carries the filters and sort); null on the last page.
data_sourcesNoWhere the sources, licences and notices are for emails and owner names that may come from open datasets and public records. Pass it on with any of this data you share.
total_matchingNoExact match count — on the first page only; null on later pages.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • addedInput schema / properties / campaign_id
      Added value: +{
      +  "description": "Only this advanced search's leads (an id from run_advanced_search or list_advanced_searches). Not with search_id.",
      +  "type": "string"
      +}
  2. Changed3 schema fields changed
    • changedInput schema / properties / filters / description
      Previous value: -"Conditions, each {field, operator, value, conj}; omit to match every lead. text fields [operators: contains, not_contains, is, is_not, is_empty, is_not_empty]: name, category, city, state, address, website, primary_email (or email_1), email_2, email_3, primary_phone (or phone_1), phone_carrier (or phone_1_carrier), phone_2, phone_2_carrier, phone_3, phone_3_carrier, owner_name, social_facebook, social_instagram, social_linkedin, social_x, social_youtube, social_tiktok, social_yelp, gbp_url, google_cid, logo_url, main_image_url. number fields [operators: eq, neq, gt, gte, lt, lte, is_empty, is_not_empty]: rank, rating, reviews. yes/no fields [operators: is_true, is_false, is_empty, is_not_empty]: claimed. choice fields [operators: is, is_not, is_empty, is_not_empty]: email_status (or email_1_status), email_2_status, email_3_status take safe, risky, invalid, unknown; phone_line_type (or phone_1_type), phone_2_type, phone_3_type take mobile, fixed_line (Landline), fixed_line_or_mobile (Landline or mobile), voip, non_fixed_voip, toll_free (Toll-free), premium_rate, shared_cost, personal_number (Personal), pager, uan, voicemail, unknown. value is one string (a number as text, e.g. '4.5'); is_empty, is_not_empty, is_true and is_false take none. Text matching ignores case. Phone fields compare digits, so '(512) 555' finds +15125550100; website is / is_not compare domains when given a domain ('acme.com', or 'https://www.acme.com/'), and whole addresses when given a path, a query or a fragment ('acme.com/locations/austin'), either way ignoring http(s)://, www. and a closing slash. conj ('and' or 'or') joins a condition to the one before it, and 'and' binds tighter: A and B or C means (A and B) or C. To match any of several values, send one condition per value joined with conj 'or', repeating the other conditions in each group."New value: +"Conditions, each {field, operator, value, conj}; omit to match every lead. text fields [operators: contains, not_contains, is, is_not, is_empty, is_not_empty]: name, category, city, state, country, postal_code, address, website, primary_email (or email_1), email_2, email_3, primary_phone (or phone_1), phone_carrier (or phone_1_carrier), phone_2, phone_2_carrier, phone_3, phone_3_carrier, owner_name, contact_name, contact_title, social_facebook, social_instagram, social_linkedin, social_x, social_youtube, social_tiktok, social_yelp, gbp_url, google_cid, logo_url, main_image_url. number fields [operators: eq, neq, gt, gte, lt, lte, is_empty, is_not_empty]: rank, rating, reviews, people. yes/no fields [operators: is_true, is_false, is_empty, is_not_empty]: claimed. choice fields [operators: is, is_not, is_empty, is_not_empty]: website_status take none (No website), live, unreachable, blocked (Blocked our crawler), unknown, pending; email_status (or email_1_status), email_2_status, email_3_status take safe, risky, invalid, unknown; phone_line_type (or phone_1_type), phone_2_type, phone_3_type take mobile, fixed_line (Landline), fixed_line_or_mobile (Landline or mobile), voip, non_fixed_voip, toll_free (Toll-free), premium_rate, shared_cost, personal_number (Personal), pager, uan, voicemail, unknown. value is one string (a number as text, e.g. '4.5'); is_empty, is_not_empty, is_true and is_false take none. Text matching ignores case. Phone fields compare digits, so '(512) 555' finds +15125550100; website is / is_not compare domains when given a domain ('acme.com', or 'https://www.acme.com/'), and whole addresses when given a path, a query or a fragment ('acme.com/locations/austin'), either way ignoring http(s)://, www. and a closing slash. conj ('and' or 'or') joins a condition to the one before it, and 'and' binds tighter: A and B or C means (A and B) or C. To match any of several values, send one condition per value joined with conj 'or', repeating the other conditions in each group."
    • addedOutput schema / properties / results / items / properties / country
      Added value: +{
      +  "description": "ISO 3166 alpha-2 (\"US\", \"GB\").",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / results / items / properties / postal_code
      Added value: +{
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
  3. Changed3 schema fields changed
    • addedOutput schema / properties / results / items / properties / contact_name
      Added value: +{
      +  "description": "The person the business's own website names as running part of it (a manager, a practice's doctor) when it names no owner.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / results / items / properties / contact_title
      Added value: +{
      +  "description": "That person's role, as the website gives it.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / results / items / properties / people
      Added value: +{
      +  "description": "Everyone the business's own website names as working there, each { name, title, kind } (kind: owner, manager, practitioner, staff).",
      +  "items": {
      +    "type": "object"
      +  },
      +  "type": [
      +    "array",
      +    "null"
      +  ]
      +}
  4. Changed1 schema field changed
    • addedOutput schema / properties / data_sources
      Added value: +{
      +  "description": "Where the sources, licences and notices are for emails and owner names that may come from open datasets and public records. Pass it on with any of this data you share.",
      +  "type": "string"
      +}
  5. Changed16 schema fields changed
    • changedInput schema / properties / cursor / description
      Previous value: -"Opaque pagination cursor from a previous call."New value: +"next_cursor from the previous call, for the next page. It carries that call's filters and sort: send it alone, or with the same filters and sort — different ones are refused. Leave it out to start a new query."
    • changedInput schema / properties / filters / description
      Previous value: -"Filter conditions (optional — omit for everything, newest data first)."New value: +"Conditions, each {field, operator, value, conj}; omit to match every lead. text fields [operators: contains, not_contains, is, is_not, is_empty, is_not_empty]: name, category, city, state, address, website, primary_email (or email_1), email_2, email_3, primary_phone (or phone_1), phone_carrier (or phone_1_carrier), phone_2, phone_2_carrier, phone_3, phone_3_carrier, owner_name, social_facebook, social_instagram, social_linkedin, social_x, social_youtube, social_tiktok, social_yelp, gbp_url, google_cid, logo_url, main_image_url. number fields [operators: eq, neq, gt, gte, lt, lte, is_empty, is_not_empty]: rank, rating, reviews. yes/no fields [operators: is_true, is_false, is_empty, is_not_empty]: claimed. choice fields [operators: is, is_not, is_empty, is_not_empty]: email_status (or email_1_status), email_2_status, email_3_status take safe, risky, invalid, unknown; phone_line_type (or phone_1_type), phone_2_type, phone_3_type take mobile, fixed_line (Landline), fixed_line_or_mobile (Landline or mobile), voip, non_fixed_voip, toll_free (Toll-free), premium_rate, shared_cost, personal_number (Personal), pager, uan, voicemail, unknown. value is one string (a number as text, e.g. '4.5'); is_empty, is_not_empty, is_true and is_false take none. Text matching ignores case. Phone fields compare digits, so '(512) 555' finds +15125550100; website is / is_not compare domains when given a domain ('acme.com', or 'https://www.acme.com/'), and whole addresses when given a path, a query or a fragment ('acme.com/locations/austin'), either way ignoring http(s)://, www. and a closing slash. conj ('and' or 'or') joins a condition to the one before it, and 'and' binds tighter: A and B or C means (A and B) or C. To match any of several values, send one condition per value joined with conj 'or', repeating the other conditions in each group."
    • changedInput schema / properties / limit / description
      Previous value: -"Max leads to return (default 25, max 1000)."New value: +"Max leads to return (default 25, max 1,000)."
    • addedInput schema / properties / search_id
      Added value: +{
      +  "description": "Only this search's leads (a search_id from search_leads or list_searches) — e.g. to narrow one search before export_leads with the same search_id and filters. Omit to query the whole library.",
      +  "type": "string"
      +}
    • changedInput schema / properties / sort / description
      Previous value: -"Sort keys, e.g. [{id:'reviews',dir:-1}]. Optional."New value: +"Sort keys in priority order, e.g. [{id:'reviews',dir:-1}] for most-reviewed first. id is any filter field id (the column ids email_1, email_1_status, phone_1, phone_1_type and phone_1_carrier work too); dir is 1 ascending (the default) or -1 descending. A lead with no value for a key (null) sorts after the leads that have one, in either direction — except social_facebook, social_instagram, social_linkedin, social_x, social_youtube, social_tiktok, social_yelp, gbp_url, logo_url, main_image_url, which sort by whether a lead has one, not by the link: ascending puts the leads WITHOUT one first, so use dir -1 to put the leads that have one first. Without a sort the order is arbitrary but stable."
    • changedOutput schema / properties / next_cursor / description
      Previous value: -"Pass back as 'cursor' for the next page."New value: +"Pass back as 'cursor' for the next page (it carries the filters and sort); null on the last page."
    • addedOutput schema / properties / results / items / properties / address
      Added value: +{
      +  "description": "Street address from the Google listing.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / results / items / properties / claimed
      Added value: +{
      +  "description": "Whether the owner has claimed the Google listing.",
      +  "type": [
      +    "boolean",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / results / items / properties / email_status
      Added value: +{
      +  "description": "Email 1's SMTP verdict: safe | risky | invalid | unknown.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / results / items / properties / google_cid
      Added value: +{
      +  "description": "Google listing id (the query_leads google_cid field).",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / results / items / properties / maps_url
      Added value: +{
      +  "description": "The Google Maps link for the listing.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / results / items / properties / phone
      Added value: +{
      +  "description": "The Google Maps number.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / results / items / properties / phone_line_type
      Added value: +{
      +  "description": "Phone 1's line type: mobile, fixed_line, voip or toll_free usually; also unknown, fixed_line_or_mobile and the other values query_leads lists.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / results / items / properties / primary_email
      Added value: +{
      +  "description": "Email 1: the first address in emails_data, the one email_status describes. Before emails are verified, the lead's primary address.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / results / items / properties / primary_phone
      Added value: +{
      +  "description": "Phone 1: the first number in phones_data, the one phone_line_type describes. Before phones are verified, the lead's primary number, or its Google Maps number.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • changedOutput schema / properties / total_matching / description
      Previous value: -"Exact match count (first page only; absent on later pages)."New value: +"Exact match count — on the first page only; null on later pages."
  6. Changed1 schema field changed
    • changedInput schema / properties / limit / description
      Previous value: -"Max leads to return (default 25, max 100)."New value: +"Max leads to return (default 25, max 1000)."
  7. First observed

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already declare readOnly/non-destructive/closed-world, but the description adds genuinely non-obvious behavior: no credits are spent, duplicate listings collapse to the most recent record, pagination is cursor-based with the cursor carrying its own filters and sort (and mismatched filters being refused), and invalid field/operator/value combos return a self-describing error. That is real operational context beyond the annotation set.

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?

Front-loaded with the differentiator and the pagination rule before any detail, and the example is compact. Minor weakness: the campaign_id clause is stranded at the very end after the error-handling sentence, breaking the otherwise logical flow of the search_id/campaign_id scoping discussion.

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?

For a six-parameter, zero-required query tool with a full output schema, everything an agent needs is present: scope, cost, dedup semantics, page size, cursor reuse rules, scoping by search or campaign, and error recovery. Nothing material is left to inference.

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 coverage is 100%, so the baseline is 3; the description earns above baseline by supplying a concrete filter example (plumbers in Austin, rating >= 4.5, safe email, sorted by reviews desc) that shows how the nested objects compose, plus the default-25 / 1,000-cap behavior. It does not restate the operator taxonomies, which the schema already handles well, so it stops short of 5.

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?

States a specific verb and resource ("Search ALL leads the account has ever collected") and immediately scopes it against the obvious alternative: "no new search is run and no leads are spent," which is exactly what distinguishes it from search_leads. The dedup rule ("a Google listing found more than once appears once, with its most recent data") further pins down what the result set actually is.

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?

Gives explicit routing conditions: use this to query the existing library, pass search_id for one search, campaign_id for one advanced search, and it names the sibling that produces those ids (search_leads, list_searches, run_advanced_search, list_advanced_searches) as well as export_leads. The exclusion "Not with search_id" is stated in the schema and implied by "instead of search_id" in the description.

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.