Skip to main content
Glama

Search LinkedIn message history

search_linkedin_message_history
Read-only

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": {
      "where_clause": "<where_clause>"
    },
    "name": "search_linkedin_message_history"
  }
}

Input Schema

NameRequiredDescriptionDefault
limitNoMaximum number of results (default 50, max 200). Only override if the user says a specific number like "show me 10 messages". For vague quantities ("couple", "few", "recent"), omit this parameter to use the default.
offsetNoRows to skip for paging (default 0). When the result is truncated, re-call with offset += limit to fetch the next page.
order_byNoSQL ORDER BY clause (default: message_datetime DESC).message_datetime DESC
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.
where_clauseYesSQL WHERE clause (without WHERE keyword). Use ONLY these columns: provider, chat_id, sender_name, sender_provider_id, recipient_name, recipient_provider_id, content, message_datetime, reply_outcome (inbound replies only; 'interested' / 'meeting_booked' / 'not_interested' / 'neither' — the reply's classified outcome, NULL if it was never classified; to exclude a value, write reply_outcome IS DISTINCT FROM 'not_interested', which keeps the NULL rows), attachments (JSONB; `attachments != '[]'::jsonb` finds messages with files, and `attachments::text ILIKE '%deck%'` matches a document's file name). To read your thread with a person, match their provider_id alone: `sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`. Keep their name out of that clause: a name ILIKE is a substring match, so it pulls in other people's threads (`'%Ana%Li%'` also matches "Diana Lim"). Match on a name only to find someone whose provider_id you don't have; when a hit's counterpart name is their full name, take it as theirs and re-read by that counterpart's provider_id. An empty result isn't proof of no prior contact: this local store holds only a bounded initial backfill from around when the account was connected, forward. When you need certainty, call `fetch_linkedin_messages_with_person` with their provider_id — it reads LinkedIn's full synced history live.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedInput schema / properties / on_behalf_of / description
      Previous value: -"A teammate's email (one with conversation sharing turned on), 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."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 / where_clause / description
      Previous value: -"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; 'interested' / 'meeting_booked' /\n'not_interested' / 'neither' — the reply's classified outcome, NULL if it\nwas never classified; to exclude a value, write reply_outcome IS DISTINCT\nFROM 'not_interested', which keeps the NULL rows).\nTo read your thread with a person, match their provider_id alone:\n`sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`.\nKeep their name out of that clause: a name ILIKE is a substring\nmatch, so it pulls in other people's threads (`'%Ana%Li%'` also\nmatches \"Diana Lim\"). Match on a name only to find someone whose\nprovider_id you don't have; when a hit's counterpart name is their\nfull name, take it as theirs and re-read by that counterpart's\nprovider_id. An empty result isn't proof of no prior\ncontact: this local store holds only a bounded initial backfill\nfrom around when the account was connected, forward. When you need\ncertainty, call `fetch_linkedin_messages_with_person` with their\nprovider_id — it reads LinkedIn's full synced history live."New value: +"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; 'interested' / 'meeting_booked' /\n'not_interested' / 'neither' — the reply's classified outcome, NULL if it\nwas never classified; to exclude a value, write reply_outcome IS DISTINCT\nFROM 'not_interested', which keeps the NULL rows), attachments (JSONB;\n`attachments != '[]'::jsonb` finds messages with files, and\n`attachments::text ILIKE '%deck%'` matches a document's file name).\nTo read your thread with a person, match their provider_id alone:\n`sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`.\nKeep their name out of that clause: a name ILIKE is a substring\nmatch, so it pulls in other people's threads (`'%Ana%Li%'` also\nmatches \"Diana Lim\"). Match on a name only to find someone whose\nprovider_id you don't have; when a hit's counterpart name is their\nfull name, take it as theirs and re-read by that counterpart's\nprovider_id. An empty result isn't proof of no prior\ncontact: this local store holds only a bounded initial backfill\nfrom around when the account was connected, forward. When you need\ncertainty, call `fetch_linkedin_messages_with_person` with their\nprovider_id — it reads LinkedIn's full synced history live."
  3. Changed1 schema field changed
    • changedInput schema / properties / where_clause / description
      Previous value: -"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; '' / 'interested' /\n'meeting_booked' / 'not_interested' — the reply's classified outcome).\nTo read your thread with a person, match their provider_id alone:\n`sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`.\nKeep their name out of that clause: a name ILIKE is a substring\nmatch, so it pulls in other people's threads (`'%Ana%Li%'` also\nmatches \"Diana Lim\"). Match on a name only to find someone whose\nprovider_id you don't have; when a hit's counterpart name is their\nfull name, take it as theirs and re-read by that counterpart's\nprovider_id. An empty result isn't proof of no prior\ncontact: this local store holds only a bounded initial backfill\nfrom around when the account was connected, forward. When you need\ncertainty, call `fetch_linkedin_messages_with_person` with their\nprovider_id — it reads LinkedIn's full synced history live."New value: +"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; 'interested' / 'meeting_booked' /\n'not_interested' / 'neither' — the reply's classified outcome, NULL if it\nwas never classified; to exclude a value, write reply_outcome IS DISTINCT\nFROM 'not_interested', which keeps the NULL rows).\nTo read your thread with a person, match their provider_id alone:\n`sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`.\nKeep their name out of that clause: a name ILIKE is a substring\nmatch, so it pulls in other people's threads (`'%Ana%Li%'` also\nmatches \"Diana Lim\"). Match on a name only to find someone whose\nprovider_id you don't have; when a hit's counterpart name is their\nfull name, take it as theirs and re-read by that counterpart's\nprovider_id. An empty result isn't proof of no prior\ncontact: this local store holds only a bounded initial backfill\nfrom around when the account was connected, forward. When you need\ncertainty, call `fetch_linkedin_messages_with_person` with their\nprovider_id — it reads LinkedIn's full synced history live."
  4. Changed2 schema fields changed
    • changedInput schema / properties / on_behalf_of / description
      Previous value: -"Read a consented teammate's LinkedIn message history\ninstead of your own — pass their email. Gated on that teammate's\nconversation-sharing setting; a teammate who hasn't shared is\nrejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."New value: +"A teammate's email (one with conversation sharing turned on), 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."
    • changedInput schema / properties / where_clause / description
      Previous value: -"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; '' / 'interested' /\n'meeting_booked' / 'not_interested' — the reply's classified outcome).\nTo read your thread with a person, match their provider_id alone:\n`sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`.\nKeep their name out of that clause: a name ILIKE is a substring\nmatch, so it pulls in other people's threads (`'%Ana%Li%'` also\nmatches \"Diana Lim\"). Match on a name only to find someone whose\nprovider_id you don't have; when a hit's counterpart name is their\nfull name, take it as theirs and re-read by that counterpart's\nprovider_id. An empty result isn't proof of no prior\ncontact: this local store holds only a bounded initial backfill\nfrom around when the account was connected, forward. When you need\ncertainty, call `fetch_linkedin_messages_with_person` with their\nprovider_id — it reads LinkedIn's full synced history live and\nwrites it back here."New value: +"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; '' / 'interested' /\n'meeting_booked' / 'not_interested' — the reply's classified outcome).\nTo read your thread with a person, match their provider_id alone:\n`sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`.\nKeep their name out of that clause: a name ILIKE is a substring\nmatch, so it pulls in other people's threads (`'%Ana%Li%'` also\nmatches \"Diana Lim\"). Match on a name only to find someone whose\nprovider_id you don't have; when a hit's counterpart name is their\nfull name, take it as theirs and re-read by that counterpart's\nprovider_id. An empty result isn't proof of no prior\ncontact: this local store holds only a bounded initial backfill\nfrom around when the account was connected, forward. When you need\ncertainty, call `fetch_linkedin_messages_with_person` with their\nprovider_id — it reads LinkedIn's full synced history live."
  5. Changed2 schema fields changed
    • removedInput schema / properties / as_teammate
      Removed value: -{
      -  "anyOf": [
      -    {
      -      "type": "string"
      -    },
      -    {
      -      "type": "null"
      -    }
      -  ],
      -  "default": null,
      -  "description": "Read a consented teammate's LinkedIn message history\ninstead of your own — pass their email. Gated on that teammate's\nconversation-sharing setting; a teammate who hasn't shared is\nrejected. Omit for your own."
      -}
    • addedInput schema / properties / on_behalf_of
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Read a consented teammate's LinkedIn message history\ninstead of your own — pass their email. Gated on that teammate's\nconversation-sharing setting; a teammate who hasn't shared is\nrejected. Omit for your own.\nOr pass an agency client's email, from list_agency_clients, to run the call in that client's account, where none of the teammate limits above apply."
      +}
  6. Changed1 schema field changed
    • changedInput schema / properties / where_clause / description
      Previous value: -"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; '' / 'interested' /\n'meeting_booked' / 'not_interested' — the reply's classified outcome).\nTo check whether a prior conversation exists before firing a\ntemplated message, match on\n`sender_provider_id`/`recipient_provider_id` first; if that returns\nzero rows, run a second pass matching the person's full name (first\nAND last) as a substring, e.g. `sender_name ILIKE '%Jane%Doe%' OR\nrecipient_name ILIKE '%Jane%Doe%'`, before concluding there's no\nhistory — a stored provider_id is often blank or wrong, so an\nexact-match zero is indistinguishable from \"never messaged.\" Never\nmatch on first name alone (it pulls every same-first-name person in\nthe inbox and risks dropping a cold message onto a stranger's live\nthread). The full-name pass is a substring match, so if it spans\nmore than one person (different provider_ids), use only the thread\nwhose counterpart is your recipient — don't merge look-alike names\nor reuse a mismatched `chat_id`. Even both passes empty isn't proof\nof no prior contact: this local store holds only a bounded initial\nbackfill from around when the account was connected, forward, so an\nolder or pre-connection thread may never have been ingested — and\nthe same person may be stored under a variant name a substring match\nmisses. When you have the counterpart's `provider_id` and need\ncertainty, call `fetch_linkedin_messages_with_person` — it reads\nLinkedIn's full synced history live and writes it back here; if that\nalso returns empty, treat \"no history\" as confirmed, otherwise flag\nthe uncertainty when a cold-open template goes to approval."New value: +"SQL WHERE clause (without WHERE keyword). Use ONLY these\ncolumns: provider, chat_id, sender_name, sender_provider_id,\nrecipient_name, recipient_provider_id, content, message_datetime,\nreply_outcome (inbound replies only; '' / 'interested' /\n'meeting_booked' / 'not_interested' — the reply's classified outcome).\nTo read your thread with a person, match their provider_id alone:\n`sender_provider_id = '<id>' OR recipient_provider_id = '<id>'`.\nKeep their name out of that clause: a name ILIKE is a substring\nmatch, so it pulls in other people's threads (`'%Ana%Li%'` also\nmatches \"Diana Lim\"). Match on a name only to find someone whose\nprovider_id you don't have; when a hit's counterpart name is their\nfull name, take it as theirs and re-read by that counterpart's\nprovider_id. An empty result isn't proof of no prior\ncontact: this local store holds only a bounded initial backfill\nfrom around when the account was connected, forward. When you need\ncertainty, call `fetch_linkedin_messages_with_person` with their\nprovider_id — it reads LinkedIn's full synced history live and\nwrites it back here."
  7. First observed

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only supply readOnlyHint, so the description carries most of the burden and does it well: content is truncated to 1000 chars, message-only-files cases have empty content, and attachments flag `unavailable` when LinkedIn no longer serves a file. Most valuable is the disclosed limitation that this is a bounded local backfill from account connection forward, so an empty result is not proof of no prior contact — a behavioral caveat no annotation could convey.

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?

