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. A fetch costs 0.04 credits per reaction, comment or reply it pulls, people already stored included, so re-fetching a post bills everyone on it again; data fetched within the last 6 hours is served from the database free. Loading a post Sliq hasn't tracked yet counts one against the user's daily LinkedIn post-read budget. In a conversation, tell the user the cost and the wait before calling this: most posts cost under 5 credits, one fetch's reactions and comments cost at most 40 plus 0.04 per reply, and a post with hundreds of engagers takes around half a minute.

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, linkedin_provider_id and linkedin_slug the LinkedIn handles, 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 queue_linkedin_outreach call passing each row's linkedin_provider_id as provider_id and its linkedin_slug as linkedin_slug. 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, 'credits_charged': number, 'budget': {'used_today': int, 'limit': int}, 'engagers_preview': [first 10 engagers, unchecked], 'next_step': str, 'list'?: {'list_name', 'created', 'updated', 'total', 'rejected'}}. comments_stored counts replies too. 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. budget is today's LinkedIn post-read budget. On failure, {'success': False, 'error': str} when the post isn't visible to the user's account, the daily LinkedIn post-read budget is spent, or LinkedIn actions are paused after a rate limit. Raises when the user's credits can't cover a fetch.

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": {
      "post_url": "<post_url>",
      "criteria": "<criteria>"
    },
    "name": "fetch_post_engagers"
  }
}

Input 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 in any form, including a lnkd.in share link, 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_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
    • changedInput schema / properties / post_url / description
      Previous value: -"LinkedIn post URL (activity, ugcPost, and share forms all\nwork) or a raw activity ID."New value: +"LinkedIn post URL in any form, including a lnkd.in share\nlink, or a raw activity ID."
  3. 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."
      +}
  4. 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"
      +]
  5. 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."
  6. 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."
  7. 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."
  8. 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."
      -}
  9. 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."
  10. First observed

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only say readOnlyHint=false and destructiveHint=false; the description goes far beyond, disclosing per-item credit pricing (0.04), a 6-hour free cache, a 500+500 cap with a truncated flag, the daily post-read budget, the ~20s/100-person criteria check cost, and that re-runs update rather than duplicate rows. It also documents failure modes (private/deleted post, budget spent, paused after rate limit, raises when credits are insufficient) and instructs the agent to warn the user about cost and wait time.

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 a one-line summary, then when-to-use, then costs, then agent-side behavior and next steps — a sensible ordering. It is long, but the density is mostly justified by a genuinely complex, billable, side-effectful tool; only a few sentences (e.g. the repeated cost restatement) could be trimmed.

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-param, credit-charging, writing tool with no formal output schema, the description supplies the full return dict semantics (including the subtle difference between unique_engagers and list.total, and comments_stored counting replies) plus failure shapes and the required follow-up call sequence. Nothing an agent needs to invoke or chain this 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 baseline is 3. The description earns above that by adding behavioral meaning the schema doesn't: criteria is applied person-by-person with verdicts persisted and rejected people hidden, broad criteria keep everyone, a new list_name is needed to re-screen, and existing members keep their old verdict.

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 — and immediately scopes it ('the user's own post or anyone else's') plus the side effect (store as queryable engagement rows). An agent can tell this apart from query_linkedin_post_engagements and query_search_results, which the description explicitly positions as downstream readers.

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 an explicit trigger ('Use when the user wants to turn a post's engagers into leads') with concrete utterance examples. It also names alternatives and when to pick them: query_search_results in next_step to read saved people, query_linkedin_post_engagements for the full unfiltered set, and a fresh list_name when re-screening against new criteria.

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