AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Linkedin Mcp

mcp-mehmoodqureshi-linkedin-mcp · by Mehmoodqureshi

Drive LinkedIn via Playwright, exposed as an MCP server over stdio. Runs standalone (npx) or as an Electron tray app.

No reviews yet
0 installs
35 views
0.0% view→install

Install

$ agentstack add mcp-mehmoodqureshi-linkedin-mcp

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-mehmoodqureshi-linkedin-mcp)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Linkedin Mcp? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

LinkedIn MCP

[](https://github.com/Mehmoodqureshi/linkedin-mcp/actions/workflows/ci.yml) [](https://www.npmjs.com/package/@mehmoodqureshi/linkedin-mcp) [](LICENSE)

A local Electron desktop app that drives LinkedIn through a real, Playwright-controlled Chromium browser and exposes that automation to Claude Desktop (or any MCP client) as an MCP server over stdio.

Instead of using LinkedIn's (restricted) official API, the app logs in as you in a real browser window, keeps the session alive on disk, and lets an AI assistant call a small set of well-defined tools (view a profile, search people/jobs/companies, send a message, send a connection request, read the feed and notifications, etc.). You stay in control: login is manual and headed, so you complete any 2FA/captcha yourself, and your password is never stored.

> Heads-up / status: this repository currently has a number of wiring bugs between the Electron main process and the driver/MCP layers (mismatched import paths, method names, and constructor signatures). See the "Known issues" section at the bottom. The README below describes the intended design and usage.


What it does

  • Headed, persistent browser session. Launches one Chromium via chromium.launchPersistentContext() with an on-disk profile, plus a portable storageState snapshot for fast validation and recovery.
  • Control panel UI. An Electron window shows driver/session status, a sign-in panel, an MCP-client indicator, and a live activity log. The app also lives in the system tray and keeps running in the background as an MCP server.
  • MCP server over stdio. Exposes LinkedIn actions as MCP tools so Claude Desktop can call them. All diagnostics go to stderr so the JSON-RPC stream on stdout stays clean.
  • Rate-limit-aware automation. Every state-changing action is paced (>= ~2s with jitter) and selectors prefer ARIA/data-*/semantic anchors over LinkedIn's randomized CSS classes.

Quick start (npx)

No clone required — run the MCP server straight from npm:

npx -y @mehmoodqureshi/linkedin-mcp

The first install downloads Playwright's Chromium automatically (via the postinstall hook). The server speaks MCP over stdio, so it's normally launched by an MCP client rather than by hand — add it to your client config (see [Connecting to an MCP client](#connecting-to-an-mcp-client)). On first use, call the linkedin_login tool: a headed Chromium window opens so you can sign in (and clear any 2FA/captcha) once; the session is then persisted under ~/.linkedin-mcp for future runs.

npx -y @mehmoodqureshi/linkedin-mcp --help      # usage + config snippet
npx -y @mehmoodqureshi/linkedin-mcp --version

The same package also ships the optional Electron desktop control panel (tray + activity log) — see [How to run](#how-to-run) to build it from source.


Prerequisites

  • Node.js 20+ (the project targets ES2022 / modern Electron).
  • npm (ships with Node).
  • Playwright's Chromium browser. Installed automatically by the postinstall script, or manually:
npx playwright install chromium

Install dependencies:

npm install

This also runs playwright install chromium via the postinstall hook.


How to run

> Just want the MCP server? Use the [npx quick start](#quick-start-npx). This section covers the optional Electron desktop app, built from a source checkout.

npm start

This launches the Electron control panel (UI mode). From there you can:

  1. Start Driver – launches the persistent Chromium.
  2. Sign In – opens LinkedIn's login page in the controlled browser. Enter your credentials and complete any 2FA/captcha manually in that window. Once you reach the feed, the session is persisted.
  3. Watch the Session panel flip to authenticated and the Activity Log for progress.

To rebuild the compiled output (main + renderer) without packaging:

npm run build       # tsc for main/driver/mcp + renderer + copy index.html
npm run typecheck   # type-check only, no emit

Packaging (requires the missing assets/ and build/ files, see Known issues):

npm run pack        # unpacked build
npm run dist        # platform installer(s)

Connecting to an MCP client

MCP clients discover servers from a JSON config — for Claude Desktop:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\\Claude\\claude_desktop_config.json

(Claude Code: claude mcp add; other clients use the same command/args shape.)

Recommended — npx (no checkout)

{
  "mcpServers": {
    "linkedin": {
      "command": "npx",
      "args": ["-y", "@mehmoodqureshi/linkedin-mcp"]
    }
  }
}

Optional env overrides: LINKEDIN_MCP_USERDATA (data/profile dir, default ~/.linkedin-mcp) and LINKEDIN_HEADLESS=1 (run Chromium headless — but keep it headed for the first login).

Alternative — Electron desktop app (from a source checkout)

Runs the same MCP server embedded in the tray app, so you get a control-panel window alongside it:

{
  "mcpServers": {
    "linkedin": {
      "command": "./node_modules/.bin/electron",
      "args": ["."],
      "cwd": "/absolute/path/to/linkedin-mcp",
      "env": { "LINKEDIN_MCP_STDIO": "1" }
    }
  }
}

Notes:

  • Restart the client after editing the config.
  • Both paths share the same persistent session on disk, so logging in once via either is enough.
  • The MCP server keeps stdout reserved for JSON-RPC; all diagnostics go to stderr.

MCP tools

All tools are namespaced linkedin_*. Auth/status tools work without being logged in; every data/action tool requires an authenticated session (call linkedin_login first).

| Tool | Arguments | Description | | --- | --- | --- | | linkedin_login | email?, password? | Opens the headed login flow. Optionally pre-fills credentials; you finish 2FA/captcha by hand. Waits for the session, then persists it. The password is never stored. | | linkedin_logout | – | Clears the saved storageState snapshot and the profile cookies; next action needs a fresh login. | | linkedin_status | – | Returns { status, isLoggedIn, sessionValid }. Works before the browser is launched. | | linkedin_get_profile | profileUrl (full URL or /in/ slug) | Opens and scrapes a member profile: name, headline, location, about, experience, education, skills, connection count. | | linkedin_search_people | query, filters? | People search; returns name, headline, location, profile URL, connection degree. | | linkedin_search_jobs | query, filters? | Jobs search; returns title, company, location, posted date, easy-apply, job URL. | | linkedin_search_companies | query, filters? | Company search; returns name, industry, followers, company URL. | | linkedin_send_message | profileUrl, message | Sends a DM to a 1st-degree/open-profile member. Fails clearly if messaging isn't permitted. | | linkedin_send_connection | profileUrl, note? ( The advertised filters schemas for the search tools are richer than the filters the driver currently applies; treat advanced filters as best-effort for now (see Known issues).


Session persistence

Two cooperating layers keep you logged in across restarts:

  1. Persistent Chromium profile (primary). The browser runs against an on-disk profile directory (/playwright-profile). Cookies, localStorage, and IndexedDB live there and survive app restarts on their own — exactly like a normal browser that "remembers" you.
  1. Portable storageState snapshot (secondary). On login (and on graceful close) the app also writes /linkedin-session.json. This gives:
  • a fast, browser-free "am I logged in?" check by inspecting the LinkedIn li_at cookie and its expiry (no Chromium launch required), and
  • a recovery path: if the profile is corrupted, a fresh context can be re-hydrated by re-injecting the snapshot's cookies via context.addCookies(...).

` is Electron's per-app data directory (e.g. ~/Library/Application Support/LinkedIn MCP/ on macOS). Outside Electron it falls back to $LINKEDINMCPUSERDATA or ~/.linkedin-mcp`.

Validity is determined by the presence of a non-expired li_at cookie. A session cookie (expires === -1) is treated as valid. linkedin_status / the UI surface this as sessionValid, while isLoggedIn additionally confirms against the live page when a context is up.

Logging out (linkedin_logout / the Logout button) clears the profile cookies and deletes the snapshot, forcing a fresh manual login next time.

The session files contain live credentials-equivalent cookies — .gitignore already excludes userData/, pw-profile/, and any storageState.json. Never commit a logged-in session.


Configuration (.env)

Copy .env.example to .env. All values are optional:

  • LINKEDIN_EMAIL / LINKEDIN_PASSWORD – optional pre-fill for auto-login. Not recommended; manual headed login is preferred and required for 2FA/captcha accounts.
  • MCP_LOG_LEVELerror|warn|info|debug|trace.
  • LINKEDIN_MCP_STDIO – set to 1 when launched by Claude Desktop as a stdio MCP server (the config injects this).
  • PLAYWRIGHT_BROWSERS_PATH – override the Playwright browsers location (packaged builds point this at the bundled Chromium).

Project layout

src/
  main/        Electron main process: lifecycle, window, tray, IPC, bootstrapping
    index.ts
    ipc-handlers.ts
  preload/     Context-isolated bridge exposed as window.linkedinMCP
    index.ts
  driver/      Playwright automation layer
    browser.ts     BrowserManager (persistent context, singleton)
    session.ts     SessionManager (storageState persist/validate/recover)
    linkedin.ts    LinkedInDriver facade (composes the action modules)
    types.ts       Shared normalized result types
    actions/       auth, profile, search, messages, connections, feed (+ common, index)
  mcp/         MCP server + tool catalog/dispatch
    server.ts
    tools.ts
    claude-desktop-config.ts
  renderer/    Control-panel UI (sandboxed, no Node)
    index.html
    app.ts

Known issues

This codebase does not yet build/run cleanly. The main process, the driver facade, and the MCP/IPC layers were written against slightly different APIs. The most important mismatches:

  • Wrong import paths in src/main. index.ts and ipc-handlers.ts import ./driver/PlaywrightDriver and ./mcp/server, but the real files are ../driver/linkedin and ../mcp/server. There is no PlaywrightDriver file or createDriver export (use getInstance() / LinkedInDriver).
  • linkedin.ts imports ./actions/messaging but the file is actions/messages.ts.
  • Driver facade vs. main process contract drift: the main process treats the driver as an EventEmitter (driver.on('status', …)) and reads getStatus().state, neither of which the driver provides (getStatus() returns { status, isLoggedIn, sessionValid }).
  • MCP server API: index.ts imports getMcpStatus and calls startMcpServer({ driver, transport }), but server.ts exposes no getMcpStatus and startMcpServer() takes no arguments.
  • IPC handler method names don't match the action modules: e.g. profile.view (should be getProfile), search.people/jobs/companies(query, page) (should be searchPeople/searchJobs/searchCompanies(query, filters?)), connection.sendRequest (should be connections.sendConnectionRequest), message.send (should be messages.sendMessage), notifications.list (should be feed.getNotifications), feed.read (should be feed.getFeed), and auth.login({email,password}) (should be login(email, password)).
  • AuthActions constructor mismatch: linkedin.ts builds new AuthActions(page, session, {email,password}), but the constructor is (page, paths?).
  • tsconfig.json path aliases point at non-existent src/main/driver, src/main/mcp, src/shared.
  • Packaging assets missing: electron-builder.json references assets/icon.*, build/entitlements.mac.plist, and node_modules/playwright-core/.local-browsers (the project installs playwright, not playwright-core); package.json's register-mcp script points at a non-existent scripts/register-mcp.ts.
  • (Resolved) MCP mode detection: the Electron entry now keys off LINKEDIN_MCP_STDIO=1 / --mcp / a non-TTY stdout, matching the config above. The standalone npx binary (dist/cli.js) needs no such flag — it is always an MCP stdio server.

Reconcile these before expecting npm run build / npm start to succeed.


Disclaimer

Automating LinkedIn may violate its Terms of Service and can lead to rate limiting or account restriction. Use a real, consenting account, keep volumes low, and run this only for personal, lawful purposes. You are responsible for how you use it.

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.