Skip to main content
Glama

Who Is Already In The Pipeline (pre-import check)

li_check_targets
Read-only

Read-only: who among these profile URLs has ALREADY been approached from the accounts you can see. It writes nothing, queues nothing and BLOCKS nothing — a collision is information, not an error, and importing one is allowed.

WHEN NOT TO CALL IT: not before an import. li_ingest_prospects and li_create_campaign run this very check themselves, just before they write, and hand the report straight back as collisions — so calling this first does the same work twice and returns the same answer twice. For li_ingest_prospects theirs is also the BETTER answer: it knows which campaign the batch is landing in, so people already in it read as in_this_campaign, an idempotent update that costs nothing, while a call made out here without a campaignId files those same people as a clash somewhere else. (li_create_campaign CREATES the campaign, so there is nothing to be already in — its report is the same one you would get here, minus the extra call.) Reach for this when you have NOT decided to import — triaging a list somebody just sent you, deciding whether a batch is worth paying to research before you spend lookups on it, or answering "do we already know these people" as a question of its own.

Why it matters: re-importing someone into the SAME campaign is idempotent (their note and unsent step text are refreshed, nothing new is queued). Importing them into a DIFFERENT campaign creates a second invite the drip will attempt, and what happens is decided at send time — LinkedIn showing a pending invite yields "already invited" and someone already 1st-degree yields "already connected", neither of which sends anything, but BOTH still spend a ~50s browser session that this account then has to wait behind. Anything else sends a REAL second invite, charged against each account's rolling 7-day invite allowance this system enforces, set by the account's detected LinkedIn tier — 63-70 a week (5-10 a day) new or unverified, 90-100 a week (18-24 a day) established, 135-150 a week (25-30 a day) Premium, redrawn within the range each week. Our own policy, not a published LinkedIn limit; the daily band exists only to stop a burst. li_list_accounts gives each account's actual numbers.

Each person lands in exactly one bucket, most advanced first: in_this_campaign (benign — an idempotent update), messaged_elsewhere, connected_elsewhere, invited_elsewhere, unreachable_elsewhere (a PERMANENT invite failure is on record, so retrying spends a session for nothing), closed_elsewhere, and queued_elsewhere (another campaign has them queued and has NOT invited them yet — the only collision still cheap to avoid, since both campaigns will now invite them and the second is a duplicate out of the same daily budget). collisions names each one with its campaign and account, counts totals the buckets, fresh is how many none of your accounts has touched, and summary is one true sentence safe to relay verbatim.

THE BUCKET IS ONLY THE HEADLINE. A person can be in several campaigns on several accounts: otherCampaigns counts the rest per person, and view: "people" lists EVERY membership of every person (campaign, account, campaign status, connection, message, outcome, inbound) as one compact table — up to 1000 URLs in one call, so it is also the way to ask which of a list are in which campaigns. Building several new campaigns at once? Pass campaignNamePrefix (or campaignId): anyone already in one of them is named in batch.members whatever their bucket says.

Set groupByCompany: true to also group these people by employer and say which firms you already have a contact at. Read a warm firm BOTH ways: somebody there already knows you, or the same note is about to land twice inside one company. Only you can tell which, so this reports it and holds nothing. Employers come from profiles already looked up plus your own contact rows, so unknownCompany means "not matched", never "no employer".

The report is BOUNDED BY WHAT YOU CAN SEE: it covers your own accounts and reports nothing at all — no name, no count, no bucket — about anything outside them, so read a clean result as "not in your pipeline" rather than "nobody has ever approached them". scopeNote says the same in the reply. Pass campaignId when you already know the campaign you are importing into; without it, people who are already IN that campaign are reported as collisions elsewhere, which overstates the cost.

The reply is GROUPED by default (view: "summary"): the explanation is said ONCE per bucket instead of once per person, each campaign is described once in sites instead of on every row, and every person named keeps their URL, their name and a site index pointing at their own campaign and account — measured at 56% smaller on a batch with sixty collisions, and the saving grows with the batch. ⚠️ IT IS CAPPED, so read peopleHidden: a bucket names at most 50 people and states how many it did not, while count stays the true bucket size — sixty in one bucket gives you fifty names and peopleHidden: 10, and those ten are NOT fresh. view: "full" names every one of them, in the order you submitted them, each carrying its own copy of the sentence.

Input Schema

