Architecture

Back to mcp-linkedin

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.

Claude Desktop | | stdio (JSON-RPC) v +-----------------+ | MCP Server | src/index.js | (stdio transport) +-----------------+ | v +-----------------+ | Tools | src/tools.js (21 tool handlers) +-----------------+ | | v v +--------+ +------------+ |LinkedIn| | Scheduler | | API | | Storage | |Client | | (JSON file)| +--------+ +------------+ | | HTTPS (REST) v +------------------+ | LinkedIn REST API| | api.linkedin.com | +------------------+

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

  1. Claude Desktop sends call_tool with tool name and arguments over stdio
  2. index.js routes to the matching handler in tools.js
  3. Handler validates input with Zod schema from schemas.js
  4. Handler calls LinkedInAPI methods (e.g., createPost)
  5. linkedin-api.js sends HTTPS request to api.linkedin.com/rest/posts
  6. Response is parsed and returned as structured JSON to Claude Desktop

Media Upload (Image/Video/Document)

  1. Initialize upload via LinkedIn API (get upload URL and media URN)
  2. Read local file and PUT binary data to the upload URL
  3. For videos: finalize upload with ETag from the PUT response
  4. Create post referencing the media URN

Scheduling a Post

  1. linkedin_schedule_post saves post data to JSON file storage
  2. Scheduler daemon (separate process) checks every minute for due posts
  3. Due posts are published via the LinkedIn API
  4. Status updated to published or failed (with retry up to 3 times)

OAuth Authentication

  1. linkedin_get_auth_url starts a local callback server and returns an OAuth relay URL
  2. User visits URL, authenticates with LinkedIn
  3. OAuth relay redirects back to localhost with credentials
  4. Local server receives credentials and stores them in OS keychain
  5. 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:

  1. Environment variablesLINKEDIN_ACCESS_TOKEN and LINKEDIN_PERSON_ID from .env
  2. OS keychain — stored via @napi-rs/keyring after OAuth
  3. 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.