Skip to main content
Glama

linkedin-mcp

An MCP (Model Context Protocol) server for LinkedIn and job hunting: authenticate via OAuth 2.0, read your profile, draft and analyze posts with an LLM, publish posts to your feed, search free job boards, and track applications — all through tools any MCP client (Claude, Cursor, custom agents) can call.

User / agent
      │  (MCP: stdio or streamable HTTP)
      ▼
 linkedin-mcp (FastMCP + FastAPI)
      │  ├── LinkedIn OAuth 2.0 (authorization code + refresh)
      │  ├── LinkedIn REST API  (profile, email, create post)
      │  ├── Job boards  (Remotive · Arbeitnow · RemoteOK · Jobicy — keyless)
      │  ├── Application tracker  (local JSON store)
      │  └── LLM provider layer (Grok · OpenAI · Ollama · unorouter — swappable via env)
      ▼
  LinkedIn API  /  job board APIs  /  your LLM provider

What's implemented (Phase 1 + a taste of Phase 3)

Tool

What it does

auth_status

Check OAuth state; returns the login URL or a clear "what's missing" message

get_profile

Member profile (name, picture, locale, email) via OpenID Connect /v2/userinfo

get_email

Member email (requires the email scope)

create_post

Publish a text or image post via the current Posts API (/rest/posts)

draft_post

Generate a LinkedIn post with the configured LLM provider

analyze_post

Expert critique + suggested rewrites of a draft via the LLM

search_jobs

Search Remotive/Arbeitnow/RemoteOK/Jobicy — free, keyless, no LinkedIn permissions

match_jobs_to_profile

LLM-scored fit for listings from search_jobs against your profile

prepare_application

One call → fit assessment + cover letter + recruiter DM + interview prep

track_application

Save an application locally (company, role, URL, status, notes)

list_applications

List tracked applications, filterable by status

update_application

Move an application along: saved → applied → interview → offer/rejected

followup_applications

Prioritized view of what needs action next

analyze_job_fit

Compare a job description against your profile: fit score, gaps, talking points

draft_application

Tailored cover letter or recruiter DM from a job description + your profile

draft_job_post

Open-to-work / job-search announcement post

schedule_post

Queue a post to be published automatically at a future time

list_scheduled_posts

List queued posts, filterable by status

cancel_scheduled_post

Remove a queued post before it publishes

publish_scheduled_post_now

Publish a queued post immediately (handy for testing)

Resource linkedin://profile

Profile exposed as an MCP resource

Job search & application tracking

LinkedIn offers no public jobs API, so search goes through four free keyless boards (verified live): Remotive, Arbeitnow, RemoteOK, and Jobicy. The typical agent flow:

search_jobs("python fastapi")  →  match_jobs_to_profile(listings)
       →  prepare_application(jd)  →  track_application(...)
       →  draft_job_post(...)  →  create_post / schedule_post

Applications are stored in .data/applications.json (gitignored). Board outages never break a search: each board's error is reported per-board under board_errors.

Image posts

Pass a local JPG/PNG/GIF path to create_post (or schedule_post): the file is uploaded through LinkedIn's Images API (/rest/images?action=initializeUpload → signed PUT → referenced by urn:li:image: URN in the post) and published with optional alt_text.

Post scheduling

schedule_post stores the post in .data/scheduled_posts.json; while the web server is running it polls every SCHEDULER_POLL_SECONDS (default 30 s) and publishes posts whose time has arrived. publish_at is ISO-8601 — naive values are treated as UTC, or include an offset (e.g. 2026-09-05T15:30:00+05:30). Failures (e.g. token expired) are recorded on the entry as status="failed" with the error, so list_scheduled_posts shows exactly what happened.

Everything the LLM does goes through a provider-agnostic layer — you switch vendors by changing one env var, no code changes:

LLM_PROVIDER=grok     # or: openai | ollama | unorouter

Related MCP server: LinkedIn MCP Zero

Prerequisites

