# Reported issues for LinkedIn

Pod holds 15 of 15 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.

Back to [LinkedIn](/mcp/linkedin).

## Most discussed

### Add --json and --version flags to the CLI

## What is missing

The CLI has `auth`, `--test`, and serving. Two small additions would help.

**`--version`** prints the installed version. Currently the only way to check is `python -c "import linkedin_mcp; print(linkedin_mcp.__version__)"`, which is a lot to ask of someone reporting a bug. Version is the first thing an issue needs.

**`--json`** on `--test` emits machine-readable output. `--test` currently prints the redacted config and `Session OK.` for humans. A JSON mode would let people 

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/4) · 2026-08-09 · open · 2 comments

### No test covers posts.py

## What is missing

Every other tool module has a test file:

```
tests/test_company_parsing.py
tests/test_job_parsing.py
tests/test_messaging_parsing.py
tests/test_people_parsing.py
tests/test_safety.py
tests/test_session_errors.py
tests/test_throttle.py
```

There is no `test_post_parsing.py`. None of the 68 passing tests exercise `posts.py` at all, so it could break entirely and CI would stay green.

## Why this one matters more than it looks

`search_posts` returns text **any stranger on Lin

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/3) · 2026-08-09 · open · 2 comments

### No test enforces the README's blanket fencing guarantee, so unfenced fields are found one at a time

## What is wrong

Every fencing test in this repo targets one named field. Nothing asserts the
blanket guarantee the README makes, so an unfenced field is only discovered when
somebody happens to look at that specific line.

That has now happened three times:

- #6, `headline` on `get_profile` and `search_people`
- #13, `author_headline` on `search_posts`
- the company `tagline` (filed separately)

Each was found by reading the code, not by a failing test. A fourth would be
found the same way.

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/19) · 2026-08-12 · closed · 1 comment

### get_company returns tagline unfenced, though it fences the about blurb

## What is wrong

`get_company` fences the `about` blurb but returns `tagline` raw.

`src/linkedin_mcp/tools/companies.py:379` fences the about text:

```python
result["about"] = fence(
    safety_truncate(_clean(about_text), _ABOUT_LIMIT, "company about"),
    "company.about",
)
```

but line 347 returns the tagline with only whitespace normalisation:

```python
"tagline": tagline,     # _clean()ed on line 341, never fenced
```

`grep -c "fence(" src/linkedin_mcp/tools/companies.py` returns 1, 

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/18) · 2026-08-12 · closed · 1 comment

### The fencing guard walks only tools/, so a prose field in server.py would pass unnoticed

## What is wrong

`tests/test_fencing_coverage.py` walks only one directory:

```python
TOOLS_DIR = Path(__file__).resolve().parent.parent / "src" / "linkedin_mcp" / "tools"
...
for path in sorted(TOOLS_DIR.glob("*.py")):
```

Everything outside `tools/` is invisible to it. That includes `server.py`, which already
returns dicts containing a `message` key, and any future `resources.py` or prompt module.

```
src/linkedin_mcp/
  server.py      <- returns {"error": ..., "message": ...}, not walked

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/20) · 2026-08-15 · open · 0 comments

### Server still negotiates MCP protocol 2025-11-25, blocked on FastMCP allowing mcp>=2

## What's wrong

This server negotiates MCP protocol version `2025-11-25`. The current specification is
`2026-07-28`, [announced on 2026-07-28](https://blog.modelcontextprotocol.io/posts/2026-07-28/).

The cause is upstream, not in this repository. `fastmcp` currently pins `mcp<2`, and only
`mcp>=2.0.0` speaks the new protocol version. Verified:

```
# this repo's environment
mcp 1.29.0    LATEST_PROTOCOL_VERSION = 2025-11-25

# clean venv
pip install mcp==2.0.0        -> LATEST_PROTOCOL_VERSION

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/14) · 2026-08-10 · open · 0 comments

### search_posts returns author_headline unfenced, though it fences the post body

## What's wrong

`search_posts` fences the post body but returns the author's headline raw.

In `src/linkedin_mcp/tools/posts.py`, the post text goes through the safety pipeline at both
return sites:

```python
# line 369, structural path
"text": fence(safety_truncate(text, _POST_TEXT_LIMIT, "post text"), "post.text"),
# line 526, selector fallback path
"text": fence(safety_truncate(post_text, _POST_TEXT_LIMIT, "post text"), "post.text"),
```

The author headline, in the same two dicts, does not

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/13) · 2026-08-10 · closed · 0 comments

### The 120-action hourly ceiling lives only in memory, so a process restart or second instance silently resets or doubles it

## What's wrong

`README.md` (around lines 280 to 290) describes the 120-actions-per-hour ceiling as "enforced, not just claimed": "Once hit, further calls fail immediately with a rate_limited error rather than queueing or sleeping."

The enforcement lives entirely in process memory. `ActionQueue.__init__` in `src/linkedin_mcp/throttle.py` (lines 46 to 52) stores usage in `self._history: deque[float] = deque()`, an attribute on the instance, with no file, database, or IPC behind it. `build_serve

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/10) · 2026-08-10 · open · 0 comments

## Most recent

### A missing Chromium browser install surfaces as a generic internal error instead of the existing missing_browser hint

## What's wrong

`Session.start()` in `src/linkedin_mcp/session.py` (lines 92 to 129) only catches one failure mode when launching the browser:

```python
try:
    from patchright.async_api import async_playwright
except ImportError as exc:
    raise LinkedInError(
        "missing_browser",
        "The browser automation dependency is not installed.",
        "Run `uvx --from linkedin-api-mcp patchright install chromium`, or "
        "`patchright install chromium` in your environment.",
    )

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/9) · 2026-08-10 · open · 0 comments

### connect reports sent: true without confirming the invitation actually went out

## What's wrong

`do_connect` in `src/linkedin_mcp/tools/messaging.py` clicks the send-invite button and, if no weekly-limit banner appears, returns `{"sent": true, ...}` (around line 1083) purely because the click did not throw. There is no check that the invitation modal actually closed or that LinkedIn's UI now shows the person as pending.

This is the exact anti-pattern the same file explicitly guards against for messages. `_confirm_sent` (line 773) exists specifically because a click that d

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/8) · 2026-08-10 · open · 0 comments

### README's Contributing section never explains how to run the test suite, though 68 tests need no browser or LinkedIn account

## What's wrong

The README's Contributing section (around lines 325 to 333) only links to the GitHub issues/PR page and CHANGELOG.md. It never says how to run the test suite, and never mentions that this project has a full offline test double for the browser layer.

I ran the suite myself from the repo root:

```
pip install -e ".[dev]"
python -m pytest -q
```

Result: `68 passed in 0.80s`. No `patchright install chromium`, no `LINKEDIN_COOKIE`, no network access needed. The CI workflow (`.gith

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/7) · 2026-08-10 · closed · 0 comments

### get_profile and search_people return the headline unfenced, contradicting the README's fencing guarantee

## What's wrong

A LinkedIn headline is free text the profile owner chose themselves (LinkedIn allows roughly 220 characters, and people routinely fill it with slogans or full sentences). `README.md` (around line 293) says: "Anything read off a LinkedIn page (a headline, an about section, a message) passes through your agent as data. It is fenced before your agent sees it."

The code does not do this for headline. In `src/linkedin_mcp/tools/people.py`:

- Line 741: `headline = clean(card.get("he

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/6) · 2026-08-10 · closed · 0 comments

### Selectors have no early warning when LinkedIn changes its markup

## The problem

This server reads rendered pages. When LinkedIn changes its markup, tools start returning `parse_failed` and nobody knows until a user reports it.

That is not hypothetical. v0.0.1 and v0.0.2 shipped with selectors that returned `parse_failed` on **every page**, because LinkedIn had moved to hashed per-build class names (`b0712e9a`, `_129ac5aa`), removed the `id="experience"` section anchors, and stopped using `h1` on profiles. Two releases went out broken before anyone ran them 

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/5) · 2026-08-09 · open · 0 comments

### get_inbox cannot return conversation_id, so get_conversation is hard to reach

## The problem

`get_inbox` returns participant, preview, timestamp and unread state correctly, but `conversation_id` is always `null`. That makes `get_conversation` awkward to use: you can see a thread exists but cannot open it.

## Why it is null, and why this is not a simple selector fix

I checked this against a real captured inbox page. Conversation rows carry **no `href`, no `data-urn`, and no id attribute anywhere in the row's subtree**. LinkedIn binds each row's click to an in-memory obj

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/2) · 2026-08-09 · open · 0 comments

### search_posts returns null for reaction and comment counts

## What happens

`search_posts` correctly extracts the author, headline and post body, but `reactions` and `comments` always come back `null`.

```
search_posts: 2 posts
   author=Anthony M. Gonzales, MBA   reactions=None  comments=None
```

Confirmed against a live account on v0.0.3.

## Why it is worth fixing

Engagement is most of what makes a post search useful. Without it a model cannot tell a post nobody read from one that landed, so it has no basis for ranking or summarising what it found

[Read the thread](https://github.com/JohannsenLum/linkedin-api-mcp/issues/1) · 2026-08-09 · open · 0 comments

The remaining reports are on [the project's issue tracker](https://github.com/JohannsenLum/linkedin-api-mcp/issues).
