Skip to main content
Glama

Fetch Post Engagers

fetch_post_engagers

Visibility follows the user's LinkedIn account: any post it can view works, and private or deleted posts return a plain error. The list is capped at 500 reactions + 500 comments per post; on a bigger post the return flags the cut-off. Data fetched within the last 6 hours is served from the database at no cost; a real fetch counts one action against the shared daily engagement-fetch budget and paces its LinkedIn requests, so a post with hundreds of engagers takes around half a minute — set that expectation with the user before calling this on a big post.

When this runs in an agent, every person who engaged is saved as an agent_search_results person row, rendered in the agent's Output tab and queryable via query_search_results: data.person carries the name and headline, data.provider_id the LinkedIn id, and data.columns the engagement (Engagement, Comment, Post, Post URL). Company pages that reacted or commented aren't people, and the user isn't a lead, so neither is saved; both stay in the engagement rows. Re-running updates existing rows rather than duplicating them. Pass list_name to name their list; absent, they land in the 'default' list.

Each person new to the list is checked against criteria, one at a time, reading their headline and engagement, and saved with the verdict; the rejected ones are hidden from the list's default view and from query_search_results. The check adds about 20 seconds per hundred people. To keep everyone who engaged, write broad criteria, such as "Anyone who engaged with this post". A person already on the list keeps their verdict, so to screen a post's engagers against new criteria, pass a new list_name.

