Skip to main content
Glama
selvin-paul-raj

LinkedIn MCP Server

LinkedIn MCP Server

A production-quality Model Context Protocol server that exposes LinkedIn actions (post, comment, search, connections, messaging, and anything else Composio's LinkedIn toolkit adds) to Claude and ChatGPT. Built for personal, single-user use with Composio as the auth/action layer, deployed on Alpic.

Features

  • Fully dynamic tool discovery — every LinkedIn action Composio exposes is registered as an MCP tool at startup and refreshed on demand via linkedin_mcp_list_tools. No tool names are hardcoded.

  • Automatic LinkedIn auth — on startup, checks whether the fixed user is connected; if not, starts the OAuth flow, logs the authorization URL, and waits (bounded timeout) for the connection to complete.

  • Structured logging — JSON logs via structlog, rotating log file under logs/, one line per startup, shutdown, tool call (with latency and outcome), and exception.

  • Local stats tracking — total/successful/failed calls, average latency, last execution, startup time, and uptime, persisted to a local JSON file (linkedin_mcp_health tool, or read STATS_FILE directly).

  • Health reportinglinkedin_mcp_health reports server, LinkedIn connection, and Composio reachability.

  • Never crashes — every Composio/network/auth failure is caught and returned as a normal (but error-flagged) tool result, never an unhandled exception.

  • Streamable-HTTP transport, binding PORT/HOST — ready for Alpic's hosting model.

Related MCP server: LinkedIn MCP Server

Architecture

src/
├── server.py           # FastMCP app + entrypoint; wires everything together
├── composio_client.py  # Thin wrapper around the Composio SDK (LinkedIn toolkit only)
├── tool_registry.py    # Dynamic MCP tool list/dispatch: static utility tools + every LinkedIn action
├── config.py           # Settings loaded from environment / .env
├── logger.py           # structlog + rotating file handler setup
├── stats.py            # JSON-backed call statistics tracker
├── health.py           # Health status aggregation
├── models.py            # Shared Pydantic models
└── utils.py             # Retry/backoff decorator, Timer

The four static tools (linkedin_mcp_ping, linkedin_mcp_health, linkedin_mcp_version, linkedin_mcp_list_tools) are always available. Every other tool name is a Composio LinkedIn action slug (e.g. LINKEDIN_CREATE_LINKED_IN_POST, LINKEDIN_GET_MY_INFO, LINKEDIN_CREATE_COMMENT_ON_POST, ...) — call linkedin_mcp_list_tools at any time to see the current live list with descriptions.

Available LinkedIn Tools

The server's Composio session is scoped to exactly these 22 LinkedIn actions (see ENABLED_LINKEDIN_TOOLS in src/composio_client.py) — this is what's actually enabled for the connected account, not every action Composio's LinkedIn toolkit supports. Full JSON Schemas (all fields, nested objects, enums) are always available live via the linkedin_mcp_list_tools tool; this table is a quick-reference summary of required parameters and when to use each one.

Tool

Required parameters

Use case

LINKEDIN_CREATE_LINKED_IN_POST

author (urn:li:person|organization), commentary (text, ≤3000 chars)

Publish a post — as yourself or a company page you administer. Optional: images, visibility, distribution (audience targeting), lifecycleState: DRAFT, contentCallToActionLabel.

LINKEDIN_CREATE_ARTICLE_OR_URL_SHARE

author, specificContent (media URL), visibility

Share a link/article with optional commentary, via the older UGC Posts API.

LINKEDIN_CREATE_COMMENT_ON_POST

target_urn, actor, object, message.text

Comment on a post, or reply to a comment via parentComment. Supports @-mentions and image attachments.

LINKEDIN_DELETE_LINKED_IN_POST

share_id

Delete a post by its share id/URN.

LINKEDIN_DELETE_POST

post_urn (ugcPost or share)

Delete a post via the newer Posts API; idempotent.

LINKEDIN_DELETE_UGC_POST

ugc_post_urn

Delete a post via the legacy UGC Post API; idempotent.

LINKEDIN_GET_MY_INFO

(none)

Get your own profile (name, headline, person URN) — needed to get your person_id before posting.

LINKEDIN_GET_PERSON

person_id

Look up another member's basic profile by id.

LINKEDIN_GET_POST_CONTENT

post_id

Fetch the full content/metadata of an existing post by URN.

LINKEDIN_LIST_REACTIONS

entity

See who reacted to a post/comment and what reaction type. Optional: sort, count, start.

LINKEDIN_GET_COMPANY_INFO

(none)

List organizations you administer/manage — the way to find your company page's URN for posting as an org.

LINKEDIN_GET_NETWORK_SIZE

organization_id

Get a company page's follower count.

LINKEDIN_GET_ORG_PAGE_STATS

organization

Company page engagement stats (views, button clicks) — lifetime, or time-bound with timeRangeStart/timeRangeEnd/timeGranularityType. Requires org ADMINISTRATOR role.

LINKEDIN_GET_SHARE_STATS

organizational_entity

Company page content performance (impressions, clicks, likes, comments, shares). Optional time_intervals for a time-bound window.

LINKEDIN_GET_AD_TARGETING_FACETS

(none)

Discover which ad targeting categories exist (locations, industries, job functions, ...) before building a campaign.

LINKEDIN_SEARCH_AD_TARGETING_ENTITIES

query, facet

Typeahead-search a targeting facet (e.g. resolve "United States" to its geo URN) before using it in targeting criteria.

LINKEDIN_GET_AUDIENCE_COUNTS

targetingCriteria

Estimate the audience size for a given (URL-encoded) targeting criteria string, for ad campaign planning.

LINKEDIN_INITIALIZE_IMAGE_UPLOAD

owner

Step 1 of the newer image-upload flow: get a presigned upload_url + image URN, PUT the image bytes, then pass the URN to LINKEDIN_CREATE_LINKED_IN_POST.

LINKEDIN_REGISTER_IMAGE_UPLOAD

owner_urn

Step 1 of the legacy image-upload flow for feed shares; same upload-then-reference pattern as above.

LINKEDIN_GET_IMAGE

image_urn

Get one image's status, dimensions, and download URL.

LINKEDIN_GET_IMAGES

ids (array)

Batch image metadata lookup.

LINKEDIN_GET_VIDEOS

one of video_urn / video_ids / associated_account

Get video metadata — single, batch, or every video for a sponsored account (paginated via count/start).

Installation

Requires Python 3.12+ and uv.

git clone <this-repo>
cd linkedin-mcp
uv venv
uv pip install -e ".[dev]"

Configuration

Copy .env.example to .env and fill in your Composio API key:

cp .env.example .env

Variable

Required

Default

Description

COMPOSIO_API_KEY

Yes

Your Composio API key. Get one at https://app.composio.dev

COMPOSIO_USER_ID

Yes

Your Composio account's user id (from the dashboard) — tool calls are scoped to this user

COMPOSIO_PROJECT_ID

No

Composio project id, for reference only (not sent to the SDK)

COMPOSIO_ORG_ID

No

Composio org id, for reference only (not sent to the SDK)

COMPOSIO_ORG_MEMBER_EMAIL

No

Composio org member email, for reference only

LINKEDIN_AUTH_CONFIG_ID

No

Composio default

Use a specific LinkedIn auth config instead of the default one

LOG_LEVEL

No

INFO

DEBUG / INFO / WARNING / ERROR

LOG_DIR

No

logs

Directory for the rotating log file

STATS_FILE

No

logs/stats.json

Path to the persisted stats JSON file

CONNECT_TIMEOUT_MS

No

300000 (5 min)

Max time to wait for LinkedIn OAuth to complete at startup

PORT

No

8000

Bind port (Alpic sets this automatically at deploy time)

Never hardcode COMPOSIO_API_KEY anywhere — it's read exclusively from the environment.

Running locally

uv run python -m src.server

On first run, if LinkedIn isn't connected yet for the fixed user (selvin), the server logs an authorization URL and waits for you to complete the OAuth flow in a browser before finishing startup.

Test it's alive:

curl -X POST http://localhost:8000/mcp \
  -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'

Running with Docker

docker build -t linkedin-mcp .
docker run --rm -p 8000:8000 --env-file .env linkedin-mcp

Deploying to Alpic

npm install -g alpic   # once
alpic login            # or set ALPIC_API_KEY

alpic environment-variable add --env-file .env --environment-id <id>
alpic deploy --runtime python3.13

Alpic prints the live MCP server URL and a /try playground URL on success. See alpic deployment logs --deployment-id <id> if a deploy fails — check that COMPOSIO_API_KEY is set on the target environment first (missing env vars are the most common startup crash cause).

Connecting from Claude Desktop

  1. Deploy first (or run locally and expose it, e.g. with ngrok http 8000).

  2. In Claude Desktop: Settings → Connectors → Add custom connector.

  3. Paste the Alpic-hosted MCP URL (ends in /mcp).

  4. Claude will list linkedin_mcp_ping, linkedin_mcp_health, and every discovered LinkedIn action.

Connecting from ChatGPT

  1. In ChatGPT: Settings → Connectors → Advanced settings → Developer mode (required for full custom tool calling, not just search/fetch).

  2. Add custom connector, paste the Alpic-hosted MCP URL.

  3. Enable the connector in a chat and ChatGPT can call any LinkedIn action directly.

Troubleshooting

Symptom

Likely cause

Fix

Server won't start / crashes immediately

COMPOSIO_API_KEY missing

Set it before first deploy/run — this is the one thing that must be present

linkedin_mcp_health shows linkedin_connected: false

OAuth never completed

Check server logs for the authorization URL, visit it, then call linkedin_mcp_list_tools to refresh

linkedin_mcp_health shows composio_reachable: false

Bad API key or Composio outage

Verify COMPOSIO_API_KEY is valid; check https://status.composio.dev

A LinkedIn tool call errors out

Expired auth, missing scope, or bad input

The error message is returned verbatim from Composio/LinkedIn in the tool result — read it, it's usually actionable

New Composio LinkedIn action doesn't show up

Local schema cache

Call linkedin_mcp_list_tools to force a refresh

Alpic deploy fails at "start" phase

Missing env var or wrong port binding

alpic environment-variable list --environment-id <id>; the server always binds PORT

Testing

uv run pytest

License

MIT — see LICENSE.

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Draft, schedule, publish, and analyze your LinkedIn posts from your AI assistant via Taplio.

  • Give AI agents the LinkedIn tools to find, qualify, engage, and follow up with prospects.

  • Let AI tools securely access your LinkedIn network and DMs

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/selvin-paul-raj/Linkedin-Alpic-Mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server