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). On an existing row, a field you leave out keeps its stored value. In columns a blank you send is written as sent; anywhere else, including inside person, a blank ('' or null) keeps the stored value too. data['signals'] accumulates across runs (union by URL), and any other value you send replaces the stored one. 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 filtering identifier via query_search_results; a person row whose linkedin_slug is set is filterable by that slug, whatever its source.

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, list_people and list_companies 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).

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "agent_id": "<agent_id>",
      "results": "<results>",
      "list_name": "<list_name>"
    },
    "name": "record_search_results"
  }
}

Input 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_ofNoA teammate's email, or an agency client's from list_agency_clients, to run this call in that 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. Changed1 schema field changed
    • changedInput schema / properties / on_behalf_of / description
      Previous value: -"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."New value: +"A teammate's email, or an agency client's from list_agency_clients, to run this call in that account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
  2. Changed1 schema field changed
    • addedInput schema / properties / results / items / properties / data / properties / provider_id
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "A person row's LinkedIn provider id, copied exactly as a tool returned it (such as search_linkedin_people). A username slug or profile URL goes in identifier."
      +}
  3. 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."
      +}
  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), '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"
      +]
  5. 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."
  6. 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"
      +]
  7. 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"
      +}
  8. 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"
      +]
  9. Changed1 schema field changed
    • addedInput schema / properties / results / items / properties / data / properties / network_distance
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "integer"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null
      +}
  10. First observed

TDQS

A4.2/5.0
Behavior3/5

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

Annotations declare this is a non-read-only, non-destructive mutation, and the description goes well past that: dedup key (agent, identifier, list_name), per-field shallow merge, blank-value handling, signal accumulation by URL union, and the distinction that signals:[] preserves while evaluations:[] replaces. What is missing is permission/auth or credit implications and any indication of what happens on malformed input, which leaves this a solid but not complete behavioral disclosure for a mutation tool.

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 and split into readable paragraphs with the critical dedup and merge contract early, but it runs far longer than needed: the verdict/dark-list behavior is explained twice (once in the Websets paragraph, again in "To mark a row already saved"), and the columns instruction recurs in both prose and schema description. Dense and information-rich, yet several sentences restate each other.

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?

For a five-parameter mutation tool with no output schema it covers the essentials: merge semantics, canonical identifier forms, verdict lifecycle, and the returns block naming {created, updated, total, created_identifiers}. The only gap is that the summary blurbs are extensive while entity_type and on_behalf_of get no descriptive attention at all.

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, and the description earns more by explaining semantics the schema cannot: columns merges by key-union and is the only place user-visible display attributes surface, source should be set per row to the discovering tool, and list_name reuse is what accumulates rows into one list. It stops short of explaining entity_type and on_behalf_of, which the schema covers only briefly.

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 concrete verb-plus-resource statement ("Upsert people/company rows into the agent's entity row store") and immediately delimits it against the discovery tools that persist their own matches. An agent can tell from the first sentence whether this is the right tool for a pasted roster versus a Tavily result set.

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?

Explicitly names when to use it (a user-pasted roster, a CRM read, web-search results you resolved) and when not to (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 rows). It even specifies the correct mode for the sibling case: re-record their rows under the same identifier and list_name to add columns, signals, or a verdict.

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