MCP LinkedIn Poster (TypeScript)
An MCP (Model Context Protocol) server that allows ChatGPT / Claude Desktop to post content to LinkedIn using the official LinkedIn UGC Posts API and an OAuth 2.0 access token.
This server runs locally and is designed to be registered in your ChatGPT / Claude MCP server settings.
What this does
- Runs an MCP server over stdio
- Accepts post requests from ChatGPT / Claude
- Publishes text posts to your LinkedIn profile
- Uses LinkedIn OAuth (3-legged) authentication
- Reads secrets securely via environment variables
Project structure
mcp-linkedin-poster/ ├─ src/ │ └─ index.ts # main MCP server (TypeScript source) ├─ dist/ │ └─ index.js # compiled output (what Node actually runs) ├─ .env # environment variables (DO NOT COMMIT) ├─ package.json ├─ tsconfig.json └─ README.md
yaml Copy code
What is dist?
dist stands for distribution.
It contains the compiled JavaScript output generated from TypeScript.
You edit src/index.ts
You run dist/index.js
Install
npm install
Build
Compile TypeScript → JavaScript:
bash
Copy code
npx tsc
This generates:
bash
Copy code
dist/index.js
Run locally (sanity check)
bash
Copy code
node dist/index.js
If environment variables are missing you will see:
bash
Copy code
Missing env LINKEDIN_ACCESS_TOKEN
Missing env LINKEDIN_AUTHOR_URN
That is expected until setup is complete.
Environment variables (.env)
Create a file named .env in the project root:
env
Copy code
LINKEDIN_ACCESS_TOKEN=
LINKEDIN_AUTHOR_URN=
LINKEDIN_VERSION=202502
What each variable is
Variable Description
LINKEDIN_ACCESS_TOKEN OAuth 2.0 member access token from LinkedIn
LINKEDIN_AUTHOR_URN Your LinkedIn profile URN (urn:li:person:...)
LINKEDIN_VERSION LinkedIn API version header (optional, safe to keep)
⚠️ Never commit .env
Add it to .gitignore.
How to get LINKEDIN_ACCESS_TOKEN
Go to https://www.linkedin.com/developers/
Open your LinkedIn app
Use OAuth 2.0 → Token Generator
Select scope:
w_member_social (required for posting)
Generate token
Copy it as a single line
Paste into .env:
env
Copy code
LINKEDIN_ACCESS_TOKEN=PASTE_TOKEN_HERE
How to get LINKEDIN_AUTHOR_URN
Method (recommended): LinkedIn userinfo endpoint
Curl
bash
Copy code
curl -X GET "https://api.linkedin.com/v2/userinfo" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"
Example response:
json
Copy code
{
"sub": "8qD1tr1-1m",
"name": "Your Name"
}
Your author URN becomes:
env
Copy code
LINKEDIN_AUTHOR_URN=urn:li:person:8qD1tr1-1m
Windows PowerShell version
powershell
Copy code
$token="YOUR_ACCESS_TOKEN"
Invoke-RestMethod `
-Uri "https://api.linkedin.com/v2/userinfo" `
-Headers @{ Authorization = "Bearer $token" }
Example: LinkedIn post via curl (UGC API)
bash
Copy code
curl -X POST "https://api.linkedin.com/v2/ugcPosts" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Restli-Protocol-Version: 2.0.0" \
-H "Content-Type: application/json" \
-d '{
"author": "urn:li:person:YOUR_PERSON_ID",
"lifecycleState": "PUBLISHED",
"specificContent": {
"com.linkedin.ugc.ShareContent": {
"shareCommentary": { "text": "Hello from MCP 🚀" },
"shareMediaCategory": "NONE"
}
},
"visibility": {
"com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
}
}'
A 201 Created response means success.
Registering this server in ChatGPT / Claude MCP
Add this to your MCP config:
json
Copy code
{
"mcpServers": {
"linkedin_poster": {
"command": "node",
"args": [
"C:\\Users\\User\\Desktop\\mcp-linkedin-poster\\dist\\index.js"
],
"env": {
"LINKEDIN_ACCESS_TOKEN": "PASTE_TOKEN_HERE",
"LINKEDIN_AUTHOR_URN": "urn:li:person:PASTE_ID_HERE",
"LINKEDIN_VERSION": "202502"
}
}
}
}
Restart ChatGPT / Claude Desktop after saving.
Common errors & fixes
❌ Missing env LINKEDIN_ACCESS_TOKEN
.env not created
MCP config missing env
Token pasted with line breaks
❌ Invalid OAuth 2.0 Access Token (code 190)
Token expired
Wrong app
Missing w_member_social scope
Regenerate token.
❌ PowerShell header error
Use a hashtable, not a string:
powershell
Copy code
-Headers @{ Authorization = "Bearer $token" }
❌ Node cannot find dist/index.js
Run commands from the project root, not src/.
Correct:
bash
Copy code
node dist\index.js
Wrong:
bash
Copy code
node src\dist\index.js
Security notes
Treat LinkedIn tokens like passwords
Do not commit secrets
Prefer MCP environment injection over .env in production
**
for CLAUDE
"linkedin_poster": {
"command": "node",
"args": [
"C:\\Users\\User\\Desktop\\mcp-linkedin-poster\\dist\\index.js"
],
"env": {
"LINKEDIN_ACCESS_TOKEN": "",
"LINKEDIN_AUTHOR_URN": "urn:li:person:",
"LINKEDIN_VERSION": "202502"