Skip to main content
Glama
TSS99
by TSS99

LinkedIn MCP

An MCP server that lets an AI assistant search LinkedIn jobs and read LinkedIn profiles, by driving a real Chrome window over the DevTools protocol.

It is read-only. There is no tool here that connects, messages, endorses, posts or applies — not as a setting, but because the code to do it does not exist. That is a deliberate line: LinkedIn treats automated writing far more harshly than automated reading, and mixing the two is what gets accounts restricted.

Tools

Tool

What it returns

linkedin_search_jobs

Listings with title, company, location, workplace type, salary when posted, posted age, and job_id. Filters: location, posted-within-days, experience level, job type, workplace type, Easy Apply, sort order.

linkedin_get_job

One posting in full — company, location, LinkedIn's job insights, and the whole description with the "See more" clamp expanded.

linkedin_get_profile

Name, headline, location, about text, experience history, education and skills.

linkedin_check_session

Whether the saved browser session is still signed in.

linkedin_close_session

Detaches from Chrome, leaving the window and session alone.

Related MCP server: LinkedIn Sales & Navigator MCP Server

Install

Requires Python 3.12+, uv, and Google Chrome.

git clone https://github.com/TSS99/linkedin-mcp.git
cd linkedin-mcp
uv sync

Sign in once. This opens a Chrome window and waits for you to log in by hand — credentials are never typed by the script, because LinkedIn challenges scripted credential entry:

uv run linkedin-mcp --login

The session persists in ~/.linkedin-mcp/chrome-profile. Check it any time with uv run linkedin-mcp --check, clear it with uv run linkedin-mcp --logout.

Which Chrome profile it drives

By default the server opens a throwaway profile of its own. That window will look brand new — no bookmarks, no extensions, signed into nothing but LinkedIn. That is the point: the server can only ever see LinkedIn.

If you would rather it drove your everyday Chrome, so it reuses the LinkedIn session you already have and does not open a stranger window, set:

LINKEDIN_MCP_PROFILE_DIR=system

or point it at any profile directory explicitly. In an MCP client config that goes in the env block:

{
  "mcpServers": {
    "linkedin": {
      "command": "uv",
      "args": ["--directory", "/path/to/linkedin-mcp", "run", "linkedin-mcp"],
      "env": { "LINKEDIN_MCP_PROFILE_DIR": "system" }
    }
  }
}

Two things to understand before you do:

You still have to restart Chrome once. --remote-debugging-port only takes effect at launch; it cannot be switched on for a process that is already running. So a Chrome that is open right now can never be attached to, whatever the profile setting says. Quit it fully (Cmd+Q, not just closing the window) and let the server start it, or start it yourself with the command --login prints.

The debug port is not LinkedIn-scoped. Anything that can reach 127.0.0.1:9224 can drive every tab in that Chrome and read every session in it — your mail, your bank, all of it — not just LinkedIn. On the isolated profile there is nothing else to reach. On your everyday profile there is everything. Run it that way only on a machine you trust, and close the debug Chrome when you are done.

--logout refuses to delete a profile it did not create, so pointing this at your real Chrome cannot wipe your browser state.

Configure

Add to your MCP client config (see mcp-config.example.json):

{
  "mcpServers": {
    "linkedin": {
      "command": "uv",
      "args": ["--directory", "/path/to/linkedin-mcp", "run", "linkedin-mcp"]
    }
  }
}

For Claude Code: claude mcp add linkedin -- uv --directory /path/to/linkedin-mcp run linkedin-mcp

How it works, and why

CDP against a real Chrome profile, not a launched browser. Playwright's own Chromium advertises itself in a dozen ways LinkedIn checks. Attaching to a real Chrome you logged into yourself keeps the fingerprint and the cookie honest. The server runs on debug port 9224, so it coexists with other browser-driven MCP servers on 9222/9223.

Navigations are paced. LinkedIn rate-limits on cadence, not just volume, so goto enforces a jittered minimum gap. A long research run is slower than it could be, on purpose.

Every string in LinkedIn's DOM appears twice — once visible, once in a visually-hidden span for screen readers — so raw innerText reads as "Acme Corp\nAcme Corp". dedupe_lines collapses consecutive repeats; without it every parsed field comes out doubled.

Selectors are ordered fallback lists. LinkedIn's class names are obfuscated and renamed often. Where possible the parsers anchor on things that have outlived the class churn — the data-occludable-job-id attribute, the stable div#experience anchor ids — and fall back to parsing line order out of card text rather than trusting a per-field hook.

A broken scrape never returns an empty result. Every tool is wrapped so a missing anchor comes back as {"status": "error", "error_type": "stale_selector", ...} naming the selector and URL. An LLM handed [] will report "no jobs found" with total confidence; this makes tool breakage and genuine emptiness distinguishable.

Tests

uv run pytest

The tests cover the pure parsers — URL/filter construction, card parsing, profile entry parsing, line deduplication — against fixtures in the shape LinkedIn's DOM actually produces. They need no browser and no network.

When it breaks

It will. LinkedIn ships markup changes constantly, and a scraper is a guess about someone else's HTML. Expect stale_selector errors eventually; the fix is usually one more entry in a selector list in tools/jobs.py or tools/profiles.py, plus a fixture in tests/test_parsers.py.

If you get auth_required with a checkpoint URL, LinkedIn wants a human: open the Chrome window, clear the challenge, and retry.

A word on terms of use

Automated access is against LinkedIn's User Agreement regardless of how careful the implementation is, and LinkedIn does restrict accounts for it. This is a personal research tool; read-only design and paced requests reduce the risk but do not eliminate it, and the account you point it at is the one that carries that risk. Use your judgement.

Licence

Apache-2.0

Install Server
A
license - permissive license
A
quality
C
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.

Related MCP Servers

  • F
    license
    A
    quality
    Not graded
    maintenance
    Enables AI assistants to interact with LinkedIn and LinkedIn Sales Navigator for searching profiles, managing leads, and handling messaging via cookie-based authentication. It supports professional networking tasks such as sending connection requests and retrieving account details through the Model Context Protocol.
    22
    1
  • A
    license
    A
    quality
    D
    maintenance
    Enables searching and scraping of LinkedIn for structured data on people, companies, and job listings. It allows AI clients to retrieve detailed profiles, experience, and activity sections using browser automation.
    7
    175
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants to search, filter, and extract job listings from LinkedIn using an automated headless browser with semantic AI filtering and deduplication.
    15
    MIT

View all related MCP servers

Related MCP Connectors

  • Give AI agents the LinkedIn tools to find, qualify, engage, and follow up with prospects.

  • Live LinkedIn data for AI agents: profiles, companies, jobs, posts, email finding. No account risk.

  • Run LinkedIn outreach from your AI chat: find leads, launch campaigns, send, and reply.

View all MCP Connectors

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/TSS99/linkedin-mcp'

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