LinkedIn & Sales NavigatorTeardown

The 2-star LinkedIn MCP server has more outbound capability than the 2,800-star one

Source audit of 6 LinkedIn MCP repos: the 2-star repo has Sales Navigator, invite origination, and InMail send — the most-starred repo lacks them.

What we ranSource audit of 6 LinkedIn/Sales Navigator MCP repos at named commits; tool surfaces read from source; star counts captured 2026-07-22. Includes a v3 correction of three false-absence claims found across successive passes.

Capability across six LinkedIn MCP repos runs opposite to GitHub stars. stickerdaniel/linkedin-mcp-server, 2,854 stars, has two write tools and no Sales Navigator at commit b872f92. bhaktatejas922/unipile-linkedin-mcp, 2 stars, has Sales Navigator search, invitation origination, InMail send, and direct messages at commit a6a28cb — the most complete outbound surface in the set. Star count tells you nothing about capability here.

Kairon builds a competing LinkedIn/Sales Navigator MCP server, so read what follows with that in mind. The method is a source read anyone can reproduce — shallow-clone a repo, grep its tool declarations at a named commit, done — the same discipline as wiring Search Console to an agent through Application Default Credentials instead of trusting a summary. Run it yourself before trusting a vendor's audit of its own category, including this one.

On LinkedIn automation. LinkedIn's User Agreement prohibits automated access to the platform, and every tool audited below automates it. This is a source read of what they declare — not a test of whether they work, not permission, not a safety guarantee, and not advice to run them. Your account, your risk.

Earlier passes of this same audit got three facts wrong — all false negatives: two from a grep truncated before it finished listing a repo's tools, and a third that called a repo's tools unenumerable when its own docs listed them. The corrections are below, each with the fix.

Method for all of it: shallow clone, read the tool declarations in source at the commit shown, pull stars/license/last-push from the GitHub API. No LinkedIn account was authenticated and no request reached LinkedIn — source and metadata only.

Capability by repo

RepoStarsSales Nav searchInviteInMail sendDM
stickerdaniel2,854connect_with_personsend_message
eliasbiondo161✗ (read-only)
felipfr75send-message
LaGrowthMachine29~ (URL import, not a search endpoint)✗ (no atomic tool)send_linkedin_message
globodai4linkedin_send_inmail
bhaktatejas922/unipile2search_people_sales_navsend_invitationsend_inmailsend_message, start_chat

Star counts and last-push dates captured 2026-07-22; commit hashes are pinned and stable. LaGrowthMachine's row is read from the tools its repo documents in CLAUDE.md at f1f8402, not verified against the live mcp.lagrowthmachine.com server; every other row is read from source at the commit shown.

The most complete outbound surface in the set belongs to the least-starred repo.

One caveat before that reads as a scoreboard: these are not like-for-like. unipile-linkedin-mcp is a thin wrapper over Unipile, a paid third-party API, and stickerdaniel's own README points readers at Unipile as the paid route to Sales Navigator and InMail. So part of what the table shows is a free self-hosted scraper measured against a wrapper on a commercial service that already solved those endpoints. "More capability per star" is the accurate reading; "better" is not, and we have not measured setup cost, account requirements, or whether any of it works.

bhaktatejas922/unipile-linkedin-mcp — commit a6a28cb, 22 tools

2 stars, Python, MIT, last pushed 2026-01-22. Unipile-API-backed rather than browser scraping. Tools extracted from unipile_linkedin.py by matching @mcp.tool() decorators to their function signatures:

list_accounts()                     get_my_profile()
search_people(keywords, location, ...)
search_people_sales_nav(keywords, location, ...)      <- Sales Navigator
search_companies(keywords, industry, ...)
search_posts(keywords, sort_by, date_posted, ...)
get_search_params(param_type, query)
get_profile(provider_id, sections)  get_company_profile(company_id)
send_invitation(provider_id, message)                 <- originates a request
list_invitations_sent(limit, cursor)
list_invitations_received(limit, cursor)
accept_invitation(invitation_id)    decline_invitation(invitation_id)
cancel_invitation(invitation_id)    list_relations(limit, cursor)
list_chats(limit, cursor, unread_only)
get_chat_messages(chat_id, limit, cursor)
send_message(chat_id, text)         start_chat(attendees_ids, text)
send_inmail(attendees_ids, subject, text)             <- InMail send
get_inmail_credits()

