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, evaluations, or people nested under a company — 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.

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 what you filtered out, and the user reviews those rows, with their reasons, by filtering it on Verdict. query_search_results / query_task_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

Schema Changelog

Changes observed during successful MCP inspections.

  1. 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"
      +]
  2. 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"
      +}
  3. 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"
      +]
  4. Changed1 schema field changed
    • addedInput schema / properties / results / items / properties / data / properties / network_distance
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "integer"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null
      +}
  5. First observed

TDQS

A4.8/5.0
Behavior5/5

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

With annotations only declaring write/non-destructive, the description carries the real behavioral burden and does so richly: per-(agent, identifier, list_name) dedup, signals union-by-URL accumulation vs evaluations replacement, last-writer-wins on other fields, unset fields preserved, allowed cross-list duplicates, and canonical identifier storage. It also explains the side effect of a rejected verdict (row stays saved but drops from default views) and that source is recorded only at creation.

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?

It is long, but the tool is genuinely complex and the content is front-loaded: the lead sentence establishes the core operation before the operational detail. Nearly every sentence covers a distinct semantic contract (dedup, empty-lists, columns, source, verdict) rather than restating structure, though some tightening is possible.

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?

Complete for a multi-field upsert tool. It covers write semantics, merge behavior, identifier canonicalization, verdict side effects, and display-column requirements, and it even describes the return payload (`{created, updated, total, created_identifiers}`) despite no output schema existing. 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 description coverage is 100%, so the baseline is 3. The description still adds genuine meaning beyond the schema: the empty-array contract (`signals: []` preserves, `evaluations: []` replaces), the columns key-union and canonical identifier matching, and the per-row source/verdict mechanics. That is real added value over already-complete schema text.

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 precise verb and resource: 'Upsert people/company rows into the agent's entity row store (`agent_search_results`)'. It explicitly separates itself from the discovery tools that auto-save their own matches, so an agent knows this is the persistence/filtering layer, not a discovery tool.

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 when-to-use cases (bulk set you assembled: pasted roster, CRM read, resolved web results) and when to reach for it against discovery output (to add a verdict, or to add columns/signals/evaluations to an already-saved row). Names the sibling discovery tools it is not and the condition each selects on, including the constraint that you must reuse the same identifier and list_name.

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