Building a LinkedIn MCP Server: Connecting AI Assistants to LinkedIn

Most businesses think of AI assistants as chatbots that answer questions inside a text box. Giving an assistant real tools — including access to a locked-down platform like LinkedIn — raises a different set of questions. This case study covers a custom LinkedIn MCP (Model Context Protocol) server that securely connects an AI assistant to a real LinkedIn account.


What Even Is This?

Imagine an assistant that is great at talking and thinking, but has no hands — it cannot open LinkedIn, click buttons, or read a profile. MCP is like giving that assistant a hand and a set of instructions: “here is a button labeled my_profile — press it, and I will hand you back the person’s LinkedIn info.” MCP does not make the assistant smarter. It gives the assistant permission and a path to touch a real system, instead of guessing from memory.


The Challenge

LinkedIn is one of the most locked-down platforms for developers. Unlike a weather API or a hotel search API, LinkedIn does not let just any app read a full profile or auto-publish content — every permission has to be explicitly granted through LinkedIn’s OAuth system, and most write-access (posting, messaging) requires separate partner approval.

The goal for this build was to:

  • Authenticate securely with LinkedIn using an OAuth access token
  • Expose LinkedIn data through the same MCP interface used across our other tools (weather, hotels, email)
  • Prove the pattern works, so that broader LinkedIn API access can slot into the same server with no architecture changes

The Solution: A Custom LinkedIn MCP Server

We built the server with FastMCP, the same framework behind our other MCP integrations. The access token is loaded securely from a .env file — never hardcoded — using python-dotenv.

import os
import httpx
from dotenv import load_dotenv
from mcp.server.fastmcp import FastMCP

load_dotenv()
ACCESS_TOKEN = os.getenv("LINKEDIN_ACCESS_TOKEN")

if not ACCESS_TOKEN:
    raise ValueError("LINKEDIN_ACCESS_TOKEN not found")

mcp = FastMCP("linkedin")

HEADERS = {
    "Authorization": f"Bearer {ACCESS_TOKEN}",
    "LinkedIn-Version": "202405",
    "X-Restli-Protocol-Version": "2.0.0"
}

@mcp.tool()
async def my_profile():
    """Get the authenticated LinkedIn user's profile."""
    async with httpx.AsyncClient(timeout=30) as client:
        response = await client.get(
            "https://api.linkedin.com/v2/userinfo",
            headers=HEADERS
        )
        response.raise_for_status()
        return response.json()

if __name__ == "__main__":
    mcp.run(transport="stdio")

What Is Actually Happening Here, 

  • load_dotenv() — like fetching a key from a locked drawer instead of taping it to the front door
  • HEADERS — the ID badge the server shows LinkedIn’s API every time it asks for data (“Bearer token” means “here is my badge, let me in”)
  • @mcp.tool() — a sticky note the AI assistant can read that says “this button exists, here is what it does”
  • httpx.AsyncClient — the actual knock on LinkedIn’s door, asking for the profile data

Example: What the Assistant Sees and Does

Someone asks the assistant:What is my LinkedIn profile info?

The assistant calls the tool:

my_profile() -> GET https://api.linkedin.com/v2/userinfo

LinkedIn responds:

{
  "name": "Jane Doe",
  "email": "jane@example.com",
  "email_verified": true,
  "picture": "https://media.licdn.com/.../profile.jpg",
  "locale": { "country": "US", "language": "en" }
}

The assistant replies in plain English: “You are logged in as Jane Doe, verified email, based in the US.”

That is the whole loop: assistant asks, tool fetches, real data comes back, assistant explains it in plain language.


Why This Tool Cannot Post Yet (And Why That Is LinkedIn, Not Us)

This is the honest, important part of this case study: the current server can read a profile, but it cannot publish posts. That is not a gap in the code, it is a wall LinkedIn puts up on purpose. Auto-publishing on someone’s behalf requires LinkedIn’s Community Management API, which needs a separate, approved partner application. Most developers, including this project, only get access to the basic userinfo read scope by default.

The takeaway for clients: an AI assistant that can post to LinkedIn (or any locked-down platform) automatically is a two-part project — the MCP server code, which we can build in days, and the platform’s own approval process, which is out of any developer’s hands and can take longer.


Tech Stack

  • Python 3.12+, managed with uv
  • FastMCP (mcp[cli]) for the MCP server
  • httpx for async HTTP calls to LinkedIn’s API
  • python-dotenv for secure token storage
  • LinkedIn OAuth 2.0 / OpenID Connect (userinfo endpoint)

Security & Best Practices

The access token lives only in a local .env file, excluded from version control via .gitignore. This keeps the credential out of source code and off any public repository entirely — the same discipline we apply across every MCP server we build.


Results & Takeaways

This project proves the MCP pattern extends cleanly even to a heavily restricted platform like LinkedIn: authenticate once, expose a clean tool interface, and let the AI assistant do the asking. It is built to grow — the moment expanded LinkedIn API access is approved, posting, commenting, and messaging tools slot into this exact same server with no architecture changes.


Want an MCP Server Built for Your Platform?

If you want your AI assistant securely connected to LinkedIn, internal tools, or any other API, with proper OAuth handling and zero hardcoded secrets, we can design and build it the same way we built this one.

Digital AI SEO AssistantAsk about our agentic AI services