search_people_sales_nav is a Sales Navigator search endpoint — one of two in the set, alongside globodai's, below. send_invitation(provider_id, message) originates a connection request rather than acting on one that already exists. send_inmail(attendees_ids, subject, text) is a second InMail send tool, also alongside globodai's. send_message and start_chat cover direct messages.

stickerdaniel/linkedin-mcp-server — commit b872f92, 16 tools

2,854 stars, Python, Apache-2.0, last pushed 2026-07-22. Two write tools live in linkedin_mcp_server/tools/*.py:

messaging.py: send_message(linkedin_username: str, message: str, confirm_send: bool, ctx: Context, profile_urn: str | ...)
person.py:    connect_with_person(linkedin_username: str, ctx: Context, note: str | None = None, ...)

confirm_send: bool is a required positional argument, not a config flag — every call has to explicitly opt in to sending. That is a deliberate safety design, worth crediting regardless of who ships it.

Full tool surface at this commit:

get_company_profile      get_company_posts        search_companies
get_company_employees    get_feed                  get_job_details
search_jobs              get_inbox                 get_conversation
search_conversations     send_message              get_person_profile
search_people            connect_with_person       get_sidebar_profiles
get_my_profile

No Sales Navigator:

$ grep -ril "sales.nav|salesnav|sales_nav" --include=*.py
scraping/connection.py
scraping/extractor.py
tests/test_scraping.py

All three .py hits are incidental: scraping/connection.py:66 and scraping/extractor.py:236 are the same DOM comment — "Primary action <button>s (Follow, Connect, Save in Sales Navigator) carry aria-label" — and tests/test_scraping.py:889 is a test fixture comment. Widening the grep to .md, the discipline this post insists on elsewhere, surfaces a fourth mention at README.md:25: marketing copy pointing readers at Unipile's hosted Sales Navigator support, not a tool this repo declares. The claim holds — every Sales Navigator reference in this repo is documentation or DOM text, not a search endpoint.

No InMail send either, just two read-side mentions: tools/messaging.py:107 disambiguates whether an inbox thread is an InMail, and scraping/extractor.py:396 is a regex stripping the upsell string ^Get up to .+ replies when you message with InMail$ out of scraped text.

globodai-group/mcp-linkedin-sales-navigator — commit aeb02f3, 7 tools

4 stars, TypeScript, MIT, last pushed 2026-02-05. Playwright-driven (src/browser/navigator.ts). Tool files under src/tools/:

search.ts  -> linkedin_search_leads
leads.ts   -> linkedin_get_lead_profile, linkedin_save_lead
lists.ts   -> linkedin_create_lead_list, linkedin_list_lead_lists
inmails.ts -> linkedin_send_inmail
export.ts  -> linkedin_export_leads

Sales Navigator is the product here, not a comment: linkedin_search_leads is a Sales Navigator search endpoint, and linkedin_send_inmail is an InMail send tool. No invitation-origination or direct-message tool appears in this file set.

felipfr/linkedin-mcpserver — commit d87e7f3, 7 tools

75 stars, TypeScript, MIT, last pushed 2025-03-28. Seven server.tool( registrations in src/: get-connections, get-my-profile, get-network-stats, get-profile, search-jobs, search-people, send-message. send-message is the one write path in this set.

eliasbiondo/linkedin-mcp-server — commit 34d277c, 7 tools

161 stars, Python, MIT, last pushed 2026-03-08. Read-only at this commit: get_person_profile, get_company_profile, get_company_posts, get_job_details, search_people, search_jobs, close_browser. No write tool appears in this file set.

LaGrowthMachine/gtm-system — commit f1f8402, a client for a hosted server

29 stars, Python, MIT, last pushed 2026-07-19.

This repo has now cost two corrections, both of the same kind. An earlier version of this post said it was "not an MCP server at all", on the strength of this grep:

$ grep -ril "mcp.server|FastMCP|@modelcontextprotocol" --include=*.py --include=*.ts --include=*.json
(no matches)

That was wrong, and the grep is why: it never searched .md. The repo's own docs describe a live hosted server.

claude mcp add --scope user --transport http LaGrowthMachine \
  https://mcp.lagrowthmachine.com

So the project does expose an MCP tool surface — the server is hosted, not in-repo. What sits in the repo is the client side: CLAUDE.md, install.sh, and a skills/ directory (catch-opportunities, fuel-my-pipeline, get-qualified-meetings).

The next version fixed that sentence and then wrote a fresh false-absence in its place: that the tools could not be enumerated without a live account, so the row had to stay unknown. That is the third false-absence in this audit, and it landed on the very repo the post keeps using to teach the lesson. The enumeration was in the repo the whole time. CLAUDE.md at f1f8402 (commit title: "refresh MCP tool list") documents roughly 30 tools under the mcp__LaGrowthMachine__* namespace. A sample of what it lists:

  • create_audience_from_linkedin_url — create an audience directly from a Sales Navigator search URL
  • send_linkedin_message — send a LinkedIn reply inside a conversation
  • send_email_message, list_campaigns, get_campaign_stats, get_linkedin_post, execute_bigquery_query, plus inbox, workspace, and identity tools

(CLAUDE.md also lists higher-level skillssales-nav-search-builder, won-deal-icp-finder, and others — which are agent workflows layered on those tools, not entries in the mcp__LaGrowthMachine__* namespace. This post counts tools, not skills.)

Read the shape, not the count. Sales Navigator access here is a URL import (create_audience_from_linkedin_url) rather than a live search endpoint like unipile's search_people_sales_nav; LinkedIn messaging is send_linkedin_message, an in-conversation reply. The documented surface includes no atomic connection-request tool and no InMail-send tool — which is why those two cells read ✗ and the DM cell reads ✓.

One honest boundary remains, narrower than unknown: these are the tools the repo documents, not tools we ran. We have not verified them against the live mcp.lagrowthmachine.com server, which needs an account we do not have. "Documented in-repo" and "verified against the live server" are different claims; this row is the first, and it is flagged as such under the table.

The architecture is worth naming because it is the one we use ourselves. Kairon's MCP is hosted at app.heykairon.com/mcp with the code in apps/api. Grep the wrong directory of our repo and you would "prove" we have no server either.

Where the first pass got it wrong

This audit ran three times: a first extraction at 2026-07-21T03:18:10Z (clones and greps, 03:18–03:35Z), a deepening pass at 2026-07-21T10:12:05Z that closed a gap on felipfr's tool declarations and refuted two of the first pass's own conclusions, and an independent re-clone of all six repos on 2026-07-22 that caught a third.

  1. The first pass claimed linkedin_send_inmail (globodai) was the only InMail send tool across the six repos. False. bhaktatejas922/unipile also has send_inmail(attendees_ids, subject, text).
  2. The first pass claimed unipile only carried invitation lifecycle, not origination. False. send_invitation(provider_id, message) originates a request. The tool list that produced this claim came from a grep ... | head -20 against unipile's source that cut off before reaching the send_* functions.
  3. A later pass claimed LaGrowthMachine's tools could not be enumerated without a live account, and left its row unknown. False, on the same pattern. The repo's own CLAUDE.md at f1f8402 (commit title: "refresh MCP tool list") documents roughly 30 tools under mcp__LaGrowthMachine__*create_audience_from_linkedin_url, send_linkedin_message, and more (the namespaced tools; CLAUDE.md lists higher-level skills separately). An account verifies those tools against the live server; it is not what reveals that the repo documents them.

All three errors trace to the same mistake: inferring that a tool doesn't exist because it didn't show up in an extraction that was itself incomplete. The second pass matched @mcp.tool() decorators to their full function signatures instead of grepping and truncating. Any claim shaped like "repo X doesn't have Y" needs the full tool list, not a slice of it — a head -20 on a grep is exactly the kind of slice that produces a false absence.

What this audit does not establish

  • Whether any of it works. A tool declared in source is not a tool that works. No server was executed and no request reached LinkedIn.
  • Reliability, rate limits, or ban risk. That needs a live run against an owned test account over time, deliberately not attempted here.
  • Setup cost. bhaktatejas922/unipile-linkedin-mcp is API-backed and presumably requires a Unipile account; that was not investigated and shouldn't be assumed either way.

The reproduction is a shallow clone and a grep against a commit hash you can check out yourself. More audits built the same way are on the Kairon blog.