Last updated: January 28, 2026
Overview
mcp-linkedin is a Node.js MCP (Model Context Protocol) server that exposes 21 tools for LinkedIn post management. It communicates with Claude Desktop over stdio and makes REST API calls to LinkedIn on behalf of the authenticated user.
Project Structure
src/index.js — MCP Server Entry Point
Sets up the MCP server with stdio transport. Loads credentials (priority: env vars > OS keychain > legacy file), registers 21 tool definitions with JSON Schema input specs, and routes call_tool requests to the appropriate handler in tools.js. Handles Zod validation errors and LinkedIn API errors uniformly.
src/tools.js — Tool Implementations
Contains all 21 tool handler functions. Each tool validates input via Zod schemas, calls the LinkedIn API client, and returns structured output. Covers content creation (text, link, image, multi-image, video, document, poll), content management (get, update, delete), social interactions (comment, react), scheduling (schedule, list, cancel, get), and authentication (auth URL, save credentials, exchange code, refresh token, user info).
src/linkedin-api.js — LinkedIn REST API Client
HTTP client wrapping Node.js https module. Handles all communication with api.linkedin.com. Sends OAuth bearer tokens and the LinkedIn-Version header (YYYYMM format). Provides methods for posts, images, documents, videos, comments, reactions, and OAuth token exchange/refresh.
src/schemas.js — Zod Validation Schemas
Runtime validation schemas for all tool inputs and outputs, LinkedIn API types (URNs, visibility, distribution), configuration, and error responses. Used by tools.js to validate input before any API call.
src/types.js — JSDoc Type Definitions
JSDoc @typedef declarations for IDE support and documentation. Mirrors the Zod schemas but provides static type hints for development without TypeScript.
src/database.js — Scheduled Posts Storage
JSON file-based storage (~/.mcp-linkedin-scheduled-posts.json) for scheduled posts. No native module dependencies. Provides CRUD operations, due post queries, retry/reschedule support, and status transitions (pending → published/failed/cancelled).
src/scheduler.js — Background Scheduler Daemon
Standalone process using node-cron to check for due posts every minute. Publishes posts via the LinkedIn API client, with retry logic (3 attempts max). Handles graceful shutdown on SIGINT/SIGTERM.
src/auth/ — Authentication Module
Two files handle OAuth: local-server.js starts a local HTTP callback server to receive OAuth credentials automatically, and token-storage.js stores credentials in the OS keychain via @napi-rs/keyring (macOS Keychain, Windows Credential Manager, Linux Secret Service).
Data Flow
Creating a Post
- Claude Desktop sends
call_toolwith tool name and arguments over stdio index.jsroutes to the matching handler intools.js- Handler validates input with Zod schema from
schemas.js - Handler calls
LinkedInAPImethods (e.g.,createPost) linkedin-api.jssends HTTPS request toapi.linkedin.com/rest/posts- Response is parsed and returned as structured JSON to Claude Desktop
Media Upload (Image/Video/Document)
- Initialize upload via LinkedIn API (get upload URL and media URN)
- Read local file and PUT binary data to the upload URL
- For videos: finalize upload with ETag from the PUT response
- Create post referencing the media URN
Scheduling a Post
linkedin_schedule_postsaves post data to JSON file storage- Scheduler daemon (separate process) checks every minute for due posts
- Due posts are published via the LinkedIn API
- Status updated to
publishedorfailed(with retry up to 3 times)
OAuth Authentication
linkedin_get_auth_urlstarts a local callback server and returns an OAuth relay URL- User visits URL, authenticates with LinkedIn
- OAuth relay redirects back to
localhostwith credentials - Local server receives credentials and stores them in OS keychain
- Credentials are set in the current process environment for immediate use
Key Dependencies
| Package | Purpose |
|---|---|
@modelcontextprotocol/sdk |
MCP server framework (stdio transport, request schemas) |
@napi-rs/keyring |
OS keychain access (macOS, Windows, Linux) |
zod |
Runtime input/output validation |
node-cron |
Cron-based scheduler for due post checks |
uuid |
UUID generation for scheduled post IDs |
dotenv |
Environment variable loading from .env |
LinkedIn API Integration
| Endpoint | Method | Purpose |
|---|---|---|
/rest/posts |
POST | Create post (text, link, image, video, document, poll, multi-image) |
/rest/posts?author={urn}&q=author |
GET | Retrieve user's posts (paginated) |
/rest/posts/{urn} |
POST + PARTIAL_UPDATE | Update post (commentary, CTA, landing page) |
/rest/posts/{urn} |
DELETE | Delete post |
/rest/images?action=initializeUpload |
POST | Get image upload URL |
/rest/documents?action=initializeUpload |
POST | Get document upload URL |
/rest/videos?action=initializeUpload |
POST | Get video upload URL |
/rest/videos?action=finalizeUpload |
POST | Finalize video upload |
/rest/socialActions/{urn}/comments |
POST | Add comment to post |
/rest/reactions?actor={urn} |
POST | Add reaction to post |
/v2/userinfo |
GET | Get authenticated user profile |
/oauth/v2/accessToken |
POST | Exchange code / refresh token |
All requests include Authorization: Bearer {token}, LinkedIn-Version: YYYYMM, and X-Restli-Protocol-Version: 2.0.0 headers. OAuth scope: w_member_social.
Credential Storage
Credentials are resolved in priority order:
- Environment variables —
LINKEDIN_ACCESS_TOKENandLINKEDIN_PERSON_IDfrom.env - OS keychain — stored via
@napi-rs/keyringafter OAuth - Legacy file —
~/.mcp-linkedin-credentials.json(backward compatibility)
Testing
135 tests in __tests__/ using Jest. Tests cover all tool handlers, schema validation, database operations, scheduler logic, and API client methods using mocked HTTP responses.
npm test— run all testsnpm run test:watch— watch modenpm run test:coverage— coverage report