Skip to main content
Glama
pauling-ai

LinkedIn MCP Server

by pauling-ai

LinkedIn MCP Server (Fork)

Personal fork of stickerdaniel/linkedin-mcp-server with additional tools for messaging and post engagement.

Added Tools

Tool

Description

send_message

Send a LinkedIn message to a 1st-degree connection

send_connection_request

Send a connection request, with or without a note

follow_company

Follow a LinkedIn company page

get_inbox

Read recent inbox conversations (supports unread_only)

get_conversation

Read the full message thread with a specific person

get_post_likers

Get the list of people who reacted to a LinkedIn post

get_last_post

Get the most recent organic post from a person's profile

Related MCP server: LinkedIn MCP Server

Detail Mode

All page-scraping tools (get_person_profile, search_people, get_company_profile, get_company_posts, get_job_details, search_jobs) accept a detail parameter:

  • "basic" (default) — truncates each section's raw text to BASIC_SECTION_MAX_CHARS (2000 chars). LinkedIn front-loads the most important content (name, headline, location, about), so this captures essentials while staying well within LLM context limits. References, job IDs, and post URNs are always kept.

  • "full" — returns the complete raw page text, current behaviour.

If basic mode doesn't return enough information, call the tool again with detail="full".

The truncation limit is a single constant in linkedin_mcp_server/tools/utils.py:

BASIC_SECTION_MAX_CHARS = 2000

All Tools

People

Tool

Description

get_person_profile

Get profile info with optional sections: experience, education, interests, honors, languages, contact_info, posts. Supports detail param.

search_people

Search for people by keywords and optional location. Supports detail param.

get_last_post

Get the most recent organic post from a person (visits profile first, then Posts activity tab; returns text, timestamp, URL, and URN)

send_connection_request

Send a connection request, with or without a note (~300 char limit); handles profiles where Connect is in the More dropdown

send_message

Send a message to a 1st-degree connection (also used to reply to existing threads)

follow_person

Follow a person's profile

check_follow

Check whether you are following a person

check_connection

Check whether you are connected to a person

Companies

Tool

Description

get_company_profile

Get company info with optional sections: posts, jobs. Supports detail param.

get_company_posts

Get recent posts from a company's feed; returns post URNs plus lightweight posts[] metadata including posted_at. Supports detail param.

follow_company

Follow a company page

check_follow_company

Check whether you are following a company

Jobs

Tool

Description

search_jobs

Search for jobs by keywords and optional location. Supports detail param.

get_job_details

Get full details of a specific job posting. Supports detail param.

Messaging

Tool

Description

get_inbox

List recent inbox conversations with name, preview, timestamp, and unread flag; pass unread_only=true to filter

get_conversation

Read the full message thread with a person by their LinkedIn username

Posts

Tool

Description

get_post_likers

Get the list of people who reacted to a post (by feed URL or post URN)

get_last_post

Get the most recent organic post from a person (no likes, no reshares)

Session

Tool

Description

close_session

Close the browser session and clean up resources

Setup

Prerequisites: Python 3.12+ and uv installed

# 1. Clone the repo
git clone https://github.com/pauling-ai/linkedin-mcp-server
cd linkedin-mcp-server

# 2. Install dependencies
uv sync

# 3. Log in (first time only)
uv run -m linkedin_mcp_server --login

This opens a browser for you to log in manually (5 minute timeout for 2FA, captcha, etc.). The browser profile is saved to ~/.linkedin-mcp/profile/ and persists across sessions.

Installing into another project

To use this server from another Python project or Claude Code workspace, install it in editable mode pointing at your local clone:

# With uv (recommended)
uv pip install -e /path/to/linkedin-mcp-server

# Or with pip
pip install -e /path/to/linkedin-mcp-server

Editable mode means changes to the repo are immediately reflected — no reinstall needed.

Then point your MCP config at the installed binary:

# Find the installed binary path
which linkedin-scraper-mcp

Claude Code / MCP Configuration

After installing (either in a project venv or globally), add to your .mcp.json:

{
  "mcpServers": {
    "linkedin": {
      "command": "/path/to/venv/bin/linkedin-scraper-mcp",
      "args": ["--project-auth", "--delay-between-linkedin-calls", "1.5", "--delay-jitter", "0.5"],
      "env": {
        "LOG_LEVEL": "INFO"
      }
    }
  }
}

