LinkedIn MCP Server
A Model Context Protocol (MCP) server that automates LinkedIn job search and profile editing via Playwright browser automation. Built to help DS/ML job seekers keep their LinkedIn profile sharp and run targeted job searches without touching the UI.
---
Features
Job Search
- Search jobs by keywords, location, date, and type
- One-click DS/ML internship and new-grad job search presets
- Fetch full job descriptions from listing URLs
Profile Editing
- Update experience entries (title, company, dates, description)
- Update education entries (GPA, description)
- Update project entries (name, description)
- Update headline and about section
---
Setup
1. Install dependencies
pip install -r requirements.txt
playwright install chromium
2. Log in to LinkedIn
Run the server and call manual_login via Claude Code. This opens a real browser window — log in manually, then save the session:
# Claude Code will call:
manual_login() # opens browser → you log in manually
check_session() # verifies session is active
The session is saved to session.json and reused automatically on future runs.
3. Register as a Claude Code MCP server
Add this to your Claude Code MCP config (~/.claude/mcp_servers.json or via claude mcp add):
{
"linkedin": {
"command": "python",
"args": ["server.py"],
"cwd": "/path/to/linkedin_mcp"
}
}
Or run ad-hoc:
python server.py
---
Project Structure
linkedin_mcp/
├── server.py # FastMCP server — all tools + resume/profile data
├── browser.py # Playwright automation class (LinkedInBrowser)
├── requirements.txt # Python dependencies
├── .gitignore
└── README.md
---
Tools Reference
| Tool | Description | |------|-------------| | check_session | Check if LinkedIn session is still active | | manual_login | Open browser for manual login and save session | | get_profile | Scrape current profile (name, headline, about, experience, education, skills) | | suggest_profile_improvements | Compare profile against resume data and return suggestions | | update_experience | Update all experience entries with titles, dates, and descriptions | | update_education | Update education entries with GPA and descriptions | | update_projects | Update all project entries with names and descriptions | | apply_profile_improvements | Update headline and about section | | search_jobs | Search jobs by keyword, location, date filter, and job type | | search_recent_internships | Search DS/ML/AI internships posted in the last week | | search_new_grad_jobs | Search entry-level DS/ML full-time roles | | get_job_details | Fetch full job description from a listing URL |
---
Key Technical Notes
- React inputs — uses Playwright
.fill(), not JS.valueassignment (JS bypasses React state) - Obfuscated field IDs — field IDs are dynamically resolved by finding label elements by text, then reading their
htmlForattribute - LinkedIn edit URLs — discovered dynamically from
<a>tags on details pages; edit form renders immediately when navigated to directly - SPA routing — LinkedIn uses
history.pushState; pollspage.urlinstead of waiting for Playwright navigation events - Experience description —
contenteditablediv filled viaexecCommand('insertText'); education and project descriptions are regular<textarea>elements
---
Dependencies
| Package | Purpose | |---------|---------| | mcp>=1.0.0 | Model Context Protocol server framework (FastMCP) | | playwright>=1.40.0 | Browser automation | | python-dotenv>=1.0.0 | Optional env file support |
---
Notes
session.jsonstores your LinkedIn auth cookies — keep it private, it is excluded from git via.gitignore- Debug screenshots (
*.png) are written to the project folder at runtime and are also gitignored - Tested on LinkedIn as of June 2026; LinkedIn's DOM can change — if selectors break, check
browser.pyand update aria-labels or placeholder text






