linkedin-mcp
Provides a Telegram bot interface for administering the LinkedIn MCP server, including commands for status, health, logs, token management, and credential management.
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-mcppost a text update about my new blog post"
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
0 Trust · 100% Control | 0 Magic · 100% Transparency | 0 Hardcoding · 100% Flexibility
A Model Context Protocol (MCP) server for LinkedIn — official OAuth 2.0, intent-first tool surface, three-level admin (CLI · HTTP · Telegram). Designed for the KpihX homelab stack; deployment-ready with Docker + Traefik.
Why this exists
LinkedIn has no general-purpose developer API for personal accounts. The official OAuth surface (openid profile email w_member_social) covers everything an individual power-user needs:
Post, delete, like, comment on content
Read own profile and auth status
Guide agents through the flow with
linkedin_guide
This MCP wraps that surface with full operational visibility: a CLI admin, an HTTP admin API, and an optional Telegram bot — the same architecture as whats-mcp, tick-mcp, mail-mcp.
Related MCP server: @isteam/linkedin-mcp
Architecture
linkedin-mcp/
├── src/
│ ├── main.js ← entry: serve (stdio) | serve-http
│ ├── admin.js ← entry: CLI admin (linkedin-admin)
│ ├── config.js ← loadConfig() — deep merge defaults → config.json → env
│ ├── token.js ← save / load / clear / summary (~/.mcps/linkedin/token.json)
│ ├── client.js ← LinkedInClient — official REST API calls
│ ├── server.js ← createMcpServer() — MCP SDK wiring
│ ├── http_app.js ← Express HTTP app: /health /admin/* /mcp
│ ├── .env.example ← required secrets template
│ └── admin/
│ ├── cli.js ← Commander CLI (auth / status / logout / logs / health / urls
│ │ / client-id / client-secret / token)
│ ├── service.js ← unified admin kernel — single source of truth for all surfaces
│ ├── oauth.js ← LinkedIn OAuth 2.0 + OIDC flow (configurable callback port)
│ └── telegram.js ← optional Telegram bot admin (15 commands)
├── tools/
│ ├── registry.js ← listTools() + callTool()
│ ├── guide.js ← linkedin_guide
│ ├── profile.js ← linkedin_get_profile, linkedin_auth_status
│ ├── posts.js ← linkedin_create_post, linkedin_create_image_post, linkedin_delete_post
│ └── social.js ← linkedin_like_post, linkedin_create_comment
├── tests/ ← bun test (61 tests, 0 deps on live API)
├── deploy/
│ ├── docker-compose.yml
│ └── docker-compose.override.example.yml
├── Dockerfile
├── .gitlab-ci.yml
└── config.json ← non-secret defaults (port 8095, OAuth port 3001, state dir, scopes)Transport strategy:
Agent / Claude / Gemini / Codex / Copilot / Vibe
│
├─── HTTP (homelab) ──→ https://linkedin.kpihx-labs.com/mcp (primary)
└─── stdio (local) ──→ ~/.bun/bin/linkedin-mcp serve (fallback)Tools (8)
Tool | Description |
| Orientation — all tools, auth flow, quick start |
| Token presence, validity, days remaining, profile |
| Fetch own LinkedIn profile (name, email, sub) |
| Create a text post (PUBLIC or CONNECTIONS) |
| Create a post with an attached image |
| Delete a post by URN |
| Like a post by URN |
| Add a comment to a post by URN |
Admin interfaces
CLI (linkedin-admin)
# Authentication
linkedin-admin auth [--port N] # OAuth flow — opens browser, saves token (default port 3001)
linkedin-admin token set <value> # Set access token directly (fetches profile, no browser)
linkedin-admin token unset # Clear stored token (same as logout)
linkedin-admin logout # Clear stored token
# Credential management (stored in admin env file)
linkedin-admin client-id set <v> # Set LINKEDIN_CLIENT_ID
linkedin-admin client-id unset # Clear LINKEDIN_CLIENT_ID
linkedin-admin client-secret set <v>
linkedin-admin client-secret unset
# Status & observability
linkedin-admin status # Credentials table + token summary
linkedin-admin status --json # Machine-readable JSON
linkedin-admin logs [--lines 50] # Tail admin log
linkedin-admin health # HTTP server reachability
linkedin-admin urls # All public/private endpointsHTTP admin
Endpoint | Method | Description |
| GET | Liveness probe |
| GET | Full runtime status (token + Telegram state) |
| GET | All commands reference |
| GET | Recent admin log tail |
| POST | Body: |
| POST | Clear LINKEDIN_CLIENT_ID |
| POST | Body: |
| POST | Clear LINKEDIN_CLIENT_SECRET |
| POST | Body: |
| POST | Clear stored token |
| POST/GET/DELETE | MCP Streamable HTTP transport |
Telegram bot (optional)
Set TELEGRAM_LINKEDIN_HOMELAB_TOKEN + TELEGRAM_CHAT_IDS — server polls every 5 s.
Command | Action |
| Full command reference |
| Credentials + token status |
| HTTP server health |
| Endpoint map |
| Last n admin log lines (default 20) |
| Set access token (fetches profile) |
| Clear stored token |
| Set LINKEDIN_CLIENT_ID |
| Clear LINKEDIN_CLIENT_ID |
| Set LINKEDIN_CLIENT_SECRET |
| Clear LINKEDIN_CLIENT_SECRET |
Quick start
1. LinkedIn App setup
Create an app → add products: "Sign In with LinkedIn using OpenID Connect" + "Share on LinkedIn"
Set redirect URI:
http://localhost:3001/callbackNote
Client IDandClient Secret
2. Configure secrets
# Option A — via admin CLI (written to admin env file)
linkedin-admin client-id set <your_client_id>
linkedin-admin client-secret set <your_client_secret>
# Option B — via .env file
cp src/.env.example src/.env
# Fill in LINKEDIN_CLIENT_ID and LINKEDIN_CLIENT_SECRET
# Option C — via bw-env / kshrc (injected at login shell)3. Install & authenticate
bun install
bun link # editable install: ~/.bun/bin/linkedin-mcp + linkedin-admin
# Full OAuth flow (browser consent)
linkedin-admin auth # default port 3001
linkedin-admin auth --port 3002 # if 3001 is busy
# Or set access token directly (server/headless contexts)
linkedin-admin token set <your_access_token>
linkedin-admin status # verify credentials and token4. Start MCP server
# stdio (for agent config)
linkedin-mcp serve
# HTTP (for homelab deployment)
linkedin-mcp serve-httpDocker / Homelab deployment
cd deploy
cp docker-compose.override.example.yml docker-compose.override.yml
# Edit override with your secrets (or rely on CI env injection)
docker compose up -dTraefik labels in deploy/docker-compose.yml expose:
https://linkedin.kpihx-labs.com(primary private trusted route)https://linkedin.homelab(fallback)
The container persists all state in /data (Docker volume):
/data/state/token.json— OAuth token/data/linkedin-admin.env— credentials written bytoken set/client-id set
Agent registration
Claude Code (~/.claude.json)
"linkedin-mcp": {
"type": "http",
"url": "https://linkedin.kpihx-labs.com/mcp"
},
"linkedin-mcp--fallback": {
"command": "/home/kpihx/.bun/bin/linkedin-mcp",
"args": ["serve"]
}Gemini (~/.gemini/extensions/linkedin_mcp/gemini-extension.json)
{
"name": "linkedin-mcp",
"version": "0.2.1",
"mcpServers": {
"linkedin-mcp": { "httpUrl": "https://linkedin.kpihx-labs.com/mcp" },
"linkedin-mcp--fallback": {
"command": "/home/kpihx/.bun/bin/linkedin-mcp",
"args": ["serve"]
}
}
}Codex (~/.codex/config.toml)
[mcp_servers.linkedin_mcp]
url = "https://linkedin.kpihx-labs.com/mcp"
[mcp_servers.linkedin_mcp_fallback]
command = "/home/kpihx/.bun/bin/linkedin-mcp"
args = ["serve"]Copilot (~/.copilot/mcp-config.json)
"linkedin_mcp": { "type": "http", "url": "https://linkedin.kpihx-labs.com/mcp" },
"linkedin_mcp_fallback": {
"type": "stdio",
"command": "/home/kpihx/.bun/bin/linkedin-mcp",
"args": ["serve"]
}Vibe (~/.vibe/config.toml)
[[mcp_servers]]
name = "linkedin"
transport = "http"
url = "https://linkedin.kpihx-labs.com/mcp"
[[mcp_servers]]
name = "linkedin_fallback"
transport = "stdio"
command = "/home/kpihx/.bun/bin/linkedin-mcp"
args = ["serve"]Tests
bun test # 61 tests, 5 files, 0 live API calls
bun test --watch # watch modeAll tests use mocked fetch and temp directories — no credentials needed.
Token lifecycle
LinkedIn personal OAuth tokens expire in 60 days. There is no refresh token for non-Partner apps.
Day 0 → linkedin-admin auth (OAuth flow, browser consent)
OR linkedin-admin token set (direct token, headless/server)
Day 55+ → linkedin-admin status (shows days_left)
Day 60 → token invalid, re-run authBoth flows produce a complete token.json with member_id populated — required for all posting tools.
License
MIT — see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
- LinkMCPOAuthio.linkmcp
Hosted MCP server for LinkedIn: 31 tools for profiles, search, messaging, posts, enrichment.
Managed LinkedIn MCP server for AI agents: search, connect, message and enrich on accounts you own.
MCP server for LeadDelta — manage LinkedIn connections and CRM data via AI assistants.
LinkedIn API as MCP tools to retrieve profile data and publish content. Powered by HAPI MCP.
Related MCP Servers
- AlicenseAqualityDmaintenanceFully featured MCP server that provides automation tools for LinkedIn, supporting browser-based scraping and API-based operations for content management, media uploads, and reactions.63MIT
- AlicenseAqualityDmaintenanceMCP server for LinkedIn that enables AI agents to post, comment, like, and manage content on LinkedIn.106 npm1MIT
- AlicenseNot gradedqualityDmaintenanceMCP server for posting to LinkedIn, managing drafts, scheduling, commenting, and reacting, all from the terminal.84 npmMIT
- FlicenseNot gradedqualityCmaintenanceMCP server for LinkedIn API integration. Enables authentication, profile access, connections, search, messaging, and feed management via OAuth2.-