Or run directly from the repo with uv (no install needed):

{
  "mcpServers": {
    "linkedin": {
      "command": "uv",
      "args": ["--directory", "/path/to/linkedin-mcp-server", "run", "-m", "linkedin_mcp_server", "--project-auth", "--delay-between-linkedin-calls", "1.5", "--delay-jitter", "0.5"],
      "env": {
        "LOG_LEVEL": "INFO"
      }
    }
  }
}

The uv approach is the simplest — just update the --directory path and you're done.

Use --slow-mo only when debugging or when you want to watch browser actions happen more slowly. For LinkedIn-facing pacing between tool calls, prefer --delay-between-linkedin-calls / --delay-jitter. The smaller --human-delay-min-ms / --human-delay-max-ms settings still control waits inside individual browser workflows.

If you want a separate LinkedIn account per repo on the same machine, start the server with --project-auth. That stores the auth state under the current project in .linkedin-mcp-server/ instead of the shared ~/.linkedin-mcp/ location.

CLI Options

  • --login - Open browser to log in and save persistent profile

  • --logout - Clear stored LinkedIn browser profile

  • --status - Check if current session is valid and exit

  • --no-headless - Show browser window (useful for debugging)

  • --project-auth - Store auth under the current project at .linkedin-mcp-server/

  • --slow-mo MS - Debugging aid: slows all browser actions uniformly in ms (default: 0)

  • --human-delay-min-ms MS - Minimum randomized delay between LinkedIn actions (default: 500)

  • --human-delay-max-ms MS - Maximum randomized delay between LinkedIn actions (default: 2000)

  • --delay-between-linkedin-calls SECONDS - Baseline delay before each LinkedIn MCP tool call (default: 1.5)

  • --delay-jitter SECONDS - Random jitter added/subtracted from tool-call delay (default: 0.5)

  • --delay-distribution {uniform,beta,lognormal} - Shape of randomized delays (default: beta)

  • --linkedin-cache-dir PATH - Override cache root (default: beside auth profile)

  • --disable-linkedin-cache - Disable LinkedIn tool result caching

  • --profile-cache-ttl-hours HOURS - TTL for cached person profiles (default: 720 / 30 days)

  • --no-reading-time - Disable reading-time pauses after page extraction

  • --reading-time-wpm WPM - Simulated reading speed in words-per-minute (default: 800)

  • --reading-time-jitter PERCENT - Jitter percentage for reading-time pauses (default: 30)

  • --reading-time-max SECONDS - Maximum reading-time pause in seconds (default: 45)

  • --log-level {DEBUG,INFO,WARNING,ERROR} - Logging level (default: WARNING)

  • --timeout MS - Browser timeout for page operations in ms (default: 5000)

  • --transport {stdio,streamable-http} - Transport mode (default: stdio)

Troubleshooting

Patchright browser cache: The server owns Patchright setup by default. On startup it sets PLAYWRIGHT_BROWSERS_PATH=~/.linkedin-mcp/patchright-browsers, sets PLAYWRIGHT_SKIP_BROWSER_GC=1, and idempotently runs python -m patchright install chromium. This makes separate terminals, cron jobs, uvx runs, and long-running automations independent of shell state and prevents unrelated Playwright/Patchright installs from garbage-collecting the browser revision. Override PLAYWRIGHT_BROWSERS_PATH if you need a custom cache, or set LINKEDIN_PATCHRIGHT_AUTO_INSTALL=0 to disable startup install.

Derived runtime profiles: Foreign runtimes reuse committed derived profiles by default, so server restarts do not re-bridge/rebuild the browser profile every time. Set LINKEDIN_PERSIST_DERIVED_SESSION=0 for legacy fresh-bridge behavior, or LINKEDIN_DEBUG_BRIDGE_EVERY_STARTUP=1 when intentionally debugging bridging.

Session expired: Run uv run -m linkedin_mcp_server --login again.

Login issues: LinkedIn may require confirmation in the mobile app, or show a captcha — the --login browser window lets you handle these manually.

Scraping issues: Use --no-headless to watch the browser and --log-level DEBUG for detailed logs.

