LinkedIn MCP Server
# LinkedIn MCP Server
A local [Model Context Protocol](https://modelcontextprotocol.io/) server that exposes structured
LinkedIn profile, company, and job data to MCP-compatible clients. It uses a persistent Patchright
browser session, so you authenticate interactively instead of storing a LinkedIn password in an
environment variable.
> [!WARNING]
> LinkedIn may restrict automated access, and use of this project may be subject to LinkedIn's
> terms and account limitations. Use it responsibly with your own account. This project is not
> affiliated with or endorsed by LinkedIn.
## Features
| Area | MCP tools |
| --- | --- |
| People | `get_person_profile`, `search_people`, `send_connection_request` |
| Companies | `get_company_profile`, `get_company_posts` |
| Jobs | `get_job_details`, `search_jobs` |
| Session | `close_browser` |
Profile reads can include experience, education, interests, honors, languages, contact information,
and posts. Company reads can include about information, posts, and jobs. Job search supports date,
type, experience, workplace, Easy Apply, and sorting filters.
Connection requests are deliberately constrained: the tool accepts an exact profile slug, previews
the current state by default, requires `confirm=true` to write, and never includes a note.
## Requirements
- Python 3.12+
- [uv](https://docs.astral.sh/uv/getting-started/installation/)
- A LinkedIn account
The project uses `pyproject.toml` and `uv.lock`; a `requirements.txt` file is intentionally not
needed.
## Setup
```bash
git clone https://github.com/aarnav-hariramani/linkedin-mcp.git
cd linkedin-mcp
uv sync --locked
uv run patchright install chromium
```
Authenticate once in a visible browser:
```bash
uv run linkedin-mcp-server --login
```
The default profile is stored at `~/.linkedin-mcp-server/browser-data`. It contains sensitive
session cookies and must never be committed or shared.
Check or clear the saved session:
```bash
uv run linkedin-mcp-server --status
uv run linkedin-mcp-server --logout
```
## MCP client configuration
Use the absolute path to your clone. For Codex:
```bash
codex mcp add linkedin -- uv --directory /absolute/path/to/linkedin-mcp-server run linkedin-mcp-server
```
Equivalent JSON configuration for clients such as Claude Desktop or Cursor:
```json
{
"mcpServers": {
"linkedin": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/linkedin-mcp-server",
"run",
"linkedin-mcp-server"
]
}
}
}
```
Restart the MCP client after changing its configuration.
## Running directly
The recommended local transport is `stdio`:
```bash
uv run linkedin-mcp-server
```
For local development, loopback-only HTTP is also available:
```bash
uv run linkedin-mcp-server \
--transport streamable-http \
--host 127.0.0.1 \
--port 8000
```
Connect to `http://127.0.0.1:8000/mcp`. The server rejects non-loopback HTTP bindings.
## Configuration
Copy the documented template only when you need to override defaults:
```bash
cp .env.example .env
```
Configuration precedence is CLI arguments, existing environment variables, `.env.local`, `.env`,
then defaults.
| Variable | Default | Purpose |
| --- | --- | --- |
| `LINKEDIN_TRANSPORT` | `stdio` | `stdio` or `streamable-http` |
| `LINKEDIN_HOST` | `127.0.0.1` | Loopback HTTP bind address |
| `LINKEDIN_PORT` | `8000` | HTTP port |
| `LINKEDIN_PATH` | `/mcp` | HTTP endpoint path |
| `LINKEDIN_LOG_LEVEL` | `WARNING` | `DEBUG`, `INFO`, `WARNING`, or `ERROR` |
| `LINKEDIN_HEADLESS` | `true` | Run without a visible browser |
| `LINKEDIN_SLOW_MO` | `0` | Browser action delay in milliseconds |
| `LINKEDIN_TIMEOUT` | `10000` | Browser timeout in milliseconds |
| `LINKEDIN_VIEWPORT_WIDTH` | `1280` | Browser viewport width |
| `LINKEDIN_VIEWPORT_HEIGHT` | `720` | Browser viewport height |
| `LINKEDIN_CHROME_PATH` | unset | Optional Chromium executable |
| `LINKEDIN_USER_AGENT` | unset | Optional browser user agent |
| `LINKEDIN_USER_DATA_DIR` | `~/.linkedin-mcp-server/browser-data` | Persistent browser profile |
No username, password, cookie, API key, or token belongs in `.env`.
## Safety model
- Browser navigation is limited to HTTPS LinkedIn hosts.
- Raw page HTML is not returned through MCP tools.
- Persistent profiles are created with restrictive permissions and a marker used for safe logout.
- HTTP transport is local-only; `stdio` is preferred.
- All profile slugs, job IDs, and search input are validated.
- Writes require explicit confirmation.
See [SECURITY.md](SECURITY.md) for handling local session data and reporting vulnerabilities.
## Architecture
```text
src/linkedin_mcp_server/
├── domain/ # Models, parsers, validation, and domain errors
├── ports/ # Browser, authentication, and configuration interfaces
├── application/ # Scrape, search, session, and connection use cases
├── adapters/
│ ├── driven/ # Patchright browser, profile auth, environment config
│ └── driving/ # CLI, FastMCP server, tools, serialization
└── container.py # Dependency composition root
```
## Development
```bash
uv sync --locked --group dev
uv run pre-commit install
uv run ruff check .
uv run ruff format --check .
uv run pytest
uv build
```
Browser-backed integration testing requires a separately authenticated local session. Unit tests
and CI must not depend on real credentials.
See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution workflow.
## Acknowledgments
This project is built on top of [eliasbiondo/linkedin-mcp-server](https://github.com/eliasbiondo/linkedin-mcp-server)
by Elias Biondo, which provided the original hexagonal architecture, Patchright-based browser
automation, and read-only profile, company, and job scraping tools. This fork adds the
`send_connection_request` write tool, additional navigation and session-safety hardening, and
test coverage on top of that foundation.
## License
MIT. See [LICENSE](LICENSE). Original copyright retained; see the LICENSE file for details.
TDQS
Scored across 8 tools
Each tool targets a distinct resource (people, companies, jobs, browser lifecycle), and search vs. detail operations are clearly separated. Minor overlap exists where get_company_profile can fetch posts via sections, duplicating get_company_posts.
All tools follow a consistent snake_case verb_noun pattern: search_*, get_*, send_*, close_*. The naming is predictable and makes the resource and action immediately clear.
Eight tools is a well-scoped set for a LinkedIn API covering people, companies, jobs, and connection actions. Each tool serves a distinct purpose without bloat.
The main workflows are covered: search people, view profiles, send connection requests, search jobs, get job details, and view company info/posts. Minor gaps exist, such as no company search, no connection request with a note, and no direct 'view my profile' tool.