linkedin-mcp-server
This server lets you interact with LinkedIn via natural language through Claude or any MCP-compatible AI assistant.
Profile & Identity
Retrieve your LinkedIn profile details (name, email, profile picture, URN, locale)
Posts
Create text-only, image, or article/link share posts with customizable visibility
Upload images (JPEG, PNG, GIF) to LinkedIn for use in posts
Update the text or visibility of existing posts
Permanently delete your own posts
Retrieve a specific post by URN (requires LinkedIn-approved scope)
List recent posts by a member or organization (requires LinkedIn-approved scope)
Organizations (Companies)
Look up a LinkedIn company page by numeric ID or vanity name
Get the follower count for a company page (requires admin scope)
LinkedIn MCP Server
Post to LinkedIn from Claude. A Model Context Protocol (MCP) server that lets Claude Desktop, Claude Code, and any MCP-compatible AI assistant publish posts, upload images, and manage company pages on LinkedIn — using natural language.
Imagine asking Claude "draft a LinkedIn post about my latest project and publish it" — and it just happens. That's what this MCP server enables.
✨ Features
📝 Create posts | Publish text, image, or article/link posts |
🖼️ Upload images | Attach images to posts in one workflow |
✏️ Edit & delete | Update post text/visibility or remove posts entirely |
👤 Profile | Fetch your name, email, photo, and LinkedIn URN |
🏢 Companies | Look up company pages by name or ID |
📊 Follower counts | Get follower stats for any company page |
🤖 Works with any MCP client | Claude Desktop, Claude Code, Cline, Continue, etc. |
🔐 OAuth 2.0 secured | Industry-standard auth, runs locally on your machine |
Related MCP server: linkedin-mcp-server
💬 What it looks like
Once connected, just talk to Claude naturally:
You: Draft a LinkedIn post announcing my new open-source project and publish it.
Claude: Here's a draft:
🚀 Just open-sourced linkedin-mcp-server — an MCP server that lets
Claude post to LinkedIn directly. No more copy-pasting drafts.
Built with TypeScript and the LinkedIn REST API. Star on GitHub if
you find it useful!
Should I post this?
You: Yes, publish it.
Claude: ✅ Posted. URN: urn:li:share:7339284...Other things you can ask:
"Look up the LinkedIn page for OpenAI and tell me how many followers they have"
"Delete my last LinkedIn post"
"Edit my latest post to add a link to the docs"
"Share this article on LinkedIn with a short intro: https://..."
Before you start
You need three things installed on your computer:
Node.js (v18 or later) — download here
Claude Desktop — download here
A LinkedIn access token (explained in Step 2 below)
Step 1 — Download and build the server
Open your Terminal and run these commands one by one:
git clone https://github.com/abhineet34/linkedin-mcp-server.git
cd linkedin-mcp-server
npm install
npm run buildWhen it finishes, you'll have a dist/ folder. That's the built server.
Note down the full path to this folder — you'll need it in Step 3. To get it, run:
pwdIt will print something like /Users/yourname/linkedin-mcp-server. Remember this.
Step 2 — Get a LinkedIn Access Token
This is the trickiest part. LinkedIn's developer portal has some quirks — follow these steps in order and you'll be fine.
2a. Create a LinkedIn Company Page (required, even for personal use)
LinkedIn forces every developer app to be linked to a Company Page — a personal profile URL will not work, and the "Member Data Portability" default page blocks access to all useful products. So you must create a real Company Page first.
Choose Company
Fill in:
Name — your name, brand, or anything (e.g. "Your Name", "Yourname Dev")
LinkedIn public URL — auto-fills from the name
Industry — pick Software Development (or whatever fits your actual work)
Organization size —
0–1 employeesOrganization type —
Self-employedLogo — optional, any image
Check the verification box and click Create page
The page can stay empty — it just needs to exist.
2b. Create the developer app
Go to LinkedIn Developer Portal and sign in
Click Create app
App name — anything (e.g. "My MCP")
LinkedIn Page — search for the page you just created and select it (don't paste a URL)
App logo — any image
Accept the legal agreement, click Create app
2c. Add required Products to the app
This is the step most people miss. Adding scopes alone is not enough — you also have to add the Products that provide those scopes.
Open your app and go to the Products tab
Click Request access on these two products:
Sign In with LinkedIn using OpenID Connect — provides
openid,profile,emailShare on LinkedIn — provides
w_member_social
Both are auto-approved instantly (no waiting). Refresh the page — they should appear under "Added products"
If "Request access" is grayed out with a tooltip about Member Data Portability, you skipped Step 2a. Create a real Company Page, then create a new app linked to it (the old app cannot be fixed).
2d. Add a redirect URL
Go to the Auth tab of your app
Find "Authorized redirect URLs for your app" and click the pencil icon
Add this URL exactly:
https://www.linkedin.com/developers/tools/oauth/redirectSave
This is LinkedIn's own redirect URL — required for the token generator to work.
2e. Generate the access token
Select your app from the dropdown
Check all four scopes:
☑
openid☑
profile☑
email☑
w_member_social
Click Request access token
LinkedIn will pop up an authorization screen — click Allow
You'll see a long token starting with
AQX...— copy it now (you only see it once)
Your token expires after 60 days. When it stops working, come back to this step and generate a new one.
Step 3 — Connect to Claude Desktop
Open Claude Desktop
Go to Settings → Developer → Edit Config
This opens a file called
claude_desktop_config.json. Add the following inside it (replace the placeholder values):
{
"mcpServers": {
"linkedin": {
"command": "node",
"args": ["/Users/yourname/linkedin-mcp-server/dist/index.js"],
"env": {
"LINKEDIN_ACCESS_TOKEN": "paste-your-token-here"
}
}
}
}Replace
/Users/yourname/linkedin-mcp-serverwith the path you noted in Step 1Replace
paste-your-token-herewith the token from Step 2
Save the file and restart Claude Desktop
Step 4 — Test it
In Claude Desktop, try:
"What's my LinkedIn profile?"
Claude should respond with your name, email, and a long ID starting with urn:li:person:.... If it does — you're all set!
💡 Save your URN. The response includes something like
urn:li:person:izbpuvq9Vz. This is your unique LinkedIn ID — Claude needs it as theauthor_urnwhen creating posts. You can just ask Claude "remember my LinkedIn URN" or copy it somewhere safe.
Example things you can ask Claude
Once connected, just talk to Claude naturally:
"Post on LinkedIn: Excited to share my new project!"
"Create a LinkedIn post sharing this article: https://..."
"Delete my last LinkedIn post"
"How many followers does the Microsoft LinkedIn page have?"
"Look up the LinkedIn page for OpenAI"
Troubleshooting
LinkedIn rejects my personal profile URL when creating an app
LinkedIn only accepts Company Page URLs, not personal profile URLs (
/in/...)Go to Step 2a and create a Company Page first — your "company" can be just your name
"Request access" buttons are grayed out on the Products tab
This happens if you selected "Member Data Portability (Member-only default Company Page)" when creating the app
That default page locks all products. You need to create a real Company Page (Step 2a) and then create a brand new app linked to it — the existing app can't be fixed
Token generator says "no scopes available"
You skipped Step 2c. Go to your app's Products tab and add "Sign In with LinkedIn using OpenID Connect" and "Share on LinkedIn"
Wait a minute, refresh, then try the token generator again
"redirect_uri does not match" error in the token generator
You skipped Step 2d. Go to the Auth tab and add
https://www.linkedin.com/developers/tools/oauth/redirectas an authorized redirect URL
Claude says it can't find the LinkedIn tool
Make sure you restarted Claude Desktop completely after editing the config file (Quit and reopen, not just close the window)
Double-check the file path in the config points to
dist/index.jsand the file actually exists at that pathRun the path in Terminal with
ls <path>to confirm
"Unauthorized" or 401 error
Your access token may have expired (they last 60 days)
Go back to Step 2e and generate a new one, then update the config file and restart Claude Desktop
"Permission denied" or 403 error
The action requires a scope you didn't include
For posting, make sure all four scopes were checked in Step 2e
Some tools (like reading other people's posts) require LinkedIn-approved scopes that aren't available to all developers
Node.js not found
Install Node.js from nodejs.org and try again
Available Tools
✅ Verified working with self-serve scopes
These tools work with the standard scopes anyone can grant themselves (openid, profile, email, w_member_social) and have been tested end-to-end:
Tool | Description | Required scope |
| Get your LinkedIn profile (name, email, photo, URN) |
|
| Create a post (text, image, or article/link) |
|
| Edit a post's text or visibility |
|
| Delete a post |
|
| Upload an image to use in a post |
|
⚠️ Approval-gated (not tested with self-serve scopes)
These tools require LinkedIn-approved scopes that are not available to all developers. They're included in the codebase for users who have partner-level access (e.g., approved Marketing Developer Platform, Community Management API, or Page Admin programs), but they have not been verified end-to-end with the standard self-serve flow described in this README.
Tool | Description | Required scope | LinkedIn approval needed? |
| Get a post by its URN |
| Yes |
| List posts by a member or company |
| Yes |
| Look up a company page |
| Yes (Page admin role) |
| Get a company page's follower count |
| Yes (Page admin role) |
If you have approved access to these scopes and successfully use any of these tools, please open an issue or PR — we'd love to confirm them as verified and document any quirks.
🛠️ Tech stack
TypeScript — fully typed, strict mode
@modelcontextprotocol/sdk — official MCP SDK
Zod — runtime schema validation
Axios — HTTP client
LinkedIn REST API v202604 (configurable via
LINKEDIN_API_VERSIONenv var; v2 fallback used only for OIDC userinfo)
The codebase is intentionally small and easy to extend — one file per tool domain (profile, posts, media, organizations).
🤝 Contributing
Issues and PRs are welcome! If you have an idea, find a bug, or want to add a new LinkedIn API tool, open an issue first to discuss.
Ideas for contributions:
Add tools for LinkedIn comments and reactions
Add scheduled post support
Add carousel post support (multi-image)
Improve error messages with more context
Add a CLI mode for direct usage outside MCP clients
🔗 Related projects
Model Context Protocol — the open standard this server is built on
Anthropic Claude — the AI assistant this server is primarily designed for
MCP Server Registry — a curated list of MCP servers
Claude Desktop — install to use this server with Claude
⚠️ Disclaimer
This is an unofficial integration. It is not affiliated with, endorsed by, or sponsored by LinkedIn Corporation. Use responsibly and within LinkedIn's API terms of service.
⭐ Star the repo
If this saved you time, star the repo — it helps other developers find it.
License
Available Tools
9 toolslinkedin_create_postCreate LinkedIn PostA
Create a new post on LinkedIn on behalf of a member or organization.
Supports text-only posts, image posts (after uploading via linkedin_upload_image), and article/link posts. Post length is capped at 3000 characters.
Requires scope: w_member_social (for member posts) or w_organization_social (for org posts)
Args:
author_urn (string): URN of the post author. Format: 'urn:li:person:{id}' for members. Get your ID from linkedin_get_profile's 'sub' field.
text (string): Post text (1–3000 characters, required)
visibility ('PUBLIC' | 'CONNECTIONS' | 'LOGGED_IN'): Who can see the post (default: 'PUBLIC')
image_asset_urn (string, optional): Asset URN from linkedin_upload_image
article_url (string, optional): URL to share as a link post
article_title (string, optional): Link preview title (used with article_url)
article_description (string, optional): Link preview description (used with article_url)
Returns: The URN of the newly created post (e.g., 'urn:li:share:7123456789')
Examples:
Text post: { author_urn: "urn:li:person:abc", text: "Hello LinkedIn!" }
Image post: { author_urn: "urn:li:person:abc", text: "Check this out", image_asset_urn: "urn:li:digitalmediaAsset:..." }
Article share: { author_urn: "urn:li:person:abc", text: "Great read", article_url: "https://..." }
Error Handling:
403 if w_member_social scope is missing
400 if text exceeds 3000 chars or author URN is invalid
Inputs: author_urn (required), text (required), visibility, image_asset_urn, +3 more.
linkedin_delete_postDelete LinkedIn PostADestructive
Permanently delete a LinkedIn post by its URN.
Only the author of the post can delete it. This action cannot be undone.
Requires scope: w_member_social (member posts) or w_organization_social (org posts)
Args:
post_urn (string): URN of the post to delete (e.g., 'urn:li:share:7123456789')
Returns: Confirmation message on success.
Error Handling:
403 if you are not the author of the post
404 if the post does not exist or is already deleted
Inputs: post_urn (required).
linkedin_get_organizationGet LinkedIn OrganizationARead-onlyIdempotent
Retrieve a LinkedIn organization (company) page by its numeric ID or vanity name.
Without admin access, returns public fields: id, localizedName, vanityName, localizedWebsite, primaryOrganizationType, locations, and logoV2. With admin access (rw_organization_admin scope), returns additional fields like description, industries, staffCountRange, foundedOn, and specialties.
Requires scope: rw_organization_admin
Args:
lookup_by ('id' | 'vanity_name'): How to identify the org (default: 'id')
organization_id (string): Numeric org ID — required when lookup_by='id'
vanity_name (string): URL slug — required when lookup_by='vanity_name' E.g., 'microsoft' for linkedin.com/company/microsoft
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns: For JSON format: { "id": string, "localizedName": string, "vanityName": string, "localizedWebsite": string, "primaryOrganizationType": string, "locations": [...], // Additional fields if you have admin access }
Examples:
By ID: { lookup_by: "id", organization_id: "1441" } ← LinkedIn's own page
By vanity: { lookup_by: "vanity_name", vanity_name: "microsoft" }
Error Handling:
403 if rw_organization_admin scope is missing
404 if the organization ID or vanity name does not exist
Inputs: lookup_by, organization_id, vanity_name, response_format.
linkedin_get_org_follower_countGet LinkedIn Organization Follower CountARead-onlyIdempotent
Get the total number of LinkedIn members following an organization page.
Requires scope: rw_organization_admin
Args:
organization_id (string): Numeric organization ID (e.g., '1234567')
Returns: { "organization_id": string, "follower_count": number }
Examples:
Use when: "How many followers does our company page have?"
Use when: "Compare follower counts across organizations"
Error Handling:
403 if rw_organization_admin scope is missing or you are not an admin of the org
404 if the organization ID does not exist
Inputs: organization_id (required).
linkedin_get_postGet LinkedIn PostARead-onlyIdempotent
Retrieve a LinkedIn post by its URN.
NOTE: Reading posts requires the r_member_social scope for member posts or r_organization_social for org posts. These scopes require LinkedIn approval and are not available to all developers. If you only have w_member_social, use linkedin_create_post and linkedin_list_posts instead.
Requires scope: r_member_social or r_organization_social
Args:
post_urn (string): Post URN (e.g., 'urn:li:share:7123456789')
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns: For JSON format: { "id": string, // Post URN "author": string, // Author URN "commentary": string, // Post text "visibility": string, // PUBLIC | CONNECTIONS | LOGGED_IN "lifecycleState": string, // PUBLISHED | DRAFT | DELETED "createdAt": number, // Unix timestamp (ms) "lastModifiedAt": number, "content": object // Media/article content (if any) }
Error Handling:
403 if r_member_social scope is not approved for your app
Inputs: post_urn (required), response_format.
linkedin_get_profileGet LinkedIn ProfileARead-onlyIdempotent
Retrieve the authenticated LinkedIn member's profile information via the OIDC userinfo endpoint.
Returns the member's name, profile picture, email address, and locale.
Requires scopes: openid, profile, email
Args:
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns: For JSON format: { "sub": string, // LinkedIn member URN (e.g., "urn:li:person:abc123") "name": string, // Full display name "given_name": string, // First name "family_name": string, // Last name "picture": string, // Profile picture URL (optional) "email": string, // Primary email address (optional) "email_verified": bool, // Whether email is verified (optional) "locale": { "country": string, // Country code "language": string // Language code } }
Examples:
Use when: "Who am I logged in as?" → call with default params
Use when: "Get my LinkedIn email" → call with response_format='json', read email field
Don't use when: You need to look up someone else's profile (LinkedIn doesn't expose that via standard API)
Error Handling:
Returns auth error if LINKEDIN_ACCESS_TOKEN is missing or expired
Returns permission error if 'profile' scope is not granted
Inputs: response_format.
linkedin_list_postsList LinkedIn PostsARead-onlyIdempotent
List posts by a LinkedIn member or organization.
NOTE: Listing posts requires the r_member_social scope (member) or r_organization_social scope (org). These are approval-gated scopes. For org posts, you also need to be an admin of the organization.
Requires scope: r_member_social or r_organization_social
Args:
author_urn (string): Author URN — 'urn:li:person:{id}' or 'urn:li:organization:{id}'
limit (number): Max posts to return, 1–50 (default: 20)
offset (number): Posts to skip for pagination (default: 0)
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns: { "total": number, "count": number, "offset": number, "items": [Post], "has_more": boolean, "next_offset": number }
Error Handling:
403 if r_member_social / r_organization_social scope is missing or not approved
Inputs: author_urn (required), limit, offset, response_format.
linkedin_update_postUpdate LinkedIn PostA
Update the text or visibility of an existing LinkedIn post.
Only the author of the post can update it. Note that LinkedIn only allows updating the commentary (text) and visibility after posting — content attachments (images, articles) cannot be changed post-publish.
Requires scope: w_member_social (member posts) or w_organization_social (org posts)
Args:
post_urn (string): URN of the post to update (e.g., 'urn:li:share:7123456789')
text (string): New text content (1–3000 characters)
visibility ('PUBLIC' | 'CONNECTIONS' | 'LOGGED_IN', optional): New visibility setting
Returns: Confirmation message on success.
Error Handling:
403 if you are not the author of the post
404 if the post does not exist
Inputs: post_urn (required), text (required), visibility.
linkedin_upload_imageUpload LinkedIn ImageA
Upload an image to LinkedIn and get an image URN to use when creating an image post.
This is a two-step process handled automatically:
Initialize the upload via /rest/images to get a pre-signed upload URL
PUT the image binary to that URL
After upload, use the returned image URN as image_asset_urn in linkedin_create_post.
Requires scope: w_member_social
Args:
author_urn (string): Owner URN — 'urn:li:person:{id}' from linkedin_get_profile
image_base64 (string): Base64-encoded image file contents
mime_type ('image/jpeg' | 'image/png' | 'image/gif'): Image MIME type (default: image/jpeg)
Returns: { "asset_urn": string, // e.g., "urn:li:image:C5622AQH..." "upload_url": string // The URL that was used for upload (informational) }
Examples:
Use when: "Post an image to LinkedIn" → upload first, then create_post with asset URN
Don't use when: You only want a text post (image upload not needed)
Error Handling:
403 if w_member_social scope is not granted
400 if the author_urn format is invalid
Inputs: author_urn (required), image_base64 (required), mime_type.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 9 tools
Each tool targets a distinct action: creating, updating, deleting, listing, or retrieving posts; retrieving profile or organization data; uploading images. No two tools have overlapping purposes, and descriptions clearly differentiate them.
All tools follow a 'linkedin_verb_noun' pattern with consistent snake_case (e.g., create_post, get_organization, upload_image). No mixed conventions or ambiguous verbs.
9 tools cover the core use cases for LinkedIn post management and organization information without being excessive. The count is well-scoped for the server's purpose.
CRUD operations for posts are fully covered (create, read, update, delete, list), plus profile retrieval, org info, and image upload. Minor gaps exist (e.g., no comment or search functionality), but the surface is solid for the intended domain.
Maintenance
Related MCP Connectors
Draft LinkedIn posts in your voice, then schedule or publish them through LinkedIn's official API.
Save, illustrate, schedule, and publish LinkedIn posts from Claude, ChatGPT, or any MCP client.
Lets Claude and AI agents post and schedule on Instagram, TikTok, LinkedIn and 5 more networks.
Publish and schedule LinkedIn posts from your AI assistant.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables posting text and media content directly to LinkedIn from Claude Desktop with support for authentication and visibility controls.12MIT
- AlicenseAqualityDmaintenanceEnables Claude Desktop to manage your LinkedIn profile and company pages, including posting, reading posts, and fetching profile information.7149 npm4MIT
- AlicenseAqualityDmaintenanceEnables Claude to interact with LinkedIn for posting, commenting, sharing media, and viewing feed and profile.9149 npm3MIT
- AlicenseAqualityDmaintenanceEnables posting content, sharing articles, and managing your LinkedIn profile through Claude using the official LinkedIn API with OAuth 2.0.69 npmMIT