Skip to main content
Glama

Record Search Results

record_search_results

The discovery tools (exa_find_people, search_linkedin_people, fetch_post_engagers, theirstack_search, find_companies_by_tech_stack, exa_search_news, search_news_events) already save and link their own matches when run in an agent. Use this tool on their rows to filter them (a verdict on each screened row, below) or to add to a saved row — columns, signals, or evaluations — always under the same identifier and list_name the discovery tool saved it with; a different list_name makes a second row.

Dedup per (agent, identifier, list_name). Existing rows: data['signals'] accumulates across runs (union by URL); other fields are last-writer-wins. Unset fields preserve existing values. Cross-list duplicates of the same identifier are allowed (intentional — two lists may track the same entity for different reasons).

Identifiers are stored canonical — the registrable domain (eTLD+1) for companies (https://www.Acme.com/ and news.acme.com both → acme.com), bare slug for LinkedIn (https://linkedin.com/in/JohnDoe/ → johndoe). Match the canonical form when querying via query_search_results.

Empty-list semantics: signals: [] preserves accumulated signals (the cross-run contract); evaluations: [] REPLACES (LLM-rewritten per run).

Put every user-requested display attribute — a metric, a person's email, any column the user asked to see — in data.columns as {label: value}, one entry per column under the label the user asked for. columns merges by key-union across runs (new values win). The Output tab and CSV render these as columns; a value written to a top-level extra key instead is not surfaced.

Set each result's source to the discovery tool that surfaced that candidate — per row, so a batch merged from several tools keeps each row's origin. Leave it at 'agent' only for rows no discovery tool surfaced.

A Websets list screens its own new rows against the search's criteria, and a discovery tool checks the rows it saves against the criteria it was called with, each giving a row a verdict, 'unclear' when its details are too thin to judge, so a verdict you record on those rows overrules that one. For any other row, this is also how you filter what you screened: give each screened row a verdict — 'qualified' for one you keep, 'rejected' with a verdict_reason for one you drop — under the identifier and list_name it was saved with, so the verdict lands on that row. To mark a row already saved, re-record it with its identifier, display_name and list_name plus the verdict; the data you leave out stays as stored. A rejected row stays saved but drops out of the list the user sees by default; the table counts it as "didn't match" beside its own count, and the user clicks that count to see those rows with their reasons. query_search_results / query_agent_people leave it out the same way unless you ask for rejected rows. A row recorded without a verdict keeps whatever verdict it already has. {created, updated, total, created_identifiers} — created_identifiers are the canonical identifiers of the rows this call added (not ones it merged into).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultsYesThe people/company rows to upsert; each SearchResult carries its identifier, display_name, source, any signals/columns to store, and optionally its verdict and verdict_reason.
agent_idYesThe agent these rows belong to.
list_nameYesshort kebab slug naming the bucket these rows belong to (e.g. 'oil-gas-operators', 'fintech', 'companies'). Required. Distinct slugs are separate lists on the agent's People and Companies tabs; reuse the same slug across calls to accumulate into one list. For uncategorized runs, pick a single descriptive slug (e.g. the entity_type plural — 'companies' / 'people') and use it consistently.
entity_typeNoWhether `results` are companies or people ('company' by default).company
on_behalf_ofNoAn agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed3 schema fields changed
    • changedInput schema / properties / entity_type / enum
      Previous value: -[
      -  "company",
      -  "person"
      -]New value: +[
      +  "person",
      +  "company"
      +]
    • addedInput schema / properties / entity_type / title
      Added value: +"EntityType"
    • addedInput schema / properties / on_behalf_of
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
      +}
  2. Changed2 schema fields changed
    • changedInput schema / properties / results / items / properties / source / description
      Previous value: -"Where this row came from: the discovery tool that surfaced this candidate — 'theirstack' (theirstack_search), 'theirstack_tech_stack' (find_companies_by_tech_stack), 'tavily' (tavily_search), 'predictleads' (search_news_events), 'exa_news' (exa_search_news), 'google_news' (google_news_search), 'apollo' (apollo_search), 'exa_find_people', 'search_linkedin_people', 'post_engagers' (fetch_post_engagers, query_linkedin_post_engagements), 'luma_guests' (get_luma_guests). Use 'agent' for a row no discovery tool surfaced, such as a pasted list or a CRM read. Recorded only when the row is first created; an existing row keeps its original source."New value: +"Where this row came from: the discovery tool that surfaced this candidate — 'theirstack' (theirstack_search), 'theirstack_tech_stack' (find_companies_by_tech_stack), 'tavily' (tavily_search), 'predictleads' (search_news_events), 'exa_news' (exa_search_news), 'google_news' (google_news_search), 'apollo' (apollo_search), 'exa_find_people', 'search_linkedin_people', 'post_engagers' (fetch_post_engagers, query_linkedin_post_engagements), 'post_authors' (a LinkedIn monitor's author capture), 'luma_guests' (get_luma_guests). Use 'agent' for a row no discovery tool surfaced, such as a pasted list or a CRM read. Recorded only when the row is first created; an existing row keeps its original source."
    • changedInput schema / properties / results / items / properties / source / enum
      Previous value: -[
      -  "agent",
      -  "exa_find_people",
      -  "search_linkedin_people",
      -  "theirstack",
      -  "theirstack_tech_stack",
      -  "luma_guests",
      -  "post_engagers",
      -  "exa_websets",
      -  "warm_intro",
      -  "tavily",
      -  "predictleads",
      -  "exa_news",
      -  "google_news",
      -  "apollo"
      -]New value: +[
      +  "agent",
      +  "exa_find_people",
      +  "search_linkedin_people",
      +  "theirstack",
      +  "theirstack_tech_stack",
      +  "luma_guests",
      +  "post_engagers",
      +  "post_authors",
      +  "exa_websets",
      +  "warm_intro",
      +  "tavily",
      +  "predictleads",
      +  "exa_news",
      +  "google_news",
      +  "apollo"
      +]
  3. Changed2 schema fields changed
    • changedInput schema / properties / results / items / properties / verdict / anyOf
      Previous value: -[
      -  {
      -    "description": "The agent's curation call on an `agent_search_results` row. NULL (no verdict) means\nunreviewed; the default task views hide only `REJECTED` rows.",
      -    "enum": [
      -      "qualified",
      -      "rejected"
      -    ],
      -    "title": "SearchResultVerdict",
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "description": "The agent's curation call on an `agent_search_results` row. NULL (no verdict) means not yet\nscreened; `UNCLEAR` means screened but too thin to judge. The default task views hide only\n`REJECTED` rows.",
      +    "enum": [
      +      "qualified",
      +      "rejected",
      +      "unclear"
      +    ],
      +    "title": "SearchResultVerdict",
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / properties / results / items / properties / verdict / description
      Previous value: -"Your judgment of this row after screening it: 'qualified' keeps it on the list; 'rejected' keeps it saved but out of the list the user sees by default, and they see it, with `verdict_reason`, when they filter the table on Verdict. Omit it to leave the row's current verdict as it is."New value: +"Your judgment of this row after screening it: 'qualified' keeps it on the list; 'rejected' keeps it saved but out of the list the user sees by default, and they see it, with `verdict_reason`, when they filter the table on Verdict; 'unclear' keeps it on the list, marked as one whose details were too thin to judge. Omit it to leave the row's current verdict as it is."
  4. Changed2 schema fields changed
    • changedInput schema / properties / results / items / properties / source / description
      Previous value: -"Where this row came from: the discovery tool that surfaced this candidate — 'theirstack' (theirstack_search, find_companies_by_tech_stack), 'tavily' (tavily_search), 'predictleads' (search_news_events), 'exa_news' (exa_search_news), 'google_news' (google_news_search), 'apollo' (apollo_search), 'exa_find_people', 'search_linkedin_people', 'post_engagers' (fetch_post_engagers, query_linkedin_post_engagements), 'luma_guests' (get_luma_guests). Use 'agent' for a row no discovery tool surfaced, such as a pasted list or a CRM read. Recorded only when the row is first created; an existing row keeps its original source."New value: +"Where this row came from: the discovery tool that surfaced this candidate — 'theirstack' (theirstack_search), 'theirstack_tech_stack' (find_companies_by_tech_stack), 'tavily' (tavily_search), 'predictleads' (search_news_events), 'exa_news' (exa_search_news), 'google_news' (google_news_search), 'apollo' (apollo_search), 'exa_find_people', 'search_linkedin_people', 'post_engagers' (fetch_post_engagers, query_linkedin_post_engagements), 'luma_guests' (get_luma_guests). Use 'agent' for a row no discovery tool surfaced, such as a pasted list or a CRM read. Recorded only when the row is first created; an existing row keeps its original source."
    • changedInput schema / properties / results / items / properties / source / enum
      Previous value: -[
      -  "agent",
      -  "exa_find_people",
      -  "search_linkedin_people",
      -  "theirstack",
      -  "luma_guests",
      -  "post_engagers",
      -  "exa_websets",
      -  "warm_intro",
      -  "tavily",
      -  "predictleads",
      -  "exa_news",
      -  "google_news",
      -  "apollo"
      -]New value: +[
      +  "agent",
      +  "exa_find_people",
      +  "search_linkedin_people",
      +  "theirstack",
      +  "theirstack_tech_stack",
      +  "luma_guests",
      +  "post_engagers",
      +  "exa_websets",
      +  "warm_intro",
      +  "tavily",
      +  "predictleads",
      +  "exa_news",
      +  "google_news",
      +  "apollo"
      +]
  5. Changed4 schema fields changed
    • changedInput schema / properties / list_name / description
      Previous value: -"short kebab slug naming the bucket these rows belong to\n(e.g. 'oil-gas-operators', 'fintech', 'companies'). Required.\nDistinct slugs render as separate sub-pills in the Output tab; reuse\nthe same slug across calls to accumulate into one list. For\nuncategorized runs, pick a single descriptive slug (e.g. the\nentity_type plural — 'companies' / 'people') and use it consistently."New value: +"short kebab slug naming the bucket these rows belong to\n(e.g. 'oil-gas-operators', 'fintech', 'companies'). Required.\nDistinct slugs are separate lists on the agent's People and Companies\ntabs; reuse the same slug across calls to accumulate into one list. For\nuncategorized runs, pick a single descriptive slug (e.g. the\nentity_type plural — 'companies' / 'people') and use it consistently."
    • changedInput schema / properties / results / description
      Previous value: -"The people/company rows to upsert; each SearchResult carries its\nidentifier, display_name, source, and any signals/columns to store."New value: +"The people/company rows to upsert; each SearchResult carries its\nidentifier, display_name, source, any signals/columns to store, and\noptionally its verdict and verdict_reason."
    • addedInput schema / properties / results / items / properties / verdict
      Added value: +{
      +  "anyOf": [
      +    {
      +      "description": "The agent's curation call on an `agent_search_results` row. NULL (no verdict) means\nunreviewed; the default task views hide only `REJECTED` rows.",
      +      "enum": [
      +        "qualified",
      +        "rejected"
      +      ],
      +      "title": "SearchResultVerdict",
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Your judgment of this row after screening it: 'qualified' keeps it on the list; 'rejected' keeps it saved but out of the list the user sees by default, and they see it, with `verdict_reason`, when they filter the table on Verdict. Omit it to leave the row's current verdict as it is."
      +}
    • addedInput schema / properties / results / items / properties / verdict_reason
      Added value: +{
      +  "default": "",
      +  "description": "Why, in one short sentence the user reads in the row's Verdict column (e.g. 'HQ in Toronto — outside the US location filter'). Required with 'rejected'; set it only with a verdict.",
      +  "type": "string"
      +}
  6. Changed7 schema fields changed
    • addedInput schema / properties / agent_id
      Added value: +{
      +  "description": "The agent these rows belong to.",
      +  "type": "integer"
      +}
    • changedInput schema / properties / results / description
      Previous value: -"The people/company rows to upsert; each SearchResult carries its\nidentifier, display_name, and any signals/columns to store."New value: +"The people/company rows to upsert; each SearchResult carries its\nidentifier, display_name, source, and any signals/columns to store."
    • changedInput schema / properties / results / items / properties / data / properties / network_distance / anyOf
      Previous value: -[
      -  {
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "description": "A person's LinkedIn degree to the user, closest first.",
      +    "enum": [
      +      "1",
      +      "2",
      +      "3",
      +      "out_of_network"
      +    ],
      +    "title": "Degree",
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / properties / results / items / properties / identifier / description
      Previous value: -"Company domain or canonical URL — unique within the task."New value: +"Company domain or canonical URL — unique within the agent."
    • addedInput schema / properties / results / items / properties / source
      Added value: +{
      +  "default": "agent",
      +  "description": "Where this row came from: the discovery tool that surfaced this candidate — 'theirstack' (theirstack_search, find_companies_by_tech_stack), 'tavily' (tavily_search), 'predictleads' (search_news_events), 'exa_news' (exa_search_news), 'google_news' (google_news_search), 'apollo' (apollo_search), 'exa_find_people', 'search_linkedin_people', 'post_engagers' (fetch_post_engagers, query_linkedin_post_engagements), 'luma_guests' (get_luma_guests). Use 'agent' for a row no discovery tool surfaced, such as a pasted list or a CRM read. Recorded only when the row is first created; an existing row keeps its original source.",
      +  "enum": [
      +    "agent",
      +    "exa_find_people",
      +    "search_linkedin_people",
      +    "theirstack",
      +    "luma_guests",
      +    "post_engagers",
      +    "exa_websets",
      +    "warm_intro",
      +    "tavily",
      +    "predictleads",
      +    "exa_news",
      +    "google_news",
      +    "apollo"
      +  ],
      +  "type": "string"
      +}
    • removedInput schema / properties / task_id
      Removed value: -{
      -  "description": "The task these rows belong to.",
      -  "type": "integer"
      -}
    • changedInput schema / required
      Previous value: -[
      -  "task_id",
      -  "results",
      -  "list_name"
      -]New value: +[
      +  "agent_id",
      +  "results",
      +  "list_name"
      +]
  7. Changed1 schema field changed
    • addedInput schema / properties / results / items / properties / data / properties / network_distance
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "integer"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null
      +}
  8. First observed

TDQS

A4.7/5.0
Behavior5/5

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

Annotations (readOnlyHint=false, destructiveHint=false) only establish that this is a non-destructive write. The description goes far beyond: dedup key (agent, identifier, list_name), signals union-by-URL vs. last-writer-wins, the asymmetric empty-list semantics (signals: [] preserves, evaluations: [] replaces), canonical identifier normalization (eTLD+1, LinkedIn slug), and that source/verdict are recorded only under specified conditions. This is precisely the behavioral context annotations cannot carry.

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

Conciseness3/5

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

Front-loaded correctly with the core purpose and sibling routing, but the body is very long and repeats the verdict/rejected-row mechanics across multiple paragraphs (rejected rows dropping out of the default list, how counts display, how query tools hide them). For a complex tool most of it earns its place, yet there is real redundancy that could be tightened.

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 5-parameter upsert tool with nested data payloads and no output schema in the structured fields, the description covers merge/union semantics, canonicalization, verdict lifecycle, and source attribution, and even includes a returns section documenting `{created, updated, total, created_identifiers}`. Nothing an agent needs to call it correctly is missing.

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, but the description adds genuinely non-schema semantics: the canonical identifier forms to match when querying, why every display attribute must go in `columns` (key-union merge, otherwise unsurfaced), and how list_name reuse accumulates vs. creates a new list. It stops short of 5 only because several of these points are also richly restated in the schema descriptions.

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?

Opens with a specific verb+resource ('Upsert people/company rows into the agent's entity row store (`agent_search_results`)') and immediately scopes it to rows the agent assembled itself (pasted roster, CRM read, resolved search results). It then explicitly separates itself from the discovery tools by name, so an agent can route correctly without opening any schema.

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?

Names the alternatives (exa_find_people, search_linkedin_people, fetch_post_engagers, theirstack_search, etc.) and states the exact condition for choosing this tool over them: only to filter their rows, add columns/signals/evaluations, or record a verdict. The when-not is also implied clearly ('Leave it at agent only for rows no discovery tool surfaced').

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.

Resources