Timeout issues: Increase with --timeout 10000 or env var TIMEOUT=10000.

Tool-call pacing: Tune with --delay-between-linkedin-calls / --delay-jitter or env vars LINKEDIN_CALL_DELAY_MS / LINKEDIN_CALL_DELAY_JITTER_MS. Defaults are 1.5 ± 0.5 seconds, so each LinkedIn MCP tool call starts after roughly 1-2 seconds.

In-tool pacing: Tune with --human-delay-min-ms / --human-delay-max-ms or env vars HUMAN_DELAY_MIN_MS / HUMAN_DELAY_MAX_MS. These are smaller pauses inside a tool workflow, e.g. after navigation or clicks.

Delay distribution: By default, randomized delays use a beta distribution (right-skewed) instead of a flat uniform distribution. This means most delays cluster toward the shorter end of the configured range, with occasional longer pauses — a pattern that's mathematically much harder for bot detectors to fingerprint. Use --delay-distribution uniform or DELAY_DISTRIBUTION=uniform to restore the old flat distribution. lognormal is also available for a classic human reaction-time model.

Reading-time simulation: After extracting page content, the scraper pauses proportionally to text length to simulate skimming. This makes the scraper's time-on-page correlate with content length, which looks more human to LinkedIn's behavioral analysis. Defaults are 800 WPM with ±30% jitter and a 45-second hard cap. Tune with --reading-time-wpm, --reading-time-jitter, --reading-time-max, or env vars READING_TIME_WPM, READING_TIME_JITTER_PCT, READING_TIME_MAX_SECONDS. Use --no-reading-time or READING_TIME_ENABLED=false to disable.

Scrolling behavior: The scraper simulates human-like scrolling via mouse wheel events rather than instant window.scrollTo jumps. Scroll distances are variable, occasionally include small upward corrections (overshoot/re-reading), and use non-uniform pauses between scroll steps. This triggers LinkedIn's lazy loaders and intersection observers more naturally than mechanical scrolling.

Mouse movement: The scraper replaces Playwright's default locator.click() (which teleports the mouse to the exact element center with no mousemove events) with curved quadratic Bezier paths that generate 5–10 mousemove events before every click. This includes variable speed, occasional overshoot-and-correction, and random target offsets within the element. Idle mouse micro-movements are also added during page reading time to maintain nonzero mouse event density.

Profile caching: get_person_profile caches raw profile scrape results for 720 hours by default. The cache is shared across sessions beside the auth profile: ~/.linkedin-mcp/cache/profiles/ by default, or .linkedin-mcp-server/cache/profiles/ when using --project-auth. Use force_refresh=true on the tool call to bypass cache for one profile. Requests that include the posts section are not cached, so competitor post discovery stays fresh. Use --profile-cache-ttl-hours, --linkedin-cache-dir, or --disable-linkedin-cache to tune globally. Env vars: LINKEDIN_PROFILE_CACHE_TTL_HOURS, LINKEDIN_CACHE_DIR, LINKEDIN_CACHE_DISABLED.

slow_mo vs pacing delays: --slow-mo / SLOW_MO is mainly for debugging because it slows nearly every browser action. Prefer the tool-call and in-tool pacing settings for normal runs.

Per-project LinkedIn accounts: Use --project-auth or env var PROJECT_AUTH=true. The login flow will then create:

  • .linkedin-mcp-server/profile/ for the persistent browser profile

  • .linkedin-mcp-server/cookies.json

  • .linkedin-mcp-server/source-state.json

  • .linkedin-mcp-server/runtime-profiles/ for derived runtime sessions

send_message / get_conversation not finding the Message button: The target user must be a 1st-degree connection. The tool returns an error with diagnostic info if the button isn't found.

send_connection_request not finding the Connect button: The user may already be a connection, have a pending request, or not allow connection requests. If Connect is hidden behind a More dropdown, the tool handles this automatically.

Notes

  • Browser profile stored at ~/.linkedin-mcp/profile/

  • Tool calls are serialized — concurrent requests queue rather than run in parallel

  • Use in accordance with LinkedIn's Terms of Service

License

Apache 2.0 — see LICENSE

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pauling-ai/linkedin-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server