Overview
An MCP server that reads LinkedIn profiles, companies, jobs, posts and messages through your own logged-in browser session.
Source: github.com/JohannsenLum/linkedin-api-mcp · MIT
Read this before installing
This tool controls a real browser session; it doesn't exploit undocumented APIs or bypass authentication. LinkedIn's User Agreement prohibits automated access, and accounts using automated tools can be restricted or banned. Use at your own risk; there is no guarantee of account safety.
What it does
LinkedIn has no public API for the things people actually want. You cannot look up a profile, search jobs, or read your inbox through anything Instructure-style and sanctioned. The partner API covers "Sign in with LinkedIn" and posting to your own feed, and that's it.
So this server does what you do: it opens a browser, uses the session you're already logged into, and reads the pages. Fourteen tools sit on top of . Eleven read, two write, and one reports on itself.
Install
Get your session cookie into the keyring, and wire the server to your client.
Tools
All fourteen, with the two that take real actions marked.
Safety
The rate limits, the injection fencing, and what the cookie actually is.
How a request flows
Between your model's question and the answer sit five checks. Each one exists because something specific goes wrong without it.
| Gate | Refuses when | |
|---|---|---|
| 1 | Input normalised | The URL points anywhere but linkedin.com, checked before the browser moves, because it carries your session |
| 2 | Queue | You've used 120 actions this hour. It refuses rather than sleeping |
| 3 | Navigation | LinkedIn redirected to login or a checkpoint |
| 4 | Parsing | The markup changed, reported as this server's bug, not yours |
| 5 | Fencing | wraps human-written text so it can't act as instructions |
Tools never raise. Every failure returns {"error": true, "kind": …, "message": …, "hint": …},
so a model gets something it can act on instead of a stack trace.
What makes it different
Two design choices are worth knowing before you decide whether to use this or something else.
The queue is enforced, not advised. Every tool call passes through one action queue: one LinkedIn action at a time, two seconds between them, 120 per rolling hour. Past the ceiling it refuses: the model is told to stop and report rather than to wait. An agent stuck in a retry loop is the realistic way an account gets restricted, and no amount of README wording prevents that.
Untrusted text is fenced. get_conversation and search_posts return text that any
stranger on LinkedIn can write to you, to a model that also holds send_message and
connect. That text is wrapped in delimiters marking it as data, with a boundary token
generated after the content exists so it can't be forged from inside.
What is verified, and what is not
Every read tool has been run against a live LinkedIn account:
| Tool | Status |
|---|---|
get_my_profile, get_profile | name, headline, location, public id |
search_people | name, degree, headline, location |
get_company, search_companies | name, industry, size, headquarters |
search_jobs | title, company, location, on-site/remote |
search_posts | author and post body; reaction and comment counts return null |
get_inbox | participant, preview, timestamp; conversation_id returns null |
send_message | delivered, then confirmed by reading the thread back |
Two gaps are real rather than unfinished:
conversation_id is null. LinkedIn binds each inbox row's click to an
in-memory object instead of a URL, so there is no href or identifier anywhere in the
row to read. get_conversation therefore needs an id obtained another way.
Deep profile sections are empty. experience and education load only once the
page is scrolled, and that is not implemented yet.
Why the selectors look unusual
LinkedIn ships hashed, per-build CSS class names now (b0712e9a, _129ac5aa). The
stable-looking classes that most scrapers target no longer exist, the id="experience"
section anchors are gone, and a profile page has zero h1 elements. Anything matching
on a class breaks within the week.
So this server anchors on what a rebuild cannot rename: document.title, href
patterns, and structure. A result row is the smallest ancestor containing exactly one
entity link. Fields are classified by shape rather than position, so a short headline
and a long one parse the same way.
Disclaimer: This is an independent, community project. It is not affiliated with, authorized by, endorsed by, or sponsored by LinkedIn Corporation or Microsoft. "LinkedIn" is a registered trademark of LinkedIn Corporation and is used here only descriptively to identify the third-party service this software interoperates with.