linkedin-mcp
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 post a LinkedIn update about our new product launch"
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 Server
MCP server for LinkedIn API integration — profiles, posts, feed, and connections.
Works with Claude Desktop, Cursor, Windsurf, Claude Code, OpenCode, and any MCP-compatible client.
Features
Tool | Description | Scope Required |
| Get OpenID Connect user info |
|
| Get full LinkedIn profile |
|
| Publish a LinkedIn post |
|
| List your/others' posts |
|
| Delete a LinkedIn post |
|
| Get your feed activity |
|
| Get your connections | Partner API |
| Send a direct message | Partner API |
| Search LinkedIn members | Partner API |
| OAuth PKCE login flow | — |
Related MCP server: LinkedIn MCP for OpenWorker
Quick Start
# Install from npm (recommended)
npx @eggisatriadev/linkedin-mcp
# Or install locally
npm install @eggisatriadev/linkedin-mcp
# Set your access token
export LINKEDIN_ACCESS_TOKEN=AQX_your_token_here
# Run the server
npx @eggisatriadev/linkedin-mcpFor Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"linkedin": {
"command": "npx",
"args": ["-y", "@eggisatriadev/linkedin-mcp"],
"env": {
"LINKEDIN_ACCESS_TOKEN": "AQX_your_token_here"
}
}
}
}For OpenCode
Add to your opencode.json:
{
"mcpServers": {
"linkedin": {
"command": "npx",
"args": ["-y", "@eggisatriadev/linkedin-mcp"],
"env": {
"LINKEDIN_ACCESS_TOKEN": "AQX_your_token_here"
}
}
}
}Setup
1. Create a LinkedIn Developer App
Create a new app
Add products:
Sign In with LinkedIn using OpenID Connect (auto-approved)
Share on LinkedIn (for posting capabilities)
Note your Client ID and Client Secret
Add
http://localhost:8080as an OAuth redirect URL
2. Generate Access Token
The easiest way is to use the OAuth 2.0 token generator in the LinkedIn Developer Portal:
Go to your app → Auth tab
In OAuth 2.0 settings, find the access token section
Select scopes:
openid,profile,email,w_member_socialGenerate and copy the token
3. Run the Server
# Install dependencies
npm install
# Set your token
export LINKEDIN_ACCESS_TOKEN=AQX_your_token_here
# Build and run
npm run build
npm start4. Connect to Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"linkedin": {
"command": "node",
"args": ["/absolute/path/to/linkedin-mcp-server/dist/index.js"],
"env": {
"LINKEDIN_ACCESS_TOKEN": "AQX_your_token_here"
}
}
}
}5. Connect to OpenCode
Add to your opencode.json (or use the MCP config):
{
"mcpServers": {
"linkedin": {
"command": "node",
"args": ["/absolute/path/to/linkedin-mcp-server/dist/index.js"],
"env": {
"LINKEDIN_ACCESS_TOKEN": "AQX_your_token_here"
}
}
}
}OAuth PKCE Login (No Token Required)
Skip step 2 — let the server handle authentication interactively:
# Required: set your LinkedIn app credentials
export LINKEDIN_CLIENT_ID=your_client_id
export LINKEDIN_CLIENT_SECRET=your_client_secret
# Run the server (it will detect no token and guide you)
npm run build
npm start
# Or from npm package:
npx @eggisatriadev/linkedin-mcpThen call the linkedin_oauth_login tool from your MCP client:
port (optional): Callback server port (default:
8080)open_browser (optional): Auto-open browser (default:
true)timeout (optional): Max wait in ms (default:
120000)
The flow:
Server starts a local HTTP server on port 8080
Opens your browser to LinkedIn's authorization page
You approve the request
LinkedIn redirects to localhost — server captures the code
Server exchanges the code for an access token via PKCE S256
Token is saved to
~/.config/linkedin-mcp/config.jsonAll tools immediately work without further setup
Media Upload
LinkedIn supports image upload via a 3-step /rest/images flow:
Initialize:
POST /rest/images?action=initializeUpload→ returnsuploadUrl+imageURNUpload binary:
PUT {uploadUrl}with image dataAttach to post: Use the image URN as
media.idincreatePost()
Supported formats: JPEG, PNG, GIF (static) Max file size: 10 MB Recommended dimensions: 2048×2048px
Image upload happens automatically when you pass a media_url parameter
to linkedin_create_post. The MediaUploader handles retries on expired
upload URLs and validates format/size before uploading.
Development
# Watch mode (auto-reload)
npm run dev
# Build
npm run build
# Test with MCP Inspector
npm run inspectorArchitecture
The server follows a layered architecture with four bounded contexts:
linkedin-mcp-server/
├── src/
│ ├── index.ts # Entry point: server init, tool registration, health check
│ ├── types.ts # Shared types and enums
│ ├── services/
│ │ └── linkedin-client.ts # LinkedIn API client (14+ methods, error classification)
│ ├── tools/
│ │ ├── profile.ts # Profile tools: getMyProfile, getUserInfo
│ │ ├── posts.ts # Posts tools: createPost, listPosts, deletePost
│ │ ├── network.ts # Network tools: getFeed, getConnections, sendMessage, searchPeople
│ │ └── auth.ts # Auth tool: oauthLogin
│ ├── auth/
│ │ ├── oauth.ts # PKCE OAuth 2.0 flow (RFC 7636)
│ │ ├── config.ts # Config file management
│ │ └── token-store.ts # Token persistence (env var → config file fallback)
│ └── media/
│ └── uploader.ts # 3-step image upload: init → binary → URN
├── tests/
│ ├── linkedin-client.test.ts
│ ├── tools/
│ ├── auth/
│ └── media/
├── docs/
│ └── pocket/arch/*/tech-design.md # Full technical design document
├── package.json
├── tsconfig.json
└── README.mdToken Resolution Chain
LINKEDIN_ACCESS_TOKENenv var (highest priority)~/.config/linkedin-mcp/config.json(persistent local token)OAuth PKCE flow (if
LINKEDIN_CLIENT_ID+LINKEDIN_CLIENT_SECRETare set)
See tech-design.md for the full architecture, API contracts, data flow diagrams, and ADRs.
Contributing
We welcome contributions! See CONTRIBUTING.md for:
Bug reports & feature requests (GitHub Issues)
Development setup guide
Coding standards & test requirements
Pull request guidelines
This project adheres to a Code of Conduct.
Limitations
Connections API: Requires LinkedIn Partner Program (not available on free tier)
People Search: Not available via public API (needs Sales Navigator)
Messaging API: Requires LinkedIn Messaging API (partner program)
Feed: Limited by API restrictions
Rate Limits: 100-500 requests/day per app on free tier
License
MIT — see LICENSE for details.
Available Tools
10 toolslinkedin_create_postCreate a LinkedIn PostA
Create and publish a new LinkedIn post.
Publishes a text post to your LinkedIn feed. Supports PUBLIC, CONNECTIONS-only, or LOGGED_IN visibility settings.
Args:
text (string, required): Post content (1-3000 chars)
visibility ("PUBLIC" | "CONNECTIONS" | "LOGGED_IN"): Who can see the post (default: "PUBLIC")
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns (JSON format): { "postId": string, // Post ID "urn": string, // LinkedIn URN "text": string, // Text preview (first 100 chars) "visibility": string, // Visibility setting used "status": string, // Always "PUBLISHED" "url": string // Direct link to the post }
Requires scope: w_member_social (Share on LinkedIn product)
Inputs: no named parameters.
linkedin_delete_postDelete a LinkedIn PostADestructive
Permanently delete a LinkedIn post by its ID or URN.
⚠️ DESTRUCTIVE — This action cannot be undone.
Args:
post_id (string, required): Post ID or URN (e.g., 'post-123' or 'urn:li:share:post-123')
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns (JSON format): { "postId": string, // The ID that was deleted "status": "DELETED", // Always "DELETED" "note": string // Warning about permanence }
Requires scope: w_member_social
Inputs: post_id (required), response_format.
linkedin_get_connectionsGet LinkedIn ConnectionsARead-onlyIdempotent
Get the authenticated user's LinkedIn connections.
Lists your 1st-degree connections with pagination support.
Args:
start (number): Start index for pagination (default: 0)
count (number): Number of connections to return, max 50 (default: 10)
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns (JSON format): { "total": number, "count": number, "start": number, "has_more": boolean, "connections": [{ "id": string, // Member ID "name": string, // Display name "profileUrl": string // LinkedIn profile URL }] }
⚠️ NOTE: Connections API requires LinkedIn Partner Program access. Standard OAuth apps will receive an error message explaining the limitation.
Inputs: count, start, response_format.
linkedin_get_feedGet LinkedIn FeedARead-onlyIdempotent
Get recent activity from the authenticated user's LinkedIn feed.
Returns recent posts and activities from your network.
Args:
start (number): Start index for pagination (default: 0)
count (number): Number of items to return, max 50 (default: 10)
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns (JSON format): { "total": number, "count": number, "start": number, "has_more": boolean, "items": [{ "id": string, // Activity URN "actor": string, // Actor URN "time": string, // Human-readable timestamp "timestamp": number,// Unix timestamp ms "content": string // Content preview }] }
Note: LinkedIn's public API has limited feed access.
Inputs: count, start, response_format.
linkedin_get_my_profileGet My LinkedIn ProfileARead-onlyIdempotent
Get the authenticated user's LinkedIn profile information.
Returns your full profile details including name, headline, vanity URL, profile picture, email, and locale. Combines both the OpenID Connect userinfo endpoint and the LinkedIn profile API.
Args:
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns (JSON format): { "id": string, // LinkedIn member ID "firstName": string, // First name "lastName": string, // Last name "fullName": string, // Full display name "headline": string, // Professional headline "vanityName": string, // Custom URL identifier "profileUrl": string, // Full LinkedIn URL "email": string, // Email address (if available) "pictureUrl": string, // Profile picture URL "locale": string // Locale setting }
Requires scope: openid + r_liteprofile (or r_basicprofile)
Inputs: response_format.
linkedin_get_user_infoGet LinkedIn User Info (OpenID Connect)ARead-onlyIdempotent
Get basic user identity information via OpenID Connect.
This works with the minimal "openid" scope and returns:
sub (unique member ID for your app)
name, given_name, family_name
email (if "email" scope is granted)
picture
locale
Unlike getMyProfile, this always works even with minimal scopes.
Args:
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns (JSON format): { "sub": string, // Unique member ID "name": string, // Full name "givenName": string, // First name "familyName": string, // Last name "email": string, // Email address "emailVerified": boolean // Whether email is verified "picture": string, // Profile picture URL "locale": string // Locale }
Requires scope: openid (always available with Sign In with LinkedIn)
Inputs: response_format.
linkedin_list_postsList LinkedIn PostsARead-onlyIdempotent
List LinkedIn posts for a member.
Returns posts published by the specified member (or the authenticated user by default). Posts are returned newest-first with pagination support.
Args:
member_id (string, optional): LinkedIn member ID (sub) to fetch posts for
start (number): Start index for pagination (default: 0)
count (number): Number of posts to return, max 50 (default: 10)
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Returns (JSON format): { "total": number, // Total posts available "count": number, // Posts in this response "start": number, // Current offset "has_more": boolean, // Whether more pages exist "next_offset": number, // Offset for next page "posts": [{ "id": string, // Post ID "text": string, // Post commentary/text "author": string, // Author URN "created": string, // Human-readable date "created_timestamp": number, // Unix timestamp ms "visibility": string,// Visibility setting "url": string // Direct link }] }
Requires scope: r_member_social or r_organization_social
Inputs: count, start, member_id, response_format.
linkedin_oauth_loginLogin to LinkedIn via OAuthA
Authenticate with LinkedIn using OAuth PKCE flow.
Opens your browser to LinkedIn's authorization page, captures the callback, exchanges the authorization code for an access token, and saves it to the config file for future use.
This is the recommended way to authenticate. Once complete, all other tools can use the saved token automatically.
Prerequisites:
LINKEDIN_CLIENT_ID environment variable set
LINKEDIN_CLIENT_SECRET environment variable set
A LinkedIn Developer App with "Sign In with LinkedIn using OpenID Connect" and "Share on LinkedIn" products configured
Args:
port (number, 1024-65535): Localhost port for the OAuth callback (default: 8080)
open_browser (boolean): Auto-open browser (default: true). Set false for headless.
timeout (number, 30000-600000): Max wait for callback in ms (default: 120000)
Returns: Confirmation message with token expiry and granted scopes.
Inputs: port, timeout, open_browser.
linkedin_search_peopleSearch LinkedIn People (Not Available)ARead-onlyIdempotent
Search for LinkedIn members by keywords.
⚠️ NOTE: This tool is NOT available with standard OAuth apps. People search requires LinkedIn Sales Navigator API or a third-party provider.
This tool returns a clear error message explaining the limitation.
Args:
keywords (string, required): Search keywords (name, title, company, etc.)
start (number): Start index for pagination (default: 0)
count (number): Number of results (max 50, default: 10)
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Inputs: keywords (required), count, start, response_format.
linkedin_send_messageSend a LinkedIn Message (Partner API)A
Send a direct message to a LinkedIn member.
⚠️ NOTE: This tool is NOT available with standard OAuth apps. Direct messaging requires LinkedIn's Messaging API (partner program).
This tool returns a clear error message explaining the limitation.
Args:
recipient_id (string, required): LinkedIn member ID of the recipient
text (string, required): Message text (1-2000 chars)
response_format ('markdown' | 'json'): Output format (default: 'markdown')
Inputs: text (required), recipient_id (required), response_format.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 10 tools
Most tools clearly target distinct actions and resources, but linkedin_get_my_profile and linkedin_get_user_info overlap in profile identity purposes, and linkedin_list_posts vs linkedin_get_feed could be confused as both return content. The unavailable tools are clearly labeled with limitation messages, which reduces ambiguity.
All tools follow a consistent linkedin_verb_noun pattern with snake_case throughout, e.g., create_post, list_posts, delete_post, get_feed. The oauth_login tool fits the same pattern and does not introduce stylistic inconsistency.
Ten tools is a reasonable size for a LinkedIn MCP server and the scope is understandable. However, three tools (send_message, get_connections, search_people) are non-functional placeholders for standard OAuth users, slightly reducing the effective surface.
The server covers authentication, profile retrieval, post creation, listing, and deletion, but lacks post updating, comments, reactions, and other common LinkedIn interactions. Several advertised tools are unavailable without partner access, leaving notable gaps in messaging, connections, and search.
Maintenance
Related MCP Connectors
LinkedIn API as MCP tools to retrieve profile data and publish content. Powered by HAPI MCP.
Manage LinkedIn messages, connections, posts, and profile data through Linked API.
Managed LinkedIn MCP server for AI agents: search, connect, message and enrich on accounts you own.
LinkedIn Ads MCP server: run LinkedIn campaigns, ad groups, ads and reports from AI.
1
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP server for LinkedIn API integration. Enables authentication, profile access, connections, search, messaging, and feed management via OAuth2.MIT
- AlicenseAqualityDmaintenanceEnables LinkedIn profile reads and post operations through a local-first MCP server, with OAuth token management and approval-friendly tools for OpenWorker.810 npm1MIT
- AlicenseAqualityBmaintenanceEnables MCP-compatible clients to read LinkedIn profiles, companies, and jobs, and to send connection requests after explicit confirmation, using interactive browser-based authentication instead of stored credentials.8MIT
- AlicenseNot gradedqualityCmaintenanceEnables publishing explicitly approved text posts to LinkedIn and checking connection status via a stateless MCP connector.Apache 2.0