Structured into summary and returns blocks with the purpose front-loaded and no filler sentences; each paragraph carries actionable detail. It is on the long side, and the connection-request-note aside plus the follow-up routing to `queue_linkedin_outreach` are tangents that could be trimmed without losing the core call guidance.

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 5-parameter search tool with no output schema: the returns block describes the dict shape, the truncation flag, attachment fields, and the chat_id hand-off to outreach. Combined with the usage caveat about bounded backfill, an agent has everything needed to call it and to interpret an empty result correctly.

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 adds real meaning on top: it directs the agent to the exact column list, points out that a name ILIKE is a substring match ('%Ana%Li%' also hits 'Diana Lim'), and prescribes matching on provider_id alone for reading a thread. It does not itself restate limit/offset/order_by semantics, leaving those to the schema, so it stops short of a 5.

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 — searching the user's LinkedIn message history (synced conversations) — and implicitly delimits scope as the local synced store rather than live LinkedIn. The sibling relationship is made explicit later by naming `fetch_linkedin_messages_with_person` as the live-history alternative, so an agent can distinguish the two without opening a schema.

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 concrete when-to-use guidance: call once per person and loop in run_code to check several people, and no need to pre-screen prospects because first touches to existing conversations are held for review. It also names the alternative (`fetch_linkedin_messages_with_person`) and the exact condition that selects it — when certainty about prior contact is required.

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