Step 1 — Create the LinkedIn Developer App

  1. Go to https://www.linkedin.com/developersCreate app.

  2. Fill in the basics (name, LinkedIn Page, logo) and create it.

  3. In Products, request:

    • Sign In with LinkedIn using OpenID Connect — grants openid, profile, email

    • Share on LinkedIn — grants w_member_social, required to post

  4. In Auth, note your Client ID and Client Secret, and add the Authorized redirect URL:

    (For production you'll use your deployed HTTPS URL instead — Phase 2.)

Step 2 — Configure

python -m venv .venv
source .venv/Scripts/activate        # Git Bash on Windows; on macOS/Linux: .venv/bin/activate
pip install -e ".[dev]"

cp .env.example .env                 # then fill in the values

.env essentials:

LINKEDIN_CLIENT_ID=your-client-id
LINKEDIN_CLIENT_SECRET=your-client-secret
LLM_PROVIDER=grok
GROK_API_KEY=your-xai-key

About xAI billing: xAI currently does not accept Indian payment cards for its API. If that's a blocker, just set LLM_PROVIDER=openai or LLM_PROVIDER=ollama — nothing else changes.

Step 3 — Run

Web mode (personal dashboard UI + OAuth + MCP over HTTP):

python -m linkedin_mcp web
# or: uvicorn linkedin_mcp.app:app --reload

Open http://localhost:8000/ for the personal dashboard — no MCP client needed:

  • Compose & publish posts with an image picker, or schedule them for later

  • Upload documents (PDF/DOCX/TXT/MD, 10 MB max) — mark your resume as profile and every job-matching tool uses your real skills instead of bare profile metadata

  • Job search across the four boards + one-click LLM match against your profile

  • Application tracker with status buttons (saved → applied → interview → …)

  • Live scheduled-posts list with publish-now / cancel

The MCP endpoint is still available for clients at:

http://localhost:8000/mcp/

Stdio mode (for Claude Desktop / local MCP clients):

python -m linkedin_mcp

Step 4 — Connect an MCP client

Claude Desktop / Claude Code claude mcp add example (streamable HTTP):

claude mcp add linkedin --transport http http://localhost:8000/mcp/

Or, from any JSON-RPC client, call auth_status first — it tells you exactly what's missing (credentials, login, tokens) instead of failing blindly.

OAuth & token handling

  • Authorization-code flow with a per-login state (validated on callback; 10 min TTL).

  • Access + refresh tokens are persisted to LINKEDIN_TOKEN_FILE (default .data/tokens.json).

  • Expiry/refresh handling: every API call goes through TokenManager, which transparently refreshes the access token when it's within 5 minutes of expiry. If no refresh token is available (LinkedIn grants those to approved apps only), tools raise a clear "re-authorize" error.

  • The current LinkedIn API version header (Linkedin-Version) is configurable via LINKEDIN_API_VERSION — bump it when LinkedIn sunsets the version you use (check https://learn.microsoft.com/en-us/linkedin/marketing/).

LLM provider swap

LLM_PROVIDER

Requires

Notes

grok

GROK_API_KEY

xAI, OpenAI-compatible (https://api.x.ai/v1)

openai

OPENAI_API_KEY

https://api.openai.com/v1

ollama

local Ollama running

http://localhost:11434/v1, no key

unorouter

UNOROUTER_API_KEY

any other OpenAI-compatible endpoint (https://api.unorouter.com/v1)

All three share one implementation (OpenAICompatProvider) because they all speak OpenAI's chat-completions protocol; the factory in llm/factory.py validates the key and produces a provider with a helpful error if you haven't configured one.

Project layout

src/linkedin_mcp/
├── config.py            # pydantic-settings, all env vars, provider selection
├── errors.py            # ConfigurationError, NeedsReauthError, LinkedInAPIError, LLMError
├── services.py          # shared wiring (store, oauth, token manager, client, scheduler)
├── scheduling.py        # scheduled-post store + background publisher loop
├── jobs.py              # keyless job-board search (Remotive/Arbeitnow/RemoteOK/Jobicy)
├── applications.py      # local application tracker store
├── documents.py         # uploads (pdf/docx/txt/md) + resume/profile summary
├── api.py               # REST endpoints backing the dashboard UI
├── server.py            # FastMCP server: 23 tools + resources
├── app.py               # FastAPI app: /auth/* routes + mounted /mcp endpoint
├── __main__.py          # CLI: `python -m linkedin_mcp [web]`
├── llm/                 # swappable LLM layer (base protocol, factory, provider)
└── linkedin/            # OAuth flow, token storage/refresh, REST client
tests/                   # pytest (mocked HTTP), 66 tests

Security notes

  • Never commit .env or .data/ — both are gitignored.

  • Tokens are stored in plaintext on disk (local dev). For a deployed version, move them to an encrypted store / server-side secret manager.

  • The OAuth state registry is in-memory — fine for a single-user local app; replace with a signed cookie or session store when deploying.

  • The LLM layer only ever sends what you pass to draft_post/analyze_post to the provider — the server never silently uploads your LinkedIn data.

Tests

pytest                 # 59 tests: tokens/refresh, LLM factory, LinkedIn client,
                       # job boards, application tracker, tools (all mocked HTTP)
ruff check src tests   # lint

Roadmap

  • Phase 2 — free hosting: containerize, deploy on a free tier (limits change often — verify current offers), HTTPS, point LINKEDIN_REDIRECT_URI at the deployed URL.

  • Phase 3 — more tools: get_post_statistics (requires approved analytics permissions), video/document posts, an agent loop where the LLM picks tools itself, Adzuna board (free key, aggregates thousands of sites incl. India locations), resume storage + per-JD tailoring.

  • Hardening: signed-cookie state, token encryption, rate-limit handling with retries.

License

MIT

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

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

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Enables AI assistants to access and interact with LinkedIn data—profiles, messaging, jobs, companies, and more—via MCP, with remote or local deployment.
    2
    22
    52
    9
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables searching and scraping of LinkedIn profiles, companies, jobs, and posts using natural language through MCP-compatible AI clients.
    13
    MIT

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/iThinkAndCode/mcp-server'

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