After this returns, read the saved people with the query_search_results call in next_step, which is scoped to this post: one list can hold several posts' engagers. Judge anyone the check kept as unclear or left without a verdict yourself, then queue outreach for the people who fit with one batched setup_linkedin_sequence call passing each person's data.provider_id. Send-time resolution skips anyone already connected, so they don't need pre-filtering here. Every engager, the rejected ones included, also stays queryable with query_linkedin_post_engagements using post_id = <post_analytics_id>. On success, a dict {'success': True, 'post_analytics_id': int, 'author_name': str, 'is_own_post': bool, 'post_text_snippet': str, 'from_cache': bool, 'reactions_stored': int, 'comments_stored': int, 'unique_engagers': int, 'truncated': bool, 'budget': {'used_today': int, 'limit': int}, 'engagers_preview': [first 10 engagers, unchecked], 'next_step': str, 'list'?: {'list_name', 'created', 'updated', 'total', 'rejected'}}. unique_engagers counts company pages too; list.total counts only the people saved, rejected ones included, so it can be lower. list.created and list.updated count the people the list shows, and list.rejected the ones the check turned down. On failure, {'success': False, 'error': str} when the post isn't visible to the user's account, the daily budget is exhausted, or LinkedIn actions are paused after a rate limit.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
agent_idNoOptional — a specific agent to materialize the engager list into. Omit it to use the running agent, which is the usual case.
criteriaYesWho counts as a match, in plain words (e.g. "Heads of Sales at B2B software companies; not recruiters").
post_urlYesLinkedIn post URL (activity, ugcPost, and share forms all work) or a raw activity ID.
list_nameNoShort kebab slug naming the list bucket (e.g. 'launch-post-engagers'). Absent, engagers land in the 'default' list.
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. Changed1 schema field changed
    • 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. Changed7 schema fields changed
    • removedInput schema / properties / criteria / anyOf
      Removed value: -[
      -  {
      -    "minLength": 1,
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedInput schema / properties / criteria / default
      Removed value: -null
    • changedInput schema / properties / criteria / description
      Previous value: -"Optional — who counts as a match, in plain words (e.g.\n\"Heads of Sales at B2B software companies; not recruiters\"). Each\nperson saved is checked against it. Omit it to save everyone who\nengaged."New value: +"Who counts as a match, in plain words (e.g. \"Heads of Sales\nat B2B software companies; not recruiters\")."
    • addedInput schema / properties / criteria / minLength
      Added value: +1
    • addedInput schema / properties / criteria / type
      Added value: +"string"
    • removedInput schema / properties / persist
      Removed value: -{
      -  "default": true,
      -  "description": "Default True — in an agent, every person who engaged is saved as\na person row automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written and nobody is checked.",
      -  "type": "boolean"
      -}
    • changedInput schema / required
      Previous value: -[
      -  "post_url"
      -]New value: +[
      +  "post_url",
      +  "criteria"
      +]
  3. Changed2 schema fields changed
    • addedInput schema / properties / criteria
      Added value: +{
      +  "anyOf": [
      +    {
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Optional — who counts as a match, in plain words (e.g.\n\"Heads of Sales at B2B software companies; not recruiters\"). Each\nperson saved is checked against it. Omit it to save everyone who\nengaged."
      +}
    • changedInput schema / properties / persist / description
      Previous value: -"Default True — in an agent, every person who engaged is saved as\na person row automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written. To filter the saved engagers,\nrecord a `verdict` on them with `record_search_results` instead."New value: +"Default True — in an agent, every person who engaged is saved as\na person row automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written and nobody is checked."
  4. Changed1 schema field changed
    • changedInput schema / properties / persist / description
      Previous value: -"Default True — in an agent, every engager is saved as a person\nrow automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written. To filter the saved engagers,\nrecord a `verdict` on them with `record_search_results` instead."New value: +"Default True — in an agent, every person who engaged is saved as\na person row automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written. To filter the saved engagers,\nrecord a `verdict` on them with `record_search_results` instead."
  5. Changed1 schema field changed
    • changedInput schema / properties / persist / description
      Previous value: -"Default True — in an agent, every engager is saved as a person\nrow automatically (see `list_name`). Pass False to fetch the\nengagers without saving them as person rows: the raw engagement rows\nstill land and stay queryable via `query_linkedin_post_engagements`,\nbut no `agent_search_results` list is written — for a flow that\nrecords only a filtered subset itself, so the full unfiltered list\nwouldn't also clutter the Output tab."New value: +"Default True — in an agent, every engager is saved as a person\nrow automatically (see `list_name`). Pass False only for a read-only\nlookup the user doesn't want on a list: the raw engagement rows still\nland and stay queryable via `query_linkedin_post_engagements`, but no\n`agent_search_results` list is written. To filter the saved engagers,\nrecord a `verdict` on them with `record_search_results` instead."
  6. Changed3 schema fields changed
    • addedInput schema / properties / agent_id
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "integer"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Optional — a specific agent to materialize the engager list\ninto. Omit it to use the running agent, which is the usual case."
      +}
    • changedInput schema / properties / persist / description
      Previous value: -"Default True — in a task, every engager is saved as a person\nrow automatically (see `list_name`). Pass False to fetch the\nengagers without saving them as person rows: the raw engagement rows\nstill land and stay queryable via `query_linkedin_post_engagements`,\nbut no `agent_search_results` list is written — for a flow that\nrecords only a filtered subset itself, so the full unfiltered list\nwouldn't also clutter the Output tab."New value: +"Default True — in an agent, every engager is saved as a person\nrow automatically (see `list_name`). Pass False to fetch the\nengagers without saving them as person rows: the raw engagement rows\nstill land and stay queryable via `query_linkedin_post_engagements`,\nbut no `agent_search_results` list is written — for a flow that\nrecords only a filtered subset itself, so the full unfiltered list\nwouldn't also clutter the Output tab."
    • removedInput schema / properties / task_id
      Removed value: -{
      -  "anyOf": [
      -    {
      -      "type": "integer"
      -    },
      -    {
      -      "type": "null"
      -    }
      -  ],
      -  "default": null,
      -  "description": "Optional — a specific task to materialize the engager list\ninto. Omit it to use the running task, which is the usual case."
      -}
  7. Changed3 schema fields changed
    • changedInput schema / properties / list_name / description
      Previous value: -"Short kebab slug naming the list bucket (e.g.\n'launch-post-engagers'). Pass together with `task_id`."New value: +"Short kebab slug naming the list bucket (e.g.\n'launch-post-engagers'). Absent, engagers land in the 'default' list."
    • addedInput schema / properties / persist
      Added value: +{
      +  "default": true,
      +  "description": "Default True — in a task, every engager is saved as a person\nrow automatically (see `list_name`). Pass False to fetch the\nengagers without saving them as person rows: the raw engagement rows\nstill land and stay queryable via `query_linkedin_post_engagements`,\nbut no `agent_search_results` list is written — for a flow that\nrecords only a filtered subset itself, so the full unfiltered list\nwouldn't also clutter the Output tab.",
      +  "type": "boolean"
      +}
    • changedInput schema / properties / task_id / description
      Previous value: -"Pass together with `list_name` to materialize the full\nunfiltered engager list into this task's search results. When\nthe user wants only a subset (ICP fit, founders only, a\nspecific role), omit both — qualify via\nquery_linkedin_post_engagements first, then persist the keepers\nwith record_search_results."New value: +"Optional — a specific task to materialize the engager list\ninto. Omit it to use the running task, which is the usual case."
  8. First observed

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only tell us readOnlyHint=false and destructiveHint=false; the description carries the real behavioral load: 500+500 cap with a truncation flag, 6-hour cache, one-action budget cost, request pacing (~30s for thousands of engagers), re-run dedupe, company pages and the user excluded, and the ~20s-per-100-people criteria check. This is rich, non-redundant disclosure beyond the annotations.

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?

The prose is long but front-loaded under a <summary> tag with the action and when-to-use first, followed by a structured <returns> block. Nearly every sentence carries operational value, though it is denser than strictly necessary and a few clauses (e.g. repeated queryability notes) 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?

Despite no output schema, the embedded <returns> block fully documents the success dict (ids, counts, truncation, budget, preview, next_step, list stats) and the failure shape with named causes. Combined with criteria/list/agency parameter notes, an agent has everything needed to call and interpret this tool.

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 meaningfully extends it: it explains list_name's 'default' fallback and the tactic of passing a new list_name to re-screen against new criteria, plus guidance to write broad criteria to keep everyone. These are usage semantics the schema doesn't state.

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 ('Fetch the people who reacted to or commented on a LinkedIn post ... store them as queryable engagement rows') with clear scope (own or anyone's post). It is easily distinguished from siblings like query_linkedin_post_engagements (read engagers) and query_search_results (read saved people), so an agent can tell fetch-and-store apart from query.

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 says when to use it ('when the user wants to turn a post's engagers into leads', with example utterances), embeds the downstream flow (read via query_search_results next_step, re-query via query_linkedin_post_engagements), and warns to set time expectations before calling on a big post. Failure conditions (private/deleted post, exhausted budget, paused actions) are named.

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