NameRequiredDescriptionDefault
viewNoHow much to return. "summary" (the default) groups the colliding people by bucket and lists each campaign once in `sites`, naming up to 50 people per bucket (`peopleHidden` counts the rest) and `otherCampaigns` per person. "people" is EVERY campaign each person is in, one table row per person per campaign (account, campaign status, connection, message, outcome, inbound), uncapped and compact — for "which of these URLs are in which campaigns". "full" returns flat rows with every sentence, plus `people` with whole membership objects.
fieldsNoview "people" only: which columns besides profile and campaign, from name, bucket, connection, message, outcome, inbound. Default all.
formatNoview "people" only: "json" (default) returns `rows` as arrays matching `columns`; "csv" returns the same table as `csv` text.
sessionIdNoAccount you intend to import from (id)
campaignIdNoThe campaign you are importing INTO, when it already exists. People already in it are then reported as `in_this_campaign` (an idempotent update) instead of as a collision elsewhere.
profileUrlsYesLinkedIn profile URLs to check, max 1000 per call. A longer list is REFUSED rather than silently trimmed — split it and call again.
sessionLabelNoAccount you intend to import from, e.g. "Deven". Validated only — naming it does NOT narrow the check, because a collision on another of your accounts is still a collision and hiding it would defeat the point.
groupByCompanyNoAlso group these people by employer and name the firms you already have a contact at. Off by default because it costs extra queries. Nothing is held either way.
campaignNamePrefixNoThe campaigns you are BUILDING, by name prefix (case-insensitive), e.g. "PR test Oct". Anyone already in one of them is listed in `batch.members` (never capped) and marked `inBatch`, whatever their bucket says. Use it when splitting a list across several new campaigns.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed5 schema fields changed
    • addedInput schema / properties / campaignNamePrefix
      Added value: +{
      +  "description": "The campaigns you are BUILDING, by name prefix (case-insensitive), e.g. \"PR test Oct\". Anyone already in one of them is listed in `batch.members` (never capped) and marked `inBatch`, whatever their bucket says. Use it when splitting a list across several new campaigns.",
      +  "maxLength": 200,
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / fields
      Added value: +{
      +  "description": "view \"people\" only: which columns besides profile and campaign, from name, bucket, connection, message, outcome, inbound. Default all.",
      +  "items": {
      +    "enum": [
      +      "name",
      +      "bucket",
      +      "connection",
      +      "message",
      +      "outcome",
      +      "inbound"
      +    ],
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
    • addedInput schema / properties / format
      Added value: +{
      +  "description": "view \"people\" only: \"json\" (default) returns `rows` as arrays matching `columns`; \"csv\" returns the same table as `csv` text.",
      +  "enum": [
      +    "json",
      +    "csv"
      +  ],
      +  "type": "string"
      +}
    • changedInput schema / properties / view / description
      Previous value: -"How much to return. \"summary\" (the default) groups the colliding people by bucket and lists each campaign once in `sites`, naming up to 50 people per bucket and saying in `peopleHidden` how many it did not. \"full\" returns one flat row per person, each carrying its own copy of the same sentence and campaign name, in submission order."New value: +"How much to return. \"summary\" (the default) groups the colliding people by bucket and lists each campaign once in `sites`, naming up to 50 people per bucket (`peopleHidden` counts the rest) and `otherCampaigns` per person. \"people\" is EVERY campaign each person is in, one table row per person per campaign (account, campaign status, connection, message, outcome, inbound), uncapped and compact — for \"which of these URLs are in which campaigns\". \"full\" returns flat rows with every sentence, plus `people` with whole membership objects."
    • changedInput schema / properties / view / enum
      Previous value: -[
      -  "summary",
      -  "full"
      -]New value: +[
      +  "summary",
      +  "full",
      +  "people"
      +]
  2. First observed

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already declare read-only and non-destructive, but the description goes far beyond them: it states it writes nothing, queues nothing and blocks nothing, documents collision-as-information semantics, invite-allowance costs, the bounded-by-visibility scope, result capping via peopleHidden, and default grouping. Rich context that annotations alone could never provide.

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?

Strongly front-loaded (read-only guarantee first, then exclusions, then positive cases) and organized into clear sections. It is very long and includes illustrative tangents (56% size reduction, ~50s browser sessions, tier allowance bands) that are informative but not all essential to invocation.

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?

With no output schema, the description fully carries the burden of explaining returns: it details every bucket, collisions/counts/fresh/summary, the view modes, the people table, otherCampaigns, batch.members, and the peopleHidden cap. Nothing an agent needs to interpret the reply 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 schema already documents each parameter; the baseline would be 3. The description adds genuine meaning on top, notably the consequence of passing campaignId (idempotent in_this_campaign vs overstated collisions without it), that groupByCompany costs extra queries, and how campaignNamePrefix marks batch members.

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 ('Read-only: who among these profile URLs has ALREADY been approached from the accounts you can see') and immediately distinguishes scope from siblings by naming li_ingest_prospects and li_create_campaign. An agent can tell exactly what this returns without opening anything else.

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?

Contains an explicit 'WHEN NOT TO CALL IT' section that names the alternatives (li_ingest_prospects, li_create_campaign), explains why they already do this check and why theirs is the better answer, and then states the positive cases (triaging a list, deciding whether a batch is worth researching). This is textbook when/when-not/alternatives guidance.

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