# Apex MCP tools — full reference

This page documents every tool the Apex MCP server exposes to your AI agent. Apex connects your AI app (Claude, ChatGPT, or Cursor) to [LeadShark](https://www.leadshark.io) over the Model Context Protocol (MCP), so the agent can operate a real LinkedIn account in plain English.

- Product: https://www.apex.new
- Concise index: https://www.apex.new/llms.txt
- Agent operating guide: https://www.apex.new/agents.md
- MCP setup: https://apex.leadshark.io/docs/mcp
- MCP endpoint: https://apex.leadshark.io/mcp

**Legend**
- **Access** — `Read-only` (never changes anything) or `Write`. Write tools marked `destructive` may be staged for your approval and/or require an explicit `confirm`.
- **Tier** — `Apex` tools read or act on people/signals (the core Apex value). `Pro+` tools (automations, scheduling, account limits, Company-Page voice) are the Pro+ baseline.
- Every tool touches LinkedIn, an external system. Inputs are forgiving: people accept a name, public slug, provider id (`ACo…`), or full URL; posts accept a URL, a numeric id, or an activity URN (`urn:li:activity:…`).
- Writes share your account's daily/weekly budgets, honor your do-not-engage (archived) list, and are paced + logged. Prefer `manage_activity_limits` (`get`) and `list_actions` before acting.

---

## See & discover

### `search` — Search LinkedIn
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

Find the LinkedIn people (or companies) you mean — the proactive "See" primitive. People results come back decision-ready (name, headline, location, follower count, shared connections, profile picture, network distance) and each is ranked with a transparent `relevance_score` + `match_reasons`. Cheap by design: it never enriches behind the scenes — call `enrich` on a chosen person to DECIDE, then act. Returns `next_cursor` for pagination and a `rate_limit` block.

- `keywords` (string, **required**) — free-text query: name, role, and/or keywords, e.g. "VP Marketing SaaS".
- `category` (string: `people` | `companies`) — what to search for (default `people`).
- `network_distance` (number[] of 1|2|3) — people only: degrees of connection to include (default all).
- `location` (string) — people only: geographic hint, e.g. "London".
- `industry` (string) — people only: industry hint, e.g. "Software".
- `followers_min` (number) — people only: drop results below this follower count.
- `limit` (number 1–50, default 25) — max results.
- `cursor` (string) — pagination token from a previous search.

### `discover_lead_magnets` — Discover Lead-Magnet Posts
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

Fetch fresh lead-magnet posts from the LeadShark Lead Magnet Radar — a continuously refreshed feed of prospect-rich posts to engage. Each item includes the post (url/text/image), creator identity, and engagement counts. The "Discover" step of the agent loop.

- `limit` (number 1–100, default 25) — number of discoveries to fetch.
- `cursor` (string) — keyset cursor (the `first_detected_at` of the last item) for the next page.
- `since` (string, ISO 8601) — return only discoveries first detected after this instant ("new since my last poll").

### `list_signals` — List Intent Signals
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

List your heat-scored intent signals (hot leads) — who is engaging with you, how hot they are, and why. Each signal includes actor identity, `heat_score`, `signal_count`, a `signal_breakdown`, and `top_signals`. Use `actor_linkedin_id` / `actor_linkedin_url` to act on a lead.

- `limit` (number 1–100, default 25) — number of signals to fetch.
- `min_score` (number) — only return signals with `heat_score` ≥ this value.
- `sort` (string: `heat_score` | `signal_count` | `first_seen_at`) — sort field (default `heat_score`, descending).

### `feed` — Read Your LinkedIn Feed
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

Read your own LinkedIn home feed (posts from people and pages you follow), newest first, normalized into a clean shape: author, headline, post text, post URL, and engagement counts. For the lead-magnet discovery firehose instead, use `discover_lead_magnets`.

- `count` (number 1–25, default 10) — posts per page.
- `start` (number, default 0) — offset for pagination.
- `cursor` (string) — pagination token (`pagination.cursor` from a previous page).

### `list_recent_posts` — List Recent LinkedIn Posts
**Tier:** Pro+ · **Access:** Read-only · **Idempotent:** yes

Fetch your latest LinkedIn posts with engagement stats. Each post includes the complete body (`full_text`), a short `text_preview`, an `edited` flag, and whether an automation already exists for it.

- `limit` (number 1–20, default 5) — number of posts to fetch.

### `list_person_posts` — List Posts by a Person
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

Pull recent posts from a LinkedIn person so you have something concrete to act on — closes the loop: `search` (find) → `enrich` (decide) → `list_person_posts` → `comment_on_post` / `react_to_post` (act). Each post returns a `post_id` (`urn:li:activity:…`) that drops straight into the act tools, plus text, share URL, `posted_at`, and engagement metrics.

- `profile` (string, **required**) — whose posts: provider id, vanity slug, or full profile URL.
- `limit` (number 1–100, default 10) — max posts.
- `cursor` (string) — pagination token from a previous call.

### `list_post_engagers` — List a Post's Engagers
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

Turn any LinkedIn post into a warm lead list: everyone who commented, reacted, and/or reposted it, deduped into one people list ranked warmest-first (more distinct engagements = warmer). Each engager carries a `signals[]` of HOW they engaged. Then `enrich` the people you like and `send_connection_request` / `comment_on_post`.

- `post_id` (string, **required**) — the post: URN, bare numeric id, or URL.
- `types` (string[] of `comments` | `reactions` | `reposts`) — engagement types (default all three).
- `limit` (number 1–100, default 50) — max engagers per type for comments/reactions (reposts page size is fixed by LinkedIn).
- `comments_cursor` / `reactions_cursor` / `reposts_cursor` (string) — per-type pagination tokens.

### `get_person_activity` — Get a Person's Recent Activity
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

The sharpest intent signal there is: a person's recent COMMENTS and REACTIONS — the posts they actually engaged with. Get back what they care about right now so you can open with something specific. Each item carries the engaged-with post (`share_url` + `activity_urn`) and its author — pass an `activity_urn` straight to `comment_on_post` / `react_to_post`, or the author to `enrich`.

- `profile` (string, **required**) — whose activity: provider id, vanity slug, or full profile URL.
- `types` (string[] of `comments` | `reactions`) — which activity to pull (default both).
- `limit` (number 1–50, default 20) — max items per type.
- `comments_cursor` / `reactions_cursor` (string) — per-type pagination tokens.

---

## Decide — enrich & context

### `enrich` — Enrich a Person
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

Enrich a LinkedIn person into a structured profile — name, headline, location, current role & company, work history, links, follower/connection counts, and network distance. The "Decide" input: LeadShark supplies the data, your agent ranks fit and intent (combine with `list_signals`). Reads through a cache so repeat lookups are cheap.

- `profile` (string, **required**) — the person: LinkedIn id (`ACoA…`), public slug, or full profile URL.

### `enrich_company` — Enrich a Company
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

Enrich a LinkedIn Company Page into a structured company profile — name, description, industry, website, employee count & range, follower count, founded date, organization type, and locations. Pair with `enrich` (person) so your agent can judge account fit. Cached.

- `company` (string, **required**) — numeric company id, vanity slug, or full company URL.

### `get_lead_activity` — Get Lead Activity
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

Everything you know a specific lead did, as one newest-first timeline: clicked a lead-magnet link, submitted their email, got DM'd or replied to (by an automation or by you/Apex), got a connection request, etc. Also tells you whether the lead is on your do-not-engage list.

- `lead` (string, **required**) — id, provider id (`ACo…`), profile slug, or name.
- `limit` (number 1–200, default 50) — max activity items.

---

## Act — engagement

### `comment_on_post` — Comment on a Post
**Tier:** Apex · **Access:** Write (destructive) · **Idempotent:** no

Post a comment on a LinkedIn post as yourself (your personal profile). The core "Act" primitive — reply to a discovered lead-magnet post or a hot signal. To comment as a Company Page instead, use `comment_as_company`.

- `post_id` (string, **required**) — the post: URN, bare numeric id, or URL.
- `text` (string, **required**) — the comment text.

### `react_to_post` — React to a Post
**Tier:** Apex · **Access:** Write · **Idempotent:** yes

Like (or react to) a LinkedIn post as yourself. A lightweight "Act" primitive for warming up a lead before commenting or connecting. To react as a Company Page, use `react_as_company`.

- `post_id` (string, **required**) — the post: URN, bare numeric id, or URL.
- `reaction_type` (string) — `like` (default), `love`, `celebrate`, `support`, `insightful`, `funny`.

### `send_connection_request` — Send Connection Request
**Tier:** Apex · **Access:** Write · **Idempotent:** no

Send a LinkedIn connection request (invite) to a person as yourself. Respects your configured daily/weekly connection limits — if outgoing is disabled or a limit is reached, the request is rejected.

- `profile` (string, **required**) — the person: a LinkedIn id (e.g. `actor_linkedin_id` from `list_signals`, `ACo…`) or a public slug.
- `note` (string) — optional personalized note with the invitation.

### `engage_with_comment` — Engage With Comment
**Tier:** Apex (personal) / Pro+ (Company-Page voice) · **Access:** Write · **Idempotent:** no

Act on a comment: reply to it or like it — as yourself or, with `organization_id`, in a Company Page's voice (you must administer the page). `reply` (as yourself) counts against your daily reply budget; `like` is unlimited. Provide `author_provider_id` so the do-not-engage list is honored.

- `action` (string: `reply` | `like`, **required**) — what to do.
- `comment_id` (string, **required**) — the provider comment id.
- `post_id` (string) — the post the comment is on (required for `reply`).
- `text` (string) — reply text (required for `reply`).
- `reply_type` (string: `first_degree` | `non_first_degree`) — which reply budget bucket (default `first_degree`).
- `author_provider_id` (string) — comment author's provider id (honors do-not-engage).
- `reaction_type` (string) — reaction for `like` (default `like`).
- `organization_id` (string) — numeric Company Page id (from `list_companies`); posts in that page's voice (Pro+).

### `manage_invitations` — Manage Connection Invitations
**Tier:** Apex · **Access:** Write (destructive) · **Idempotent:** no

Triage the inbound connection-request queue. `list` returns pending invites with the inviter's identity; `accept` / `ignore` bulk-respond to the `invitation_id`(s) you choose. A governed write: if your account approves first, responses are staged for review; otherwise they run and are logged. The other half of `send_connection_request`.

- `action` (string: `list` | `accept` | `ignore`) — default `list`.
- `invitation_ids` (string[]) — required for `accept`/`ignore`: the ids to act on (up to 50 per call).
- `limit` (number 1–100, default 50) — for `list` only: max invitations to return.

---

## Act — messaging

### `list_recent_messages` — List Recent Messages
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

List your most recent LinkedIn DM threads, newest first — each with the other person's name, headline, profile URL, last-message preview, time, unread count, `chat_id`, and provider id. Use it to triage who to reply to.

- `limit` (number 1–50, default 20) — number of threads.

### `get_messages_with_person` — Get Conversation With Person
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

Return the DM conversation with one specific person so you have context before replying. `person` can be a NAME ("Jane Doe"), a provider id (`ACo…`), or a slug — names are matched against your inbox and leads; if several match you get a short list to choose from. Messages include text, `from_me`, and timestamp, oldest → newest.

- `person` (string, **required**) — name, provider id, or LinkedIn slug.
- `limit` (number 1–100, default 30) — max messages.

### `send_message` — Send Direct Message
**Tier:** Apex · **Access:** Write · **Idempotent:** no

Send a LinkedIn message to a person as yourself. By default a direct message (requires 1st-degree connection). Set `as_inmail: true` to reach a non-connection via InMail, which spends one paid InMail credit (checked first; refused if you have none). Either way it counts against your daily DM limit (the SAME budget your automations use) and skips anyone on your do-not-engage list. Check InMail balance via `manage_activity_limits` (`get` → `channels.inmail`).

- `recipient` (string, **required**) — provider id (`ACo…`) or public profile slug.
- `text` (string, **required**) — the message body.
- `as_inmail` (boolean) — send as InMail (non-connection; spends a credit). Default false.
- `subject` (string) — optional subject line, used only for InMail.

---

## Company Pages (Pro+)

### `list_companies` — List Company Pages
**Tier:** Pro+ · **Access:** Read-only · **Idempotent:** yes

List the LinkedIn Company Pages you administer (id + name). Step 0 for any company action: use the returned id as `organization_id` when posting, commenting, or reacting as a Company Page.

- No parameters.

### `comment_as_company` — Comment as Company Page
**Tier:** Pro+ · **Access:** Write (destructive) · **Idempotent:** no

Post a comment on a LinkedIn post in one of your Company Pages' voice (instead of your personal profile). You must administer the target page.

- `post_id` (string, **required**) — the post: URN, bare numeric id, or URL.
- `text` (string, **required**) — the comment text from the Company Page.
- `organization_id` (string, **required**) — numeric org id of the page you administer (from `list_companies`).

### `react_as_company` — React as Company Page
**Tier:** Pro+ · **Access:** Write · **Idempotent:** yes

Like (or react to) a LinkedIn post in one of your Company Pages' voice. You must administer the target page.

- `post_id` (string, **required**) — the post: URN, bare numeric id, or URL.
- `organization_id` (string, **required**) — numeric org id (from `list_companies`).
- `reaction_type` (string) — `like` (default), `love`, `celebrate`, `support`, `insightful`, `funny`.

---

## Automations

### `create_automation` — Create Automation
**Tier:** Pro+ · **Access:** Write (destructive) · **Idempotent:** no

Create a new automation for a LinkedIn post. Always created as Draft — activate it separately. Supports keywords, DM templates, comment replies, and settings like auto-connect and auto-like.

- `post_url` (string, **required**) — LinkedIn post URL to automate.
- `instruction` (string) — natural-language description of what the automation should do.
- `keywords` (string[]) — trigger keywords (e.g. `["interested","send","want"]`).
- `dm_templates` (string[]) — DM templates. Use `{{firstName}}`, `{{fullName}}`, `{{linkedinUsername}}`.
- `comment_reply_templates` (string[]) — comment reply templates. Use `{{fullNameMention}}` for @mentions.
- `offer_url` (string) — URL to include in DM (e.g. a lead-magnet link).
- `settings` (object) — `auto_connect`, `auto_like`, `auto_enrich` (Apex), `partially_engage` (engage only comments you haven't replied to — enable for posts >1 day old), `follow_up_enabled`, `follow_up_delay_minutes` (1–10080), `follow_up_template`.

### `list_automations` — List Automations
**Tier:** Pro+ · **Access:** Read-only · **Idempotent:** yes

List all your automations with status, settings, performance stats, and full template contents (keywords, every DM and comment-reply template). Use `get_automation` to read one by id.

- `status` (string: `all` | `Running` | `Paused` | `Draft`) — filter (default `all`).

### `get_automation` — Get Automation
**Tier:** Pro+ · **Access:** Read-only · **Idempotent:** yes

Read back the full, current spec of a single live automation by id — keywords, every DM template, comment-reply and non-first-degree-reply templates, and all settings. Use to verify exactly what an automation will say and do before it fires.

- `automation_id` (string, **required**) — UUID (from `list_automations` or `list_recent_posts`).

### `edit_automation` — Edit Automation
**Tier:** Pro+ · **Access:** Write (destructive) · **Idempotent:** yes

Update an automation. Can change name, status (Running/Paused/Draft), keywords, DM templates, comment replies, and all settings. Only include fields you want to change.

- `automation_id` (string, **required**) — UUID to edit.
- `updates` (object, **required**) — any of `name`, `status`, `keywords`, `dm_templates`, `comment_reply_templates`, `settings` (same settings shape as `create_automation`).

### `suggest_automation_settings` — Suggest Automation Settings
**Tier:** Pro+ · **Access:** Read-only · **Idempotent:** yes

AI-powered tool that analyzes a LinkedIn post and generates suggested automation settings — name, trigger keyword, DM template, comment replies, and non-connected replies. Returns suggestions only; nothing is created. Use before `create_automation` or `schedule_post_with_automation`.

- `post_text` (string, **required**) — the post content to analyze.
- `post_url` (string) — optional, provides additional context.

---

## Scheduled posts

### `schedule_post_with_automation` — Schedule LinkedIn Post
**Tier:** Pro+ · **Access:** Write (destructive) · **Idempotent:** no

Schedule a LinkedIn post for future publishing with optional automation and image. The automation activates automatically when the post goes live. Pass `organization_id` to publish as a Company Page you administer (call `list_companies` first; automation isn't supported for Company-Page posts).

- `post_content` (string, **required**) — the post content (max 3000 chars).
- `scheduled_for` (string, **required**) — ISO 8601 datetime (e.g. `2026-02-03T09:00:00Z`).
- `timezone` (string) — e.g. `America/New_York` (defaults to UTC).
- `image_url` (string) — image to attach (JPEG/PNG/GIF/WebP, max 5MB).
- `organization_id` (string) — numeric Company Page id to publish as that page.
- `first_comment` (string) — a comment posted in your voice right after publish (max 3000 chars); common for putting the lead-magnet link in the first comment.
- `automation` (object) — optional automation to attach (`keywords`, `offer_url`, `dm_template`, `comment_reply`, `settings`).

### `list_scheduled_posts` — List Scheduled Posts
**Tier:** Pro+ · **Access:** Read-only · **Idempotent:** yes

List all your scheduled posts with full content, status, timing, author (personal or Company Page), and whether an automation is attached (`has_automation` + `automation_template_id`). Use `get_scheduled_post` to read the attached automation's full template.

- `status` (string: `pending` | `published` | `failed` | `all`) — filter (default `pending`).

### `get_scheduled_post` — Get Scheduled Post
**Tier:** Pro+ · **Access:** Read-only · **Idempotent:** yes

Read back a single scheduled post by id, including the full, CURRENT spec of its attached pre-automation (read from the live template, reflecting edits). Use to verify exactly what will publish and fire before it goes live.

- `post_id` (string, **required**) — UUID (from `list_scheduled_posts`).

### `edit_scheduled_post` — Edit Scheduled Post
**Tier:** Pro+ · **Access:** Write (destructive) · **Idempotent:** yes

Update a scheduled post's content, scheduled time, or pre-automation settings. Can only edit pending posts (not published/failed).

- `post_id` (string, **required**) — UUID to edit.
- `updates` (object, **required**) — any of `content`, `scheduled_time`, `automation` (keywords, dm/comment/non-first-degree reply templates, `offer_url`, settings).

### `cancel_scheduled_post` — Cancel Scheduled Post
**Tier:** Pro+ · **Access:** Write (destructive) · **Idempotent:** yes

Cancel a scheduled post. DESTRUCTIVE: requires `confirm: true`. Deletes post content and attachments permanently (pre-automation templates are preserved and reusable). Can only cancel pending posts.

- `post_id` (string, **required**) — UUID to cancel.
- `confirm` (boolean, **required**) — must be `true` to confirm.

---

## Safety, limits & history

### `manage_activity_limits` — Manage Activity Limits
**Tier:** Apex · **Access:** Read (`get`) / Write (`update`, `pause`) · **Idempotent:** yes

Your activity-limit dashboard and the safety contract to check BEFORE acting. `get` returns every channel — DMs, connected & non-connected comment replies, daily & weekly connection requests, and your InMail credit balance — with its limit, used, remaining, and a `can_act` flag, plus any cooldown. Caps are SHARED with your automations and set by the account owner. `pause` sets a channel to 0 (emergency brake). `update` changes a cap: LOWERING applies immediately; RAISING requires admin authority and is staged for the owner — an agent can never widen its own budget.

- `action` (string: `get` | `update` | `pause`) — default `get`.
- `channel` (string: `dm` | `connected_reply` | `non_connected_reply` | `connection_daily` | `connection_weekly`) — required for `update`/`pause`.
- `limit` (number 0–1000) — new cap for `update` (lowering applies now; raising is staged).

### `set_daily_dm_limit` — Set Daily DM Limit
**Tier:** Pro+ · **Access:** Write (destructive) · **Idempotent:** yes

Set your daily DM sending limit. Setting to 0 is an emergency stop — all DM sending pauses immediately. Requires `confirm: true` for destructive changes.

- `limit` (number 0–250, **required**) — new daily DM limit. Use 0 for emergency stop.
- `confirm` (boolean) — required when setting limit to 0.

### `list_actions` — List Your Action History
**Tier:** Apex · **Access:** Read-only · **Idempotent:** yes

Your own action memory — the audit trail of governed writes (`comment_on_post`, `react_to_post`, `send_connection_request`, `manage_invitations`, and Company-Page actions) this account ran via MCP or REST, newest first. Use it to dedupe ("did I already comment on / connect with this person?"), read why something was refused (`result` + `refusal_code`), see what is staged (`status: 'pending'`), and check your current safety mode (`active_mode`). Scoped to your own account only.

- `result` (string: `ok` | `queued` | `refused` | `error`) — filter by outcome.
- `status` (string: `pending`) — return actions staged for approval instead of the executed log.
- `limit` (number 1–200, default 50) — max items.
- `cursor` (string) — pass the `created_at` of the last item to get older actions.
