@leadshark/mcp-server
v2.0.1
Published
LeadShark MCP Server - Control LinkedIn automations with natural language
Maintainers
Readme
@leadshark/mcp-server
Talk to your LinkedIn growth engine.
The LeadShark MCP server lets you control posts, automations, DMs, and limits using natural language inside Cursor, Claude Desktop, OpenClaw, or any MCP-compatible client.
What You Can Do
With MCP access, you can:
- Inspect your latest LinkedIn posts (full text + engagement) and performance
- Get AI-suggested automation settings for any post before you create anything
- Create comment → DM automations safely (draft-first)
- Pause, resume, rename, and modify live automations
- Schedule posts with images, a first comment, and automation pre-wired
- Publish, comment, and react as a Company Page you administer
- Control daily DM volume or emergency-stop instantly
On Apex, you can also prospect end-to-end:
- Search LinkedIn for the right people or companies, then enrich them into structured profiles
- Build a warm lead list from any post — everyone who commented, reacted, or reposted, deduped and ranked
- See what a prospect recently commented on and reacted to, so you can open with something specific
- Comment, react, and send connection requests as yourself
- Triage your inbound connection requests in bulk — accept the good ones, ignore the rest
All actions are:
- auditable
- rate-limited
- reversible
Everything runs against your existing LeadShark account and respects plan limits.
Who This Is For
- Existing power-users already running LeadShark automations
- Founders using Cursor or Claude as a daily control surface
- Operators who want to manage LinkedIn growth without dashboards
Who This Is Not For
- Users new to LeadShark or new to automations
- Fully autonomous agents without human review
- High-frequency bot orchestration or spam workflows
There is a human-in-the-loop control layer by design.
Requirements
- LeadShark Pro+ or Apex subscription (MCP access is Pro+ exclusive as of Feb 2026)
- API Key – generate one at https://apex.leadshark.io/docs/api
- Node.js 18+
Installation
npm install -g @leadshark/mcp-serverThis installs the leadshark-mcp command globally.
Configuration
Cursor
Add the following to your Cursor MCP config (~/.cursor/mcp.json):
{
"mcpServers": {
"leadshark": {
"command": "leadshark-mcp",
"env": {
"LEADSHARK_API_KEY": "your_api_key_here"
}
}
}
}Restart Cursor after saving.
Claude Desktop (macOS)
Add the following to:
~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"leadshark": {
"command": "leadshark-mcp",
"env": {
"LEADSHARK_API_KEY": "your_api_key_here"
}
}
}
}Restart Claude Desktop after saving.
Verify Setup
Once connected, try:
"Show me my recent LinkedIn posts"If configured correctly, LeadShark will return your latest posts with engagement and automation status (if exists).
Tool Mental Model
Think of the MCP tools as verbs on your growth system:
Inspect
list_recent_postslist_automationsget_automationlist_scheduled_postsget_scheduled_postlist_companies
Plan
suggest_automation_settings
Create
create_automationschedule_post_with_automation
Modify
edit_automationedit_scheduled_postset_daily_dm_limitmanage_activity_limits(Apex — read remaining budget + tighten/pause channels)
Delete
cancel_scheduled_post
Act (engage as yourself — Apex only, the ACT arm of the loop)
comment_on_postreact_to_postsend_connection_requestmanage_invitationssend_message(DM, or InMail withas_inmail: true)engage_with_comment(reply to or like a comment — as yourself, or in a Company Page's voice withorganization_id)
Inbox & Feed (your conversations and home feed — Apex only)
list_recent_messagesget_messages_with_personfeedget_lead_activity
Company Page (act as a brand you administer — Pro+)
comment_as_companyreact_as_company
Search, Discover, Signals & Enrich (Apex only — the SEE + DECIDE half of the loop)
searchlist_person_postsget_person_activitylist_post_engagersdiscover_lead_magnetslist_signalsenrichenrich_company
Remember (the agent's own action memory — Apex only)
list_actions
You describe intent. MCP handles sequencing, safety, and execution.
Available Tools
list_recent_posts
Fetch your latest LinkedIn posts with engagement stats and automation status.
Each post includes the complete post body (full_text), a short text_preview,
an edited flag, and whether an automation already exists for it.
Examples:
"Show me my recent LinkedIn posts"
"What are my top performing posts?"
"Show me the full text of my last post"list_scheduled_posts
List all your scheduled posts with the full post content, timing, status,
author (personal or Company Page), and whether an automation is attached
(has_automation + automation_template_id). Each item includes content (the
complete body), content_preview, first_comment, and
post_as/organization_id/organization_name. Filter by status: pending,
published, failed, or all (default: pending). To read the attached
automation's full template contents, call get_scheduled_post.
Examples:
"Show me all my scheduled posts"
"What posts are scheduled for this week?"
"List my failed scheduled posts"get_scheduled_post
Read back a single scheduled post by id, including the full, current spec of its attached pre-automation — keywords, every DM template, comment-reply and non-first-degree-reply templates, and settings. Read from the live template (reflects your edits), not a stale snapshot, so you can verify exactly what will publish and fire before it goes live.
Examples:
"Show me everything tomorrow's scheduled post will do"
"Did my keyword edit on that scheduled automation actually save?"suggest_automation_settings
AI-powered tool that analyzes a post and suggests automation settings — name,
trigger keyword, DM template, comment replies, and non-connected replies.
Returns suggestions only; nothing is created. Run it before
create_automation or schedule_post_with_automation to get recommended
settings based on the post content.
Examples:
"Suggest automation settings for this post draft"
"What keyword and DM should I use for my latest post?"create_automation
Create a new automation for a LinkedIn post. All automations are created as Draft by default for safety.
Examples:
"Create an automation for my latest post — DM anyone who comments 'interested' with my ebook link"
"Set up a comment automation that replies 'DM sent!' and sends the PDF"list_automations
List all your automations with status, performance metrics, and the full template contents (keywords, every DM and comment-reply template).
Examples:
"Show me all my running automations"
"Which automations have sent the most DMs?"get_automation
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 (auto-connect, follow-ups, etc.). Use it to verify exactly what an automation will say and do before it fires.
Examples:
"What will my 'Ebook lead magnet' automation actually send?"
"Show me the full DM and reply templates for that automation"edit_automation
Update an automation's name, status, keywords, templates, or settings. Only the fields you include are changed.
Examples:
"Pause all my automations"
"Rename my 'Untitled automation' to 'Ebook lead magnet'"
"Add the keyword 'want' to my ebook automation"
"Change the DM template to include a personalized greeting"edit_scheduled_post
Update a scheduled post's content, scheduled time, or pre-automation settings. Can edit the post copy, reschedule it, or modify automation keywords, templates, and settings. Can only edit pending posts (not published/failed).
Examples:
"Change tomorrow's post to include the new link"
"Reschedule my 9am post to 2pm instead"
"Update Monday's post automation to use the new discount link"
"Change the keywords on my scheduled lead magnet post"
"Enable auto-like on tomorrow's scheduled post automation"cancel_scheduled_post
Cancel a scheduled post permanently. Requires confirmation. Can only cancel pending posts.
What you'll lose:
- Post content (text)
- Image attachments
- Scheduling information
What you'll keep:
- Pre-automation template (if one was attached) — it's already saved and can be reused for future posts
Examples:
"Cancel tomorrow's scheduled post"
"Delete the post scheduled for Monday"Safety Note: This action requires confirm: true. The post content and attachments are deleted permanently, but any pre-automation templates are preserved in your template library.
schedule_post_with_automation
Schedule a LinkedIn post with an optional image attachment and pre-configured automation. Supports attaching images via publicly accessible URL (JPEG, PNG, GIF, WebP up to 5MB).
You can also:
- Add a first comment (
first_comment) posted in your voice right after the post goes live — handy for putting the lead-magnet link in the comments instead of the body. - Publish as a Company Page you administer by passing
organization_id(calllist_companiesfirst). Automation is not supported for Company Page posts.
Examples:
"Schedule this post for tomorrow at 9am with an automation that DMs the link"
"Post my new content at 2pm EST with comment replies enabled"
"Schedule a post with this infographic: https://example.com/my-image.png"
"Schedule this for Monday and drop the signup link in the first comment"
"Schedule this announcement as our company page for Tuesday 10am"Image URL Requirements:
- Must be publicly accessible (no authentication)
- Supported formats: JPEG, PNG, GIF, WebP
- Max file size: 5MB
- The image is downloaded and stored securely
set_daily_dm_limit
Control your daily DM sending rate.
Setting the limit to 0 acts as an emergency stop.
Examples:
"Set my daily DM limit to 100"
"Emergency stop — pause all DM sending"comment_on_post (Apex)
Apex only. 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. Pass the post's post_id (from list_recent_posts,
discover_lead_magnets, or list_signals) and your text. To comment as a
Company Page instead, use comment_as_company (Pro+).
Examples:
"Comment 'love this — congrats on the launch!' on that post"
"Reply to this post for me"react_to_post (Apex)
Apex only. Like (or react to) a LinkedIn post as yourself. A lightweight
"Act" primitive for warming up a lead before commenting or connecting. Defaults
to like; other reactions: love, celebrate, support, insightful,
funny. To react as a Company Page instead, use react_as_company (Pro+).
Examples:
"Like that post"
"React 'insightful' to this one"send_connection_request (Apex)
Apex only. Send a LinkedIn connection request (invite) to a person as
yourself — the "Act" primitive for turning a hot signal or discovered
prospect into a connection. profile is a LinkedIn id (ACo…, e.g. the
actor_linkedin_id from list_signals) or a public identifier / slug. note is an optional
personalized message. Respects your configured daily/weekly connection
limits — if outgoing is disabled or a limit is reached, the request is
rejected.
Examples:
"Send a connection request to that hot lead"
"Invite jane-doe to connect with a note thanking her for the post"manage_invitations (Apex)
Apex only. Triage the inbound connection-request queue — the other half
of send_connection_request. Call with action: "list" to see pending invites
(each with the inviter's name, headline, and invitation_id), then
action: "accept" or action: "ignore" with the invitation_ids you choose
(bulk, up to 50). Governed write: it respects your Apex safety mode
(read-only/draft refuse, queue stages responses for your review, live/admin
execute) and your API key scope, and every response is logged. The
shared_secret needed to respond stays server-side — you act by
invitation_id only.
Where the safety mode lives: the mode is set by you (the account owner), never by the agent — that's the guardrail. Change it, review the staged-action queue, and release/cancel queued items in the dashboard under Settings → Agent Governance (https://apex.leadshark.io/dashboard/settings). In Queue mode,
accept/ignoreare staged (you get apending_action_id) and only execute on release; in Live/Admin they execute immediately.
Examples:
"Show me my pending connection requests"
"Accept everyone who looks like a founder or VP; ignore the recruiters"send_message (Apex)
Apex only. Send a LinkedIn message to a person as yourself. Pass a
provider id (ACo…) or a public profile slug as recipient, plus the text.
- Direct message (default): reaches a 1st-degree connection.
- InMail (
as_inmail: true): reaches someone you are not connected to, spending one of your LinkedIn plan's paid InMail credits. The balance is checked first and the send is refused if you have none (or no InMail feature on your plan). Optionalsubjectsets the InMail subject line.
Governed write: either channel counts against your daily DM limit — the same
budget your automations spend (so InMail can't be an end-run around the cap),
skips anyone on your do-not-engage (archived) list, is velocity-paced +
cooldown-aware, and respects your safety mode. The response includes the channel
used and your remaining DM budget. Check your InMail credit balance any time via
manage_activity_limits (action: "get" → channels.inmail).
Examples:
"DM the three people who just clicked my lead magnet and thank them"
"Message ACoAAB123 — ask if they want the full guide"
"Send an InMail to ACoAAB999 (we're not connected) inviting them to the webinar"engage_with_comment (Apex; Company-Page mode is Pro+)
Act on a comment — action: "reply" (needs post_id, comment_id, text) or
action: "like" (needs comment_id). As yourself (Apex): replies count
against your daily reply budget (shared with automations); likes are
unlimited. Pass author_provider_id when you have it so your do-not-engage list
is honored. In a Company Page's voice (Pro+): add organization_id (from
list_companies) to post the reply/like as the page you administer — audit-logged
and not metered against your personal reply budget. Governed write.
Examples:
"Reply to the top comment on my latest post and thank them"
"Like every comment on this post that asked a question"
"Reply to that comment as our company page" (pass organization_id)list_recent_messages (Apex)
Apex only. List your most recent DM threads, newest first — each with the
other person's name, headline, and profile URL, a last-message preview,
when it was sent, unread count, chat_id, and provider id. Triage your inbox
before deciding who to reply to. Read-only.
Examples:
"Who are my most recent LinkedIn conversations? Anything unread?"
"Show me my latest DM threads so I can decide who to reply to"get_messages_with_person (Apex)
Apex only. Return the conversation with one specific person so you have the
context before replying. Find them like a human would — person can be just
a name ("Jane Doe"), a provider id (ACo…), or a LinkedIn slug; names are
matched against your inbox and your leads, and if several people match you get a
short list to pick from. Each message includes the text, who sent it (from_me),
and the timestamp, returned oldest → newest. Read-only.
Examples:
"Show me my conversation with Jane Doe"
"What did I last say to that prospect?"feed (Apex)
Apex only. Read your own LinkedIn home feed (posts from the people and
pages you follow), normalized into a clean shape: author, headline, post text,
post URL, and engagement counts. Pair it with the act tools to engage. For the
lead-magnet discovery firehose, use discover_lead_magnets instead. Read-only,
paginated.
get_lead_activity (Apex)
Apex only. 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), got a connection request, and so on. Pass the lead by id, provider id, slug, or name. Also tells you whether they're on your do-not-engage list. Read-only.
Examples:
"What has Jane Doe done with my lead magnets?"
"Show me everything we know about ACoAAB123's engagement"manage_activity_limits (Apex)
Apex only. Your "can I act now?" contract and limit control panel.
action: "get"(read-only) returns every channel — DMs, connected & non-connected comment replies, daily & weekly connection requests, and your InMail credit balance (channels.inmail) — with itslimit, how much isused, how muchremaining, and acan_actflag, plus any active cooldown and the seconds until the next action is paced in. Check this before a write instead of discovering a cap by hitting it. Theinmailchannel is read-only — those are paid LinkedIn credits from your plan, not a cap we set.action: "pause"sets a channel to0(emergency brake).action: "update"changes a cap. Lowering applies immediately; raising a cap requires admin authority and is staged for the account owner to approve — an agent can never widen its own budget on its own.
All caps are shared with your automations and set by the account owner.
Examples:
"How many DMs do I have left today? Can I send one now?"
"Pause my DMs" → { "action": "pause", "channel": "dm" }
"Lower my daily connection limit to 30"list_companies
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.
Examples:
"Which company pages can I post as?"
"List my company pages"comment_as_company
Post a comment on a LinkedIn post in one of your Company Pages' voice
instead of your personal profile — useful for amplifying a post as the brand.
You must administer the target page. Get the organization_id from list_companies.
Examples:
"Comment 'Great breakdown 👏' on that post as our company page"
"Reply to this post as the brand"react_as_company
Like (or react to) a LinkedIn post as one of your Company Pages instead of
your personal profile. Defaults to like; other reactions: love, celebrate,
support, insightful, funny. You must administer the target page.
Examples:
"Like that post as our company page"
"React 'celebrate' to this announcement as the brand"search (Apex)
Apex only. Find the LinkedIn people (or companies) you mean — the proactive
See primitive. Pass free-text keywords plus optional hints (location,
industry, network_distance, followers_min). People come back decision-ready
— name, headline, location, followers_count, shared_connections_count,
profile_picture_url, normalized network_distance (1/2/3) — and each is ranked
with a transparent relevance_score + match_reasons so you can see why it
placed. Cheap-only by design: it never enriches behind the scenes (no
current_company/email — call enrich on a chosen person to Decide, then
comment_on_post / send_connection_request to Act). Set category to people
(default) or companies. Paginate with the returned next_cursor; the response
also carries a rate_limit block. Read-only.
Examples:
"Find VP Marketing people at SaaS companies in my 2nd-degree network"
"Search LinkedIn for fintech founders in London with 1k+ followers"list_person_posts (Apex)
Apex only. Pull a person's recent posts so you have something concrete to act
on — this closes the loop: search (find them) → enrich (decide) →
list_person_posts → comment_on_post / react_to_post (act). Pass profile as
the id or public_identifier from search/enrich (a provider id ACoA…, a
vanity slug, or a full profile URL). Each post's post_id is a
urn:li:activity:… that drops straight into the act tools — no id wrangling.
Returns text, share_url, posted_at, engagement metrics, and a rate_limit
block. Reads as your own connected session; paginate with next_cursor. Read-only.
Examples:
"Pull Jane Doe's last 5 posts and comment on the most recent one"
"What has this prospect posted lately?"get_person_activity (Apex)
Apex only. A person's recent comments and reactions — the posts they
actually engaged with. Available nowhere else — the sharpest "what does this
person care about right now" signal, so you can open with something specific
instead of generic. Pass
profile as the id / public_identifier from search, enrich, or
list_signals (provider id, slug, or profile URL); choose types
(["comments","reactions"], default both). Each item carries the engaged-with
post (share_url + activity_urn) and its author — feed an activity_urn to
comment_on_post / react_to_post, or the author to enrich. Reads as your own
connected session; paginate per type with comments_cursor / reactions_cursor.
Read-only.
Examples:
"What's this prospect been commenting on lately?"
"Show me what jane-doe has reacted to so I can find a hook"list_post_engagers (Apex)
Apex only. Turn any 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 tagged with a signals[] of how
they engaged. Pass post_id from list_person_posts / list_recent_posts (a
URN, bare id, or post URL) and optionally types
(["comments","reactions","reposts"], default all). Then enrich the people you
like (decide) and send_connection_request / comment_on_post (act). Reads as
your own connected session; paginate each type with its own *_cursor.
Read-only.
Examples:
"Who engaged with my last post? Build me a lead list."
"List everyone who commented on this competitor's post"discover_lead_magnets (Apex)
Apex only. 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 (post_url/post_text/post_image_url), creator identity, and engagement
counts. Paginate with cursor; pass since=<ISO> for an only-new-since-last-poll
feed. This is the Discover step of the agent loop. Read-only.
Examples:
"Find me fresh lead-magnet posts to comment on today"
"What's new on the radar since yesterday?"list_signals (Apex)
Apex only. 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. This is the Signals
step of the agent loop. Read-only.
Examples:
"Show me my hottest leads right now"
"Who's been engaging with me the most this week?"enrich (Apex)
Apex only. Enrich a LinkedIn person into a structured profile — name, headline,
location, current role & company, work history, links, follower/connection counts,
and network distance. This is the Decide input: LeadShark supplies the data, your
agent ranks fit and intent itself (pair it with list_signals). profile is a
LinkedIn id (ACoA…, e.g. the actor_linkedin_id from list_signals), a public
identifier / slug, or a full profile URL. Reads through a cache so repeat lookups are cheap. Read-only.
Examples:
"Enrich that lead — what's their role and company?"
"Look up jane-doe and tell me if she fits our ICP"enrich_company (Apex)
Apex only. 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. The company-side Decide input:
pair it with enrich (person) so your agent can judge account fit. company is a
numeric LinkedIn company id (102353837), a vanity slug (microsoft), or a full
company URL (linkedin.com/company/…). Reads through a cache so repeat lookups are
cheap. Read-only.
Examples:
"Enrich Acme Corp — industry, size, and HQ?"
"Is this company big enough to fit our ICP?"list_actions (Apex)
Apex only. The agent's 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 has run via MCP or
the REST API, newest first. Use it to recall what you already did before acting
again: dedupe ("did I already comment on / connect with this person?"), read why
something was refused (result + refusal_code), and see what's staged awaiting
your approval (status: "pending"). The response also returns your current
active_mode. Scoped to your own account only — never anyone else's. Read-only;
paginate with cursor (the created_at of the last item).
Examples:
"What have I already done today? Anything refused or still pending approval?"
"Did I already connect with this person?"
"Show everything that got refused this week."Safety by Design
The MCP server is intentionally conservative.
- New automations always start as Draft
- Automations cannot be deleted via MCP
- Scheduled posts can be canceled (requires confirmation)
- DM volume respects your daily limits
- Emergency stops require confirmation
- Every action is logged and attributable
- Agent writes share the same daily/weekly budgets as your automations, are
velocity-paced, and honor your do-not-engage list — call
manage_activity_limits(action: "get") to see remaining budget before acting - An agent can tighten or pause its own limits, but raising a cap requires admin authority and the account owner's approval
Example Workflows
Quick Post Automation
Goal: deliver lead magnets with ease.
You:
"Create a draft automation for my latest post.
Trigger on 'interested' or 'send'.
Reply with 'check your DMs 👀'
DM them my lead magnet link."Result:
- Draft automation created
- No messages sent yet
- Ready for review and activation
Bulk Automation Management
You:
"Pause all my running automations except the OpenClaw post"MCP will:
- Inspect all automations
- Pause the correct set
- Leave the specified automation running
Scheduled Campaign
You:
"Schedule my new post for 9am tomorrow with full automation.
Keywords: clawdbot, clawd, openclaw, 🦞.
DM my calendar link + github gist.
Auto-like all comments."Result:
- Post is scheduled (with pre-automation details)
- Automation activates automatically when the post goes live
- Starts delivering the resource to everyone who comments the keyword
Lead Magnet with Image
You:
"Schedule a post for Monday 9am EST with this infographic: https://cdn.example.com/guide.png
Set up automation for anyone who comments 'interested' or 'send'.
DM them my ebook link and enable auto-connect."Result:
- Post scheduled with image attached
- Image downloaded and stored securely
- Automation pre-configured (activates when post goes live)
- Lead magnet infographic will publish with the post
Environment Variables
| Variable | Required | Default | Description |
| ------------------- | -------- | --------------------------- | ------------------------------------------- |
| LEADSHARK_API_KEY | Yes | — | Your LeadShark API key |
| LEADSHARK_API_URL | No | https://apex.leadshark.io | API base URL |
Rate Limits
- 100 requests/minute burst
- 250 requests/hour hourly limit
- 1000 requests/day daily limit
- If getting 429 (rate-limit) errors, fallback exponentially.
Error Handling
When things go wrong, you'll get a clean JSON error response:
{
"error": "Failed to fetch LinkedIn posts",
"details": "Unable to retrieve post data from LinkedIn"
}Common errors:
- 401 Unauthorized - Invalid or expired API key
- 429 Too Many Requests - Rate limit exceeded
- 502 Bad Gateway - LinkedIn data temporarily unavailable
- 500 Internal Server Error - Server-side issue
Support
- MCP Docs: https://apex.leadshark.io/docs/mcp
- API Docs: https://apex.leadshark.io/docs/api
- Support: [email protected]
License
MIT
