Skip to main content
Glama
tiagoyamashita

openlinkedinmcp

README.md
# openlinkedinmcp

Playwright-powered [MCP](https://modelcontextprotocol.io) server for LinkedIn. Automate jobs, profile edits, messaging, network actions, feed posts, and more using a real logged-in browser session.

> LinkedIn’s official API cannot cover most member actions. This server drives linkedin.com in Chromium instead. UI changes can break selectors — treat tools as best-effort automation.

## Requirements

- Node.js 20+
- Chromium for Playwright (`npx playwright install chromium`)

## Setup

```bash
npm install
npm run build
```

### Install into Cursor / Claude (recommended)

Register this server in your MCP client config (builds if needed, merges without wiping other servers):

```bash
./scripts/install.sh            # interactive: Cursor, Claude, or both
./scripts/install.sh --cursor   # ~/.cursor/mcp.json
./scripts/install.sh --claude   # Claude Code (~/.claude.json) and Desktop if present
./scripts/install.sh --both
./scripts/install.sh --dry-run --both   # preview only
```

Or via npm:

```bash
npm run install:mcp -- --cursor
```

Claude targets: Claude Code CLI (`claude mcp add` when available, else `~/.claude.json`) and Claude Desktop (`claude_desktop_config.json` on macOS/Linux/Windows when the app config dir exists).

Session data (cookies / browser profile) is stored at:

```text
~/.openlinkedinmcp/browser-profile
```

Override with `OPENLINKEDINMCP_DATA_DIR`.

## Manual MCP config

If you prefer to edit configs by hand, add (path must be absolute):

```json
{
  "mcpServers": {
    "openlinkedinmcp": {
      "command": "node",
      "args": ["/absolute/path/to/openlinkedinmcp/dist/index.js"],
      "env": {
        "OPENLINKEDINMCP_HEADLESS": "1"
      }
    }
  }
}
```

Typical locations:

- Cursor: `~/.cursor/mcp.json`
- Claude Code: `~/.claude.json`
- Claude Desktop (macOS): `~/Library/Application Support/Claude/claude_desktop_config.json`
- Claude Desktop (Linux): `~/.config/Claude/claude_desktop_config.json`

For local iteration without building:

```json
{
  "command": "npx",
  "args": ["tsx", "/absolute/path/to/openlinkedinmcp/src/index.ts"]
}
```

## First-time login

1. Call `linkedin_login` from your MCP client.
2. A visible browser window opens on LinkedIn’s login page.
3. Sign in (complete 2FA if prompted).
4. When the feed loads, the tool returns success and the session is reused.

Environment flags:

| Variable | Meaning |
|----------|---------|
| `OPENLINKEDINMCP_HEADLESS=0` | Always show the browser |
| `OPENLINKEDINMCP_LOGIN_TIMEOUT_MS` | Login wait (default `300000`) |
| `OPENLINKEDINMCP_TIMEOUT_MS` | Default Playwright timeout |
| `OPENLINKEDINMCP_DATA_DIR` | Custom data directory |

## Tools

### Auth
- `linkedin_login` — interactive Playwright login
- `linkedin_auth_status`
- `linkedin_logout`

### Profile
- `get_my_profile`, `get_profile`
- `update_headline`, `update_about`
- `open_profile_section` — experience, education, skills, featured, interests, contact-info

### Jobs
- `search_jobs`, `get_job`, `save_job`
- `get_saved_jobs`, `get_job_applications`
- `easy_apply_job` — defaults to `dry_run=true`

### Feed
- `get_feed`, `create_post`, `like_post`, `comment_on_post`, `repost`

### Messaging
- `list_conversations`, `get_conversation`, `send_message`, `start_conversation`

### Network
- `search_people`, `send_connection_request`
- `get_pending_invitations`, `accept_invitation`
- `get_connections`, `follow_person`

### Companies & notifications
- `search_companies`, `get_company`, `follow_company`
- `get_notifications`

### Browser helpers
- `navigate`, `screenshot_page`, `close_browser`

Destructive or outbound actions (`create_post`, `send_message`, `easy_apply_job`, connection requests, etc.) support `dry_run` where noted. Prefer dry runs until you trust the flow.

## Scripts

```bash
npm run dev          # stdio MCP via tsx
npm run build        # compile to dist/
npm run typecheck    # tsc --noEmit
npm start            # node dist/index.js
npm run install:mcp  # register with Cursor / Claude (see Setup)
```

## License

MIT (see `LICENSE`).

TDQS

B3.2/5.0

Scored across 36 tools

Disambiguation4/5

Most tools have clearly distinct purposes targeting different resources/actions (profiles, companies, jobs, messaging, feed). There is minor potential confusion between get_company vs search_companies, get_profile vs search_people, and follow_company vs follow_person, but the descriptions clarify the distinction between search vs direct access. The messaging tools (list_conversations, get_conversation, start_conversation, send_message) are well-separated by their distinct operations.

Naming Consistency4/5

The naming follows a strong verb_noun pattern throughout: get_*, search_*, create_*, follow_*, update_*, send_*, etc. A few slight deviations exist like linkedin_login, linkedin_auth_status, linkedin_logout (using the linkedin_ prefix rather than a verb_noun pattern) and navigate, screenshot_page, close_browser which don't follow the resource-targeting pattern, but the majority is highly consistent.

Tool Count3/5

36 tools is on the heavy end for a domain that could be consolidated. The server covers many sub-domains (profiles, companies, jobs, messaging, feed, notifications, auth), so the breadth justifies part of the count, but auth helpers (login, logout, auth_status) and utility tools (navigate, screenshot_page, close_browser) could arguably be grouped or reduced. Still, each tool serves a distinct function.

Completeness5/5

The tool surface is remarkably comprehensive for LinkedIn automation. It covers authentication lifecycle (login, auth_status, logout), profile viewing/editing (get_profile, get_my_profile, update_headline, update_about, open_profile_section), jobs lifecycle (search, get, save, easy_apply, saved, applications), messaging (list, get, start, send), feed engagement (get_feed, create_post, like, comment, repost), and networking (connections, people search, connection requests, invitations). There are no obvious dead-end operations.

Maintenance

ActivityStale
ResponsivenessNo issues