linkedin-mcp
by egig
README.md
# linkedin-mcp
A local MCP (Model Context Protocol) server that lets Claude log in to LinkedIn
and publish image posts to your personal profile feed, backed by LinkedIn's
REST Images API and Posts API.
Scope: two tools, `linkedin_login` and `create_image_post`. No org/company
page posting, no scheduling, no carousels.
## 1. Create a LinkedIn Developer App
1. Go to https://www.linkedin.com/developers/apps and click **Create app**.
LinkedIn requires every app to be linked to a **LinkedIn Page**, even for
personal-profile posting -- create a minimal Page first if you don't have
one.
2. Fill in the app name, select the Page, upload a logo (required), accept the
agreement, and click **Create app**.
3. In the **Products** tab, add:
- **Sign In with LinkedIn using OpenID Connect** (grants `openid`,
`profile`, `email`) -- self-serve, instant.
- **Share on LinkedIn** (grants `w_member_social`) -- self-serve, instant.
4. In the **Auth** tab, under OAuth 2.0 settings, add an **Authorized redirect
URL** that exactly matches `LINKEDIN_REDIRECT_URI` below (default
`http://localhost:3000/callback`, including the path -- must match
byte-for-byte).
5. Copy the **Client ID** and **Client Secret** from the Auth tab.
**Known risk to watch for:** the Microsoft Learn docs for `/rest/images` and
`/rest/posts` live under "Community Management API," which normally needs a
separate LinkedIn approval. Posting to your own profile with `w_member_social`
+ OpenID scopes has historically worked without that approval, but if your
first `create_image_post` call comes back with a 403, that's the signal your
app needs the **Community Management API** product added in the developer
portal (approval-gated, outside this code's control).
## 2. Install and build
```bash
npm install
npm run build
```
## 3. Configure your MCP client
### Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"linkedin": {
"command": "node",
"args": ["/absolute/path/to/linkedin-mcp/dist/index.js"],
"env": {
"LINKEDIN_CLIENT_ID": "xxxx",
"LINKEDIN_CLIENT_SECRET": "xxxx",
"LINKEDIN_REDIRECT_URI": "http://localhost:3000/callback"
}
}
}
}
```
### Claude Code
Add the same shape to your project or user `.mcp.json`, or run:
```bash
claude mcp add linkedin node /absolute/path/to/linkedin-mcp/dist/index.js \
--env LINKEDIN_CLIENT_ID=xxxx \
--env LINKEDIN_CLIENT_SECRET=xxxx \
--env LINKEDIN_REDIRECT_URI=http://localhost:3000/callback
```
Restart the client after editing config. You should see the server connected
with two tools: `linkedin_login` and `create_image_post`.
## 4. Environment variables
See [.env.example](.env.example) for the full list. Only `LINKEDIN_CLIENT_ID`
and `LINKEDIN_CLIENT_SECRET` are required; everything else has a sane default.
Tokens are persisted to `~/.linkedin-mcp/tokens.json` (mode `600`) so you only
need to log in once per ~60-day LinkedIn access token lifetime.
## 5. Usage
In a Claude conversation:
- "Log in to LinkedIn" -> calls `linkedin_login`, opens your browser, and
completes the OAuth flow. Calling it again when already logged in is a
no-op unless you pass `force: true`.
- "Post this image to LinkedIn with caption ..." -> calls `create_image_post`
with `imagePath` or `imageUrl`, `text`, and optionally `altText` /
`visibility`.
## Development
```bash
npm run dev # run directly via tsx, without building
```
All logging goes to stderr (`console.error`) -- stdout is reserved for the
MCP JSON-RPC protocol stream over stdio.
TDQS
A4.2/5.0
Scored across 2 tools
Disambiguation5/5
The two tools have completely distinct purposes: one handles authentication, the other handles posting. There is no overlap or ambiguity between them.
Naming Consistency3/5
Naming conventions are inconsistent: 'create_image_post' follows a verb_noun pattern while 'linkedin_login' follows a noun_noun pattern. However, both names are clear and understandable.
Tool Count3/5
With only 2 tools, the server feels thin for a social media platform, but it may be intentionally scoped to image posting only. The count is borderline acceptable.
Completeness3/5
The server covers the basic workflow of authentication and posting an image, but lacks operations like updating, deleting, or retrieving posts. Notable gaps exist for a complete CRUD surface.
Maintenance
ActivityInactive
ResponsivenessNo issues