linkedin-mcp
This server lets you manage your own LinkedIn profile through an MCP agent, with a strict propose-then-approve flow so the model can draft changes but only write after you approve.
Authenticate with LinkedIn using your own developer app (
auth_start,auth_status).Read your profile data (
get_profile).Draft profile edits (headline, summary, positions, skills, educations) without writing to LinkedIn (
propose_edit), returning a diff and proposal ID.List saved proposals (
list_proposals) and delete rejected or unwanted ones (discard_proposal).Apply a single proposal to LinkedIn — the only write tool — only when you supply the exact approval phrase after reviewing the diff (
apply_proposal).Store proposals locally so approvals can happen in later sessions.
Note: the final write requires LinkedIn partner approval; until then,
apply_proposalmay return an invalid-scope error.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@linkedin-mcp Draft a proposal to update my LinkedIn headline to 'Senior Product Manager'"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
linkedin-mcp
MCP server for editing your own LinkedIn profile from Claude Code — with a hard propose-then-approve split: the model can draft a change and show you the diff, but only one tool can ever write, and it acts only on a proposal you have seen.
Read this before you install
1. You bring your own LinkedIn app. There is no shared or hosted service
here. You create your own app at
linkedin.com/developers/apps, and
your own client ID and secret go in a file on your own machine
(~/.config/linkedin-mcp/.env, permissions 600). The server itself makes no
outbound call to anything but LinkedIn's own API — there is no telemetry and
no third party. Note that it is still an MCP server: profile data and diffs it
returns go to whichever agent you connect, so if that agent is cloud-backed,
your profile content reaches that provider like any other chat content. See
PRIVACY.md.
2. Writing to your profile needs LinkedIn partner approval — which you must
apply for yourself. LinkedIn's Profile Edit API is restricted to
LinkedIn-approved partner developers (w_compliance is "a private permission
and access is granted to select developers" — see
docs/api-notes.md). Approval is granted per developer app,
so your app needs your approval; nobody else's carries over. Until
it lands, apply_proposal returns an invalid-scope error. That is expected,
not a defect — and it may never land, since the permission goes to "select
developers".
Everything else works today without partner approval: OAuth sign-in, reading your profile, drafting edits, and reviewing diffs. Only the final write is gated.
Related MCP server: repo-sdk-mcp
How it works
Tool | What it does | Writes to LinkedIn? |
| Prints the LinkedIn OAuth URL, catches the one-shot localhost redirect, exchanges the code, stores tokens | no (OAuth only) |
| Reports whether a token exists and when it expires | no |
| Fetches your profile ( | no |
| Builds the exact API request for a change (headline, summary, positions, skills, educations), saves it as a proposal, returns a unified diff + | never |
| Lists saved proposals | no |
| Deletes a saved proposal | no |
| Sends ONE saved proposal to LinkedIn — the only write tool. Code-enforced confirm gate: it refuses unless called with | yes |
Proposals persist under ~/.config/linkedin-mcp/proposals/ so an approval
can happen in a later session. Tokens and client credentials live in
~/.config/linkedin-mcp/.env with permissions 600 — entered by you, never
by an agent, never committed (see .env.example).
Install
Requires Python ≥ 3.11. Clone the repo, then register it as an MCP server in
your agent — add this to your client's MCP config (for Claude Code that is
~/.claude.json, or run claude mcp add linkedin -- bash /path/to/linkedin-mcp/run.sh):
{
"mcpServers": {
"linkedin": {
"command": "bash",
"args": ["/path/to/linkedin-mcp/run.sh"]
}
}
}Replace /path/to/linkedin-mcp with the absolute path to your clone. run.sh
creates .venv/ and installs pinned dependencies on first launch (stamp-gated;
all bootstrap output goes to stderr, keeping the MCP stdio channel clean).
Then follow docs/SETUP.md to create your LinkedIn app and sign in — the server has no credentials until you do.
Setup
Follow docs/SETUP.md — it walks through creating your own
LinkedIn Developer app, registering the redirect URL, filling
~/.config/linkedin-mcp/.env with your own client ID and secret, running
auth_start, and applying for the partner program.
Development
bash run.sh --help # bootstraps the venv (runtime deps only)
.venv/bin/pip install -r requirements-dev.txt # adds pytest + coverage tooling
.venv/bin/python -m pytest # offline — every test runs against a mock transportThe test suite includes a granted-write fixture: a mock LinkedIn where
every documented write endpoint happily returns 200. Tests assert that
propose_edit leaves zero non-GET requests in the recorded log even
when writes would succeed, and (positive control) that apply_proposal does
record the documented write call in the same fixture. --live-probe is a
diagnostic flag (single real request, discriminates "endpoint right but
scope not granted" from "endpoint wrong"); it never runs in tests or CI and
requires LINKEDIN_MCP_LIVE_PROBE=1.
The API surface is pinned to dated verbatim excerpts from the official docs in docs/api-notes.md.
License
MIT — see LICENSE.
Available Tools
7 toolsapply_proposalA
THE ONLY TOOL THAT WRITES TO LINKEDIN. Sends the single prepared request stored in one proposal. Requires the human to have approved the returned diff in chat first — never call it on your own initiative, and never immediately after propose_edit without that approval. The approval is code-enforced: the 'approval' argument must be exactly 'approve <proposal_id>', supplied only after the human has seen the diff and said yes in chat. Until LinkedIn grants partner access to the Profile Edit API, an invalid-scope / permission error here is EXPECTED and is not a defect: report it plainly and stop, do not attempt any workaround.
Inputs: proposal_id (required), approval. Returns: object
auth_startA
Run the LinkedIn OAuth sign-in. Opens the consent page in a browser, catches the one-shot localhost redirect, exchanges the code, and stores the token in the owner-only config file (0600). Does NOT modify the profile. Returns only non-secret token status — never the token itself. Set url_only=true to just print the authorization URL without starting the listener.
Inputs: url_only, timeout_seconds. Returns: object
auth_statusA
Report whether a LinkedIn access token is stored and when it expires. Reads local config only: no network call, no profile change, and the token value is never returned.
Inputs: no named parameters. Returns: object
discard_proposalA
Delete a saved proposal that the human rejected or that is no longer wanted. Local file removal only — it never touches LinkedIn.
Inputs: proposal_id (required). Returns: object
get_profileA
Fetch the owner's LinkedIn profile with GET /v2/me. Read-only — it cannot change anything. Use it to get the person id and current values before calling propose_edit.
Inputs: no named parameters. Returns: object
list_proposalsA
List saved edit proposals with their diffs. Local read only — no network call and no profile change. Set include_applied=true to also show proposals that have already been sent.
Inputs: include_applied. Returns: object
propose_editA
Draft a profile change WITHOUT sending it. This tool NEVER writes to LinkedIn: it builds the exact API request, saves it as a proposal, and returns a unified diff of the current value versus the proposed one for human review. Show the diff to the human and wait for their approval; only then may apply_proposal be called with the returned proposal_id. section: headline | summary | position | skill | education. changes: for headline/summary use {'text': '...'}; for position/skill/education use {'action': 'create'|'update'|'delete', 'entity_id': '' (for update/delete), 'fields': {...}}. Localized text fields (skill name, position title/companyName/description, education schoolName/degreeName/...) may be given as plain strings — they are wrapped in LinkedIn's documented MultiLocale shape for the chosen locale.
Inputs: changes (required), section (required), locale, person_id. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 7 tools
Each tool has a distinct, unambiguous purpose: authentication start/status, profile retrieval, proposal creation, listing, discarding, and applying. No two tools overlap in function, and the descriptions clarify the exact role of each.
All tools follow a consistent verb_noun pattern (e.g., auth_start, get_profile, propose_edit, apply_proposal). The naming is uniform, predictable, and clearly indicates the action and target resource.
With 7 tools, the server is well-scoped for its purpose of LinkedIn profile editing. Each tool serves a necessary step in the workflow (auth, read, propose, manage, apply) without redundancy or bloat.
The tool surface covers the full lifecycle: authentication, profile reading, proposal creation with review, proposal management (list/discard), and the single write operation. The workflow is logically complete, and the explicit guardrails prevent dead ends.
Maintenance
Related MCP Connectors
LinkedIn MCP for Claude, ChatGPT, Grok, Hermes: triage inbox, comment, find people, live profiles
LinkedIn outreach from Claude with a human veto: review, approve and send drafts, triage replies.
Draft, revise and review LinkedIn posts; a human approves each one before it goes live.
Check, format and preview LinkedIn posts: limits, see-more cut, hooks, AI-sounding phrases.
Related MCP Servers
- AlicenseAqualityDmaintenanceAllows Claude to execute terminal commands on your computer and perform file system operations including surgical code editing with diff-based replacements.191,287,096 npm7MIT
- FlicenseNot gradedqualityDmaintenanceExposes functions to get the current working directory and review git diffs before commit as MCP tools, leveraging Claude Code CLI for diff review.-
- FlicenseNot gradedqualityCmaintenanceEnables surgical markdown edits across GitHub repositories through a claude.ai custom connector, batching multiple changes into exactly one commit.3-
- FlicenseNot gradedqualityBmaintenanceEnables Claude Code to record a verifiable history of code changes with before/after and rationale, and to propose changes that users can accept or discard, all reviewable on a local web timeline.-