Skip to main content
Glama

MCP Server for LinkedIn

Disclaimer: This is an independent, community project. It is not affiliated with, authorized by, endorsed by, or sponsored by LinkedIn Corporation or Microsoft. "LinkedIn" is a registered trademark of LinkedIn Corporation and is used here only descriptively to identify the third-party service this software interoperates with.

An MCP server that lets AI assistants like Claude read LinkedIn data through your own logged-in browser session. Access profiles and companies, search for jobs, or get job details.

Sponsor

This MCP server is free and open source, supported by Unipile. It runs locally with your own browser session. Unipile is the fully managed cloud alternative: a hosted LinkedIn API for Classic, Sales Navigator, and Recruiter that handles auth, sessions, and infrastructure for you. Try it free for 7 days →


Related MCP server: LinkedIn Job Scraper MCP Server

Installation Methods - MCP Server for LinkedIn

uvx Install MCP Bundle Docker Development

Tool

Description

get_person_profile

Get profile info with explicit section selection (experience, education, interests, honors, languages, certifications, skills, projects, contact_info, posts)

get_my_profile

Get the authenticated user's own LinkedIn profile (same sections as get_person_profile)

connect_with_person

Send a connection request or accept an incoming one, with optional note

get_sidebar_profiles

Extract profile URLs from sidebar recommendation sections ("More profiles for you", "Explore premium profiles", "People you may know") on a profile page

get_inbox

List recent conversations from the LinkedIn messaging inbox

get_conversation

Read a specific messaging conversation by username or thread ID

search_conversations

Search messages by keyword

send_message

Send a message to a LinkedIn user (requires confirmation)

get_company_profile

Extract company information with explicit section selection (posts, jobs); about-section references may include a company_urn entry carrying the numeric id used by LinkedIn's people-search currentCompany URL facet

get_company_posts

Get recent posts from a company's LinkedIn feed

search_companies

Search for companies on LinkedIn by keywords

get_company_employees

List employees at a company from the /people/ page, with optional keyword filter

search_jobs

Search for jobs with keywords and location filters

get_saved_jobs

List job postings saved by the authenticated user

search_people

Search for people by keywords, location, connection degree (1st/2nd/3rd), and current company

get_job_details

Get detailed information about a specific job posting

get_feed

Get recent posts from the authenticated user's home feed

search_posts

Search posts/content globally by keyword (the "Posts" tab) with an optional recency filter (past-24h/past-week/past-month)

close_session

Close browser session and clean up resources

Prerequisites: Install uv.

Installation

Client Configuration

{
  "mcpServers": {
    "mcp-server-linkedin": {
      "command": "uvx",
      "args": ["mcp-server-linkedin@latest"],
      "env": { "UV_HTTP_TIMEOUT": "300" }
    }
  }
}

The @latest tag turns on automatic updates, which keep the server working with LinkedIn's current page structure. The server starts quickly, prepares the shared Patchright Chromium browser cache in the background under ~/.linkedin-mcp/patchright-browsers, and opens a LinkedIn login browser window on the first tool call that needs authentication.

AI agent? Get a quick confirmation from the user before enabling automatic updates.

NOTE

Early tool calls may return a setup/authentication-in-progress error until browser setup or login finishes. If you prefer to create a session explicitly, runuvx mcp-server-linkedin@latest --login.

uvx Setup Help

Transport Modes:

  • Default (stdio): Standard communication for local MCP servers

  • Streamable HTTP: For web-based MCP server

  • If no transport is specified, the server defaults to stdio

  • An interactive terminal without explicit transport shows a chooser prompt

CLI Options:

  • --login - Open a browser to sign in and save the session

  • --import-from-browser [BROWSER] - Reuse a session from a locally signed-in Chromium browser (chrome, chromium, brave, edge, arc, vivaldi, helium, yandex, whale, auto). Bare flag picks auto, the most recently used browser with a live LinkedIn session.

  • --logout - Clear the stored session

  • --no-headless - Show the browser window (useful for debugging)

  • --log-level {DEBUG,INFO,WARNING,ERROR} - Logging level (default: WARNING)

  • --transport {stdio,streamable-http} - Force the transport mode (default: stdio)

  • --host HOST / --port PORT / --path PATH - HTTP server address (defaults: 127.0.0.1, 8000, /mcp)

  • --timeout MS - Timeout for a single page operation (default: 5000)

  • --tool-timeout SECONDS - Timeout for a whole tool call (default: 180). Raise it for heavy scrapes, slow networks, or a cold-start browser.

  • --login-timeout SECONDS - How long the login browser waits for you to finish signing in (default: 1800; 0 = no limit). --login-viewer ends the session after 30 minutes either way.

  • --login-viewer - Docker only: show the --login browser at a token-protected URL on port 6080 (see Authentication)

  • --login-inline-wait SECONDS - How long a tool call waits for a login to finish before telling the model to retry (default: 25, max 45; 0 = return at once)

  • --browser-wait SECONDS - How long to wait for another server process to hand over the shared browser (default: 25, max 45; 0 = report busy at once). Only matters with several MCP clients running at once.

  • --browser-min-hold SECONDS - Shortest time this process keeps the shared browser before handing it over (default: 20). Clamped to 3 seconds below --browser-wait, so raise that one along with it. Higher means fewer browser restarts but longer waits for other clients.

  • --browser-idle-timeout SECONDS - Close an idle browser and release the profile after this long without a tool call (default: 600; 0 = keep it open)

  • --auto-import / --no-auto-import - Import a session from a signed-in local browser on the first tool call that needs one, before falling back to manual login (default: on). Skipped in Docker, behind a proxy, and on a non-loopback HTTP bind. On macOS the keychain may prompt once.

  • --user-data-dir PATH - Browser profile directory (default: ~/.linkedin-mcp/profile). Rotating or clearing a session deletes this directory and its parent, which holds the stored cookies and derived profiles.

  • --claim-profile-root - Take over a profile directory the server will not claim on its own, such as one whose parent already holds other files. Needed once per directory.

  • --chrome-path PATH - Path to a Chrome/Chromium executable

  • --proxy-server URL - Route browser traffic through a proxy, as scheme://host:port. Set the password via PROXY_PASSWORD, which keeps it out of the process list.

Import a session from your everyday browser:

If you are already signed into LinkedIn in Chrome, Chromium, Brave, Edge, Arc, Vivaldi, Helium, Yandex, or Naver Whale, you can skip the manual --login step and reuse that session:

# Auto-pick the most recently used browser with a live LinkedIn session
uvx mcp-server-linkedin@latest --import-from-browser
# Or target a specific browser
uvx mcp-server-linkedin@latest --import-from-browser brave

This reads the browser's LinkedIn cookies, validates them against your feed, and saves them to ~/.linkedin-mcp/profile/, the same place --login writes to. Notes:

  • With several signed-in browsers, the most recently used live LinkedIn session is tried first. If LinkedIn rejects it (revoked or remote-logged-out), the next most recent is tried automatically; the first the server accepts is imported. There is no prompt to pick. Pass a browser name to target one specifically.

  • On macOS the OS keychain may prompt to allow access to the browser's Safe Storage. Close the source browser first for the most reliable read.

  • Cookies protected by Chrome 127+ app-bound encryption (v20) cannot be decrypted without OS elevation; in that case use --login instead.

  • Imported cookies match a real login's on-disk set. The local server reads them back in full from the saved profile; the Docker bridge narrows to the same minimal auth subset it uses for a normal session.

Basic Usage Examples:

# Run with debug logging
uvx mcp-server-linkedin@latest --log-level DEBUG

HTTP Mode Example (for web-based MCP clients):

uvx mcp-server-linkedin@latest --transport streamable-http --host 127.0.0.1 --port 8080 --path /mcp

Runtime server logs are emitted by FastMCP/Uvicorn.

Tool calls are serialized to protect the shared LinkedIn browser session, both within one server process and across separate ones. If you run several MCP clients at once, each starts its own server process, and only one of them uses the browser at a time; the others wait briefly and take over as soon as it finishes a call. A client that waits too long gets a "browser is busy" message and can simply retry. Use --log-level DEBUG to see the wait/acquire/release logs.

This covers processes on the same machine and in the same runtime. It does not extend between the host and a Docker container sharing the same ~/.linkedin-mcp directory, so do not run --login or --logout on the host while a container is running.

Test with mcp inspector:

  1. Install and run mcp inspector bunx @modelcontextprotocol/inspector

  2. Click pre-filled token url to open the inspector in your browser

  3. Select Streamable HTTP as Transport Type

  4. Set URL to http://localhost:8080/mcp

  5. Connect

  6. Test tools

Installation issues:

  • Ensure you have uv installed: curl -LsSf https://astral.sh/uv/install.sh | sh

  • Check uv version: uv --version (should be 0.4.0 or higher)

  • On first run, uvx downloads all Python dependencies. On slow connections, uv's default 30s HTTP timeout may be too short. The recommended config above already sets UV_HTTP_TIMEOUT=300 (seconds) to avoid this.

  • Windows, DLL load failed while importing _greenlet: move to greenlet 3.5.5 or newer, whose published Windows wheels carry the C++ runtime inside the extension again. A fresh uvx run resolves that on its own; an environment that pins its dependencies needs uv lock --upgrade-package greenlet. Only greenlet 3.3.1 through 3.5.4 need MSVCP140.dll, which neither the python.org installer nor the uv-managed builds carry, and a greenlet built from source can need it at any version. Where the version cannot be moved, the Microsoft Visual C++ Redistributable supplies that DLL. Reported as greenlet#525, fixed in greenlet#526.

Session issues:

  • Browser profile is stored at ~/.linkedin-mcp/profile/

  • Managed browser downloads are cached at ~/.linkedin-mcp/patchright-browsers/

  • The browser cache keeps growing: a server upgrade can bring a new Chromium revision, and Patchright keeps the old one for as long as any installed version still references it. uvx keeps one archive per version you have ever run, so every one of them holds such a reference and the old revisions stay. The server logs a warning naming the revisions it is holding and how much space they take. To reclaim it, stop every LinkedIn MCP Server instance, delete ~/.linkedin-mcp/patchright-browsers/, and let the next launch download the current browser.

  • Make sure you have only one active LinkedIn session at a time

Login issues:

  • LinkedIn may require a login confirmation in the LinkedIn mobile app for --login

  • LinkedIn may show a captcha challenge during login. Run uvx mcp-server-linkedin@latest --login which opens a browser where you can solve it manually.

Timeout issues:

  • Page operations failing (elements not found, navigation hangs): increase the browser page-op timeout — --timeout 10000 or TIMEOUT=10000 (milliseconds, default 5000).

  • Entire tool calls timing out (e.g. multi-section profiles, cold-start Chromium, slow containers): increase the per-tool execution timeout — --tool-timeout 300 or TOOL_TIMEOUT=300 (seconds, default 180).

  • First tool call with no session: if a locally logged-in browser has a live LinkedIn session, the server auto-imports it (see AUTO_IMPORT_FROM_BROWSER / --auto-import) instead of forcing a manual login. On macOS the keychain may prompt once for Safe Storage access. If no importable browser session exists, it falls back to opening a login window and waits up to LOGIN_INLINE_WAIT seconds (default 25, max 45; --login-inline-wait) so a quick sign-in resolves in one call. If the wait elapses, the tool returns a pending signal and the model retries in about 30 seconds. Neither the auto-import nor the inline wait applies under Docker or when the server is bound to a non-loopback HTTP host. Create the session on the host with --login, or use the explicit Docker --login --login-viewer command.

  • Users on slow connections may need higher values for either.

Told to run --login on the host when you already did:

  • If tool calls answer "No valid LinkedIn session is available in Docker" on a machine that is not a container, the runtime was misdetected. This happened on Linux hosts running a Docker daemon for unrelated services. Set LINKEDIN_MCP_CONTAINER=false to override the detection; true forces the opposite.

Using a proxy:

Most people should not use one. LinkedIn's own guidance for reducing security challenges is to avoid a VPN or proxy, and it scores the addresses a session signs in from. A home connection you have used for years is a trust signal; a commercial exit node with a history you cannot see is not, and switching to one is itself the kind of change that triggers a checkpoint. A proxy is worth it in one case: the server runs somewhere its address is obviously a data centre, or in a different country from the account's history. Even then, a WireGuard or Tailscale exit node on your own home network beats any paid provider, because the address really is yours. If you do buy one, take a dedicated static ISP address and keep it, rather than a rotating residential pool.

  • Route the browser through a proxy with --proxy-server http://host:port (http, https, socks4 and socks5 are accepted). Only browser traffic is routed, not the MCP transport.

  • Credentials go in PROXY_USERNAME and PROXY_PASSWORD. There is no --proxy-password flag on purpose: command-line arguments are readable by every other user on the machine. PROXY_SERVER also accepts the combined http://user:pass@host:port form most providers hand out.

  • Chromium cannot authenticate to a SOCKS proxy, so credentials require an http(s) endpoint. If your provider only offers authenticated SOCKS5, run a local relay that holds the credentials and point the server at that.

  • Local addresses go through the proxy too. Chromium's usual direct route for localhost is removed when a proxy is set, so add PROXY_BYPASS=localhost,127.0.0.1,::1 if you need local targets reached directly.

  • Auto-import is skipped while a proxy is configured: a session taken from a local browser was created on your real address, and moving it to the proxy is the very change that triggers a checkpoint. Use --login.

  • A wrong proxy password does not report itself: Chromium retries the authentication challenge until the page times out, so it surfaces as a timeout or a failed sign-in. If sessions stop working right after you add a proxy, check the credentials before assuming the session expired.

  • Set the proxy up before creating the session. Run --login with the proxy already configured. Turning a proxy on for an existing profile moves a logged-in session to a new IP, which is what triggers a LinkedIn checkpoint. The same applies to --import-from-browser, which imports a session created on your real IP. Use a sticky session, not a rotating pool, for the same reason.

Custom Chrome path:

  • If Chrome is installed in a non-standard location, use --chrome-path /path/to/chrome

  • Can also set via environment variable: CHROME_PATH=/path/to/chrome

  • On macOS and Linux the browser must be at least as new as the one that last opened your profile, and the server refuses the launch otherwise. (Not on Windows: a browser there cannot be asked its version without starting one, so the check is off.) An older browser can silently drop stores a newer one wrote, the saved session among them, and the failure then looks exactly like an expired login. The message names both versions. Going back to the bundled Chromium after running a newer Chrome once is the usual way to meet this; either run the newer browser again, whichever one that was, or run --login, which moves the stored session aside and signs in fresh with the browser you have. --logout also clears it but discards the old session instead of keeping it recoverable, and it asks for confirmation on the terminal, so it is not usable from a server an MCP client started.

  • Only Chrome, Chromium and Chrome for Testing are compared this way. Forks number themselves differently (Vivaldi is on 7.x, Edge's build number sits far below Chrome's under the same major), so pointing CHROME_PATH at one turns the check off rather than producing a refusal nothing could satisfy.

šŸ“¦ Claude Desktop MCP Bundle (formerly DXT)

Prerequisites: Claude Desktop.

One-click installation for Claude Desktop users:

  1. Download the latest .mcpb artifact from releases

  2. Click the downloaded .mcpb file to install it into Claude Desktop

  3. Call any LinkedIn tool

On startup, the MCP Bundle starts preparing the shared Patchright Chromium browser cache in the background. If you call a tool too early, Claude will surface a setup-in-progress error. On the first tool call that needs authentication, the server opens a LinkedIn login browser window and asks you to retry after sign-in.

MCP Bundle Setup Help

First-time setup behavior:

  • Claude Desktop starts the bundle immediately; browser setup continues in the background

  • If the Patchright Chromium browser is still downloading, retry the tool after a short wait

  • Managed browser downloads are shared under ~/.linkedin-mcp/patchright-browsers/

  • The browser cache keeps growing: Patchright keeps an old Chromium revision for as long as any installed version still references it, so an upgrade can leave both on disk. The server logs a warning naming what it holds. To reclaim the space, stop every LinkedIn MCP Server instance, delete ~/.linkedin-mcp/patchright-browsers/, and let the next launch download the current browser.

  • Windows, the bundle exits with DLL load failed while importing _greenlet: install the Microsoft Visual C++ Redistributable, or reinstall a bundle pinning greenlet 3.5.5 or newer, whose published Windows wheels carry the C++ runtime inside the extension again. A bundle pinning greenlet 3.3.1 through 3.5.4 needs MSVCP140.dll from that redistributable, which neither the python.org installer nor the uv-managed builds carry, and a greenlet built from source can need it at any version. The server names this itself on startup, and only after checking that the loader cannot produce that DLL. Reported as greenlet#525, fixed in greenlet#526.

Login issues:

  • Make sure you have only one active LinkedIn session at a time

  • LinkedIn may require a login confirmation in the LinkedIn mobile app for --login

  • LinkedIn may show a captcha challenge during login. Run uvx mcp-server-linkedin@latest --login which opens a browser where you can solve captchas manually. See the uvx setup for prerequisites.

Timeout issues:

  • Page operations failing (elements not found, navigation hangs): increase the browser page-op timeout — --timeout 10000 or TIMEOUT=10000 (milliseconds, default 5000).

  • Entire tool calls timing out (e.g. multi-section profiles, cold-start Chromium, slow containers): increase the per-tool execution timeout — --tool-timeout 300 or TOOL_TIMEOUT=300 (seconds, default 180).

  • First tool call with no session: if a locally logged-in browser has a live LinkedIn session, the server auto-imports it (see AUTO_IMPORT_FROM_BROWSER / --auto-import) instead of forcing a manual login. On macOS the keychain may prompt once for Safe Storage access. If no importable browser session exists, it falls back to opening a login window and waits up to LOGIN_INLINE_WAIT seconds (default 25, max 45; --login-inline-wait) so a quick sign-in resolves in one call. If the wait elapses, the tool returns a pending signal and the model retries in about 30 seconds. Neither the auto-import nor the inline wait applies under Docker or when the server is bound to a non-loopback HTTP host. Create the session on the host with --login, or use the explicit Docker --login --login-viewer command.

  • Users on slow connections may need higher values for either.

Told to run --login on the host when you already did:

  • If tool calls answer "No valid LinkedIn session is available in Docker" on a machine that is not a container, the runtime was misdetected. This happened on Linux hosts running a Docker daemon for unrelated services. Set LINKEDIN_MCP_CONTAINER=false to override the detection; true forces the opposite.

🐳 Docker Setup

Prerequisites: Make sure Docker is installed and running.

Authentication

Log in once. The container opens a LinkedIn login browser that you drive from your own browser tab.

macOS / Linux:

# Create the directory first so the container can save your session into it
mkdir -p ~/.linkedin-mcp
docker run -it --rm \
  -v ~/.linkedin-mcp:/home/pwuser/.linkedin-mcp \
  -p 127.0.0.1:6080:6080 \
  stickerdaniel/linkedin-mcp-server:latest \
  --login --login-viewer

PowerShell (Windows):

$sessionDir = Join-Path $env:USERPROFILE ".linkedin-mcp"
New-Item -ItemType Directory -Force -Path $sessionDir | Out-Null
docker run -it --rm `
  -v "${sessionDir}:/home/pwuser/.linkedin-mcp" `
  -p 127.0.0.1:6080:6080 `
  stickerdaniel/linkedin-mcp-server:latest `
  --login --login-viewer

Open the full URL the command prints (it carries the access token) and sign in. The viewer closes itself afterwards; let the command exit on its own so the session is stored completely. It gives up after 30 minutes.

Keep the same host directory mounted at /home/pwuser/.linkedin-mcp on every later docker run, otherwise the server cannot find the session.

Configure Claude Desktop with Docker

macOS / Linux (absolute path in JSON):

{
  "mcpServers": {
    "mcp-server-linkedin": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "-v", "/absolute/path/to/.linkedin-mcp:/home/pwuser/.linkedin-mcp",
        "stickerdaniel/linkedin-mcp-server:latest"
      ]
    }
  }
}

Spell that first path out in full. A client runs docker directly rather than through a shell, so a leading ~ reaches Docker unexpanded and it refuses the mount.

PowerShell (Windows): use a forward-slash JSON path. A backslash path like C:\Users\Alice\.linkedin-mcp fails JSON parsing because \U is an invalid escape. Use C:/Users/Alice/.linkedin-mcp instead, replacing Alice with your username.

{
  "mcpServers": {
    "mcp-server-linkedin": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "-v", "C:/Users/Alice/.linkedin-mcp:/home/pwuser/.linkedin-mcp",
        "stickerdaniel/linkedin-mcp-server:latest"
      ]
    }
  }
}
NOTE

In PowerShell,~ is not expanded inside a composite Docker -v argument. Use C:/Users/<you>/.linkedin-mcp or build the path with $env:USERPROFILE\.linkedin-mcp before passing it to Docker.

NOTE

Sessions expire over time. When tool calls start asking for authentication, repeat the login command above, or runuvx mcp-server-linkedin@latest --login on the host.

Docker Setup Help

Transport Modes:

  • Default (stdio): Standard communication for local MCP servers

  • Streamable HTTP: For a web-based MCP server

  • If no transport is specified, the server defaults to stdio

  • An interactive terminal without explicit transport shows a chooser prompt

CLI Options:

  • --log-level {DEBUG,INFO,WARNING,ERROR} - Logging level (default: WARNING)

  • --transport {stdio,streamable-http} - Force the transport mode (default: stdio)

  • --host HOST / --port PORT / --path PATH - HTTP server address (defaults: 127.0.0.1, 8000, /mcp)

  • --logout - Clear the stored session and every profile derived from it

  • --timeout MS - Timeout for a single page operation (default: 5000)

  • --tool-timeout SECONDS - Timeout for a whole tool call (default: 180). Raise it for heavy scrapes, slow networks, or a cold-start browser.

  • --login-timeout SECONDS - How long the login browser waits for you to finish signing in (default: 1800; 0 = no limit). --login-viewer ends the session after 30 minutes either way.

  • --login-viewer - With --login, show the login browser at a token-protected URL on port 6080. Needs the profile mount from Authentication.

  • --login-inline-wait SECONDS - How long a tool call waits for a login to finish before telling the model to retry (default: 25, max 45; 0 = return at once)

  • --browser-wait SECONDS - How long to wait for another server process to hand over the shared browser (default: 25, max 45; 0 = report busy at once). Only matters with several MCP clients running at once.

  • --browser-min-hold SECONDS - Shortest time this process keeps the shared browser before handing it over (default: 20). Clamped to 3 seconds below --browser-wait, so raise that one along with it. Higher means fewer browser restarts but longer waits for other clients.

  • --browser-idle-timeout SECONDS - Close an idle browser and release the profile after this long without a tool call (default: 600; 0 = keep it open)

  • --auto-import / --no-auto-import - Import a session from a signed-in local browser on the first tool call that needs one, before falling back to manual login (ignored in Docker). On macOS the keychain may prompt once.

  • --user-data-dir PATH - Browser profile directory (default: ~/.linkedin-mcp/profile). Rotating or clearing a session deletes this directory and its parent, which holds the stored cookies and derived profiles.

  • --claim-profile-root - Take over a profile directory the server will not claim on its own, such as one whose parent already holds other files. Needed once per directory.

  • --chrome-path PATH - Path to a Chrome/Chromium executable (rarely needed in Docker)

  • --proxy-server URL - Route browser traffic through a proxy, as scheme://host:port. Set the password via PROXY_PASSWORD, which keeps it out of the process list.

NOTE

Plain--login still has no visible window in Docker. Add --login-viewer and publish 127.0.0.1:6080:6080 only for the one-shot login command. Docker is already headed by default, so --no-headless changes nothing. The experimental --daemon is ignored in Docker because its owner can outlive the virtual display.

HTTP Mode Example (for web-based MCP clients):

Bash / macOS / Linux:

docker run -it --rm \
  -v ~/.linkedin-mcp:/home/pwuser/.linkedin-mcp \
  -p 127.0.0.1:8080:8080 \
  stickerdaniel/linkedin-mcp-server:latest \
  --transport streamable-http --host 0.0.0.0 --port 8080 --path /mcp

PowerShell (Windows):

$sessionDir = Join-Path $env:USERPROFILE ".linkedin-mcp"
docker run -it --rm `
  -v "${sessionDir}:/home/pwuser/.linkedin-mcp" `
  -p 127.0.0.1:8080:8080 `
  stickerdaniel/linkedin-mcp-server:latest `
  --transport streamable-http --host 0.0.0.0 --port 8080 --path /mcp

Both halves of that are needed, and they do different jobs. --host 0.0.0.0 makes the server reachable inside the container: a process bound to 127.0.0.1 in there cannot be reached through a published port at all. The 127.0.0.1: in front of -p is what limits it outside, to this machine. Drop that prefix and Docker publishes on every interface, which puts an endpoint with no authentication on your network. The server cannot tell the two apart, so it warns either way.

Loopback publishing limits this to the machine, not to the container. Other containers on the same host can still reach it through host.docker.internal wherever that name resolves, which is the default on Docker Desktop and OrbStack but not on native Linux Docker.

Runtime server logs are emitted by FastMCP/Uvicorn.

The HTTP server answers requests addressed to localhost or to the address it is bound to, and refuses others with 421. That is what stops a website you merely visit from pointing a domain at this server and using your LinkedIn session through your own browser.

Reaching the server by any other name is refused, including a machine name on your network and the public name in front of a reverse proxy. Either have the proxy rewrite the upstream Host to the backend address, or name the host you serve it under:

FASTMCP_HTTP_ALLOWED_HOSTS='["mcp.example"]'

That permits exactly that name and keeps refusing everything else. The endpoint still has no authentication, so anything reachable beyond your own machine belongs behind something that provides it.

Test with mcp inspector:

  1. Install and run mcp inspector bunx @modelcontextprotocol/inspector

  2. Click pre-filled token url to open the inspector in your browser

  3. Select Streamable HTTP as Transport Type

  4. Set URL to http://localhost:8080/mcp

  5. Connect

  6. Test tools

Docker issues:

  • Make sure Docker is installed

  • Check if Docker is running: docker ps

  • Permission errors on ~/.linkedin-mcp: an older rootful Docker run may have created the directory as root. Fix it with sudo chown -R "$(id -u):$(id -g)" ~/.linkedin-mcp.

Login issues:

  • Make sure you have only one active LinkedIn session at a time

  • LinkedIn may require a login confirmation in the LinkedIn mobile app for --login

  • LinkedIn may show a captcha challenge during login. Run uvx mcp-server-linkedin@latest --login which opens a browser where you can solve captchas manually. See the uvx setup for prerequisites.

  • If Docker auth becomes stale after you re-login on the host, restart Docker once so it can fresh-bridge from the new source session generation.

Timeout issues:

  • Page operations failing (elements not found, navigation hangs): increase the browser page-op timeout — --timeout 10000 or TIMEOUT=10000 (milliseconds, default 5000).

  • Entire tool calls timing out (e.g. multi-section profiles, cold-start Chromium, slow containers): increase the per-tool execution timeout — --tool-timeout 300 or TOOL_TIMEOUT=300 (seconds, default 180).

  • First tool call with no session: if a locally logged-in browser has a live LinkedIn session, the server auto-imports it (see AUTO_IMPORT_FROM_BROWSER / --auto-import) instead of forcing a manual login. On macOS the keychain may prompt once for Safe Storage access. If no importable browser session exists, it falls back to opening a login window and waits up to LOGIN_INLINE_WAIT seconds (default 25, max 45; --login-inline-wait) so a quick sign-in resolves in one call. If the wait elapses, the tool returns a pending signal and the model retries in about 30 seconds. Neither the auto-import nor the inline wait applies under Docker or when the server is bound to a non-loopback HTTP host. Create the session on the host with --login, or use the explicit Docker --login --login-viewer command.

  • Users on slow connections may need higher values for either.

Told to run --login on the host when you already did:

  • If tool calls answer "No valid LinkedIn session is available in Docker" on a machine that is not a container, the runtime was misdetected. This happened on Linux hosts running a Docker daemon for unrelated services. Set LINKEDIN_MCP_CONTAINER=false to override the detection; true forces the opposite.

Using a proxy:

Most people should not use one. LinkedIn's own guidance for reducing security challenges is to avoid a VPN or proxy, and it scores the addresses a session signs in from. A home connection you have used for years is a trust signal; a commercial exit node with a history you cannot see is not, and switching to one is itself the kind of change that triggers a checkpoint. A proxy is worth it in one case: the server runs somewhere its address is obviously a data centre, or in a different country from the account's history. Even then, a WireGuard or Tailscale exit node on your own home network beats any paid provider, because the address really is yours. If you do buy one, take a dedicated static ISP address and keep it, rather than a rotating residential pool.

  • Route the browser through a proxy with --proxy-server http://host:port (http, https, socks4 and socks5 are accepted). Only browser traffic is routed, not the MCP transport.

  • Credentials go in PROXY_USERNAME and PROXY_PASSWORD. There is no --proxy-password flag on purpose: command-line arguments are readable by every other user on the machine. PROXY_SERVER also accepts the combined http://user:pass@host:port form most providers hand out.

  • Chromium cannot authenticate to a SOCKS proxy, so credentials require an http(s) endpoint. If your provider only offers authenticated SOCKS5, run a local relay that holds the credentials and point the server at that.

  • Local addresses go through the proxy too. Chromium's usual direct route for localhost is removed when a proxy is set, so add PROXY_BYPASS=localhost,127.0.0.1,::1 if you need local targets reached directly.

  • Auto-import is skipped while a proxy is configured: a session taken from a local browser was created on your real address, and moving it to the proxy is the very change that triggers a checkpoint. Use --login.

  • A wrong proxy password does not report itself: Chromium retries the authentication challenge until the page times out, so it surfaces as a timeout or a failed sign-in. If sessions stop working right after you add a proxy, check the credentials before assuming the session expired.

  • Set the proxy up before creating the session. Run --login with the proxy already configured. Turning a proxy on for an existing profile moves a logged-in session to a new IP, which is what triggers a LinkedIn checkpoint. The same applies to --import-from-browser, which imports a session created on your real IP. Use a sticky session, not a rotating pool, for the same reason.

Custom Chrome path:

  • If Chrome is installed in a non-standard location, use --chrome-path /path/to/chrome

  • Can also set via environment variable: CHROME_PATH=/path/to/chrome

  • On macOS and Linux the browser must be at least as new as the one that last opened your profile, and the server refuses the launch otherwise. (Not on Windows: a browser there cannot be asked its version without starting one, so the check is off.) An older browser can silently drop stores a newer one wrote, the saved session among them, and the failure then looks exactly like an expired login. The message names both versions. Going back to the bundled Chromium after running a newer Chrome once is the usual way to meet this; either run the newer browser again, whichever one that was, or run --login, which moves the stored session aside and signs in fresh with the browser you have. --logout also clears it but discards the old session instead of keeping it recoverable, and it asks for confirmation on the terminal, so it is not usable from a server an MCP client started.

  • Only Chrome, Chromium and Chrome for Testing are compared this way. Forks number themselves differently (Vivaldi is on 7.x, Edge's build number sits far below Chrome's under the same major), so pointing CHROME_PATH at one turns the check off rather than producing a refusal nothing could satisfy.

  • In the documented Docker setup this check does not apply. The container never opens the profile you created with --login; it derives its own from your cookies, and by default rebuilds that from scratch on every start, so there is nothing for an older image to downgrade. With EXPERIMENTAL_PERSIST_DERIVED_RUNTIME the derived profile is kept, and an image tag that moves backwards then throws it away and re-derives it, again with nothing for you to do. The check matters on the host, where the server opens that profile directly. Not during --login itself, which moves the old profile aside before it starts a browser and so can never trip it.

šŸ Local Setup (Develop & Contribute)

Contributions are welcome! See CONTRIBUTING.md for architecture guidelines and checklists. Please open an issue first to discuss the feature or bug fix before submitting a PR.

Prerequisites: Git and uv installed

Installation

# 1. Clone repository
git clone https://github.com/stickerdaniel/linkedin-mcp-server
cd linkedin-mcp-server

# 2. Install UV package manager (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 3. Install dependencies
uv sync
uv sync --group dev

# 4. Install pre-commit hooks
uv run pre-commit install

# 5. Start the server
uv run -m linkedin_mcp_server

The local server uses the same managed-runtime flow as MCPB and uvx: it prepares the Patchright Chromium browser cache in the background and opens LinkedIn login on the first auth-requiring tool call. You can still run uv run -m linkedin_mcp_server --login when you want to create the session explicitly.

Local Setup Help

CLI Options:

  • --login - Open a browser to sign in and save the session

  • --import-from-browser [BROWSER] - Reuse a session from a locally signed-in Chromium browser (chrome, chromium, brave, edge, arc, vivaldi, helium, yandex, whale, auto). Bare flag picks auto, the most recently used browser with a live LinkedIn session.

  • --status - Check whether the stored session is valid, then exit

  • --logout - Clear the stored session

  • --no-headless - Show the browser window (useful for debugging)

  • --log-level {DEBUG,INFO,WARNING,ERROR} - Logging level (default: WARNING)

  • --transport {stdio,streamable-http} - Force the transport mode (default: stdio)

  • --host HOST / --port PORT / --path PATH - HTTP server address (defaults: 127.0.0.1, 8000, /mcp)

  • --timeout MS - Timeout for a single page operation (default: 5000)

  • --tool-timeout SECONDS - Timeout for a whole tool call (default: 180). Raise it for heavy scrapes, slow networks, or a cold-start browser.

  • --user-data-dir PATH - Browser profile directory (default: ~/.linkedin-mcp/profile). Rotating or clearing a session deletes this directory and its parent, which holds the stored cookies and derived profiles.

  • --claim-profile-root - Take over a profile directory the server will not claim on its own, such as one whose parent already holds other files. Needed once per directory.

  • --slow-mo MS - Delay between browser actions (default: 0, useful for debugging)

  • --viewport WxH - Viewport size (default: 1280x720). Applies to windowless mode only; a headed launch uses the real window size.

  • --chrome-path PATH - Path to a Chrome/Chromium executable

  • --proxy-server URL - Route browser traffic through a proxy, as scheme://host:port. Set the password via PROXY_PASSWORD, which keeps it out of the process list.

  • --help - Show help

Note: Most CLI options have environment variable equivalents. See .env.example for details.

HTTP Mode Example (for web-based MCP clients):

uv run -m linkedin_mcp_server --transport streamable-http --host 127.0.0.1 --port 8000 --path /mcp

Claude Desktop:

{
  "mcpServers": {
    "mcp-server-linkedin": {
      "command": "uv",
      "args": ["--directory", "/path/to/linkedin-mcp-server", "run", "-m", "linkedin_mcp_server"]
    }
  }
}

stdio is used by default for this config.

Login issues:

  • Make sure you have only one active LinkedIn session at a time

  • LinkedIn may require a login confirmation in the LinkedIn mobile app for --login

  • LinkedIn may show a captcha challenge during login. The --login command opens a browser where you can solve it manually.

Scraping issues:

  • Use --no-headless to see browser actions and debug scraping problems

  • Add --log-level DEBUG to see more detailed logging

Session issues:

  • Browser profile is stored at ~/.linkedin-mcp/profile/

  • Managed browser downloads are cached at ~/.linkedin-mcp/patchright-browsers/, shared with the uvx and MCP Bundle installations

  • The browser cache keeps growing: Patchright keeps an old Chromium revision for as long as any installed version still references it, and a uv archive or a second worktree is such a reference. The server logs a warning naming what it holds. To reclaim the space, stop every LinkedIn MCP Server instance, delete ~/.linkedin-mcp/patchright-browsers/, and let the next launch download the current browser.

  • Use --logout to clear the profile and start fresh

Python/Patchright issues:

  • Check Python version: python --version (should be 3.12.4+)

  • Reinstall Patchright: uv run patchright install chromium

  • Reinstall dependencies: uv sync --reinstall

Timeout issues:

  • Page operations failing (elements not found, navigation hangs): increase the browser page-op timeout — --timeout 10000 or TIMEOUT=10000 (milliseconds, default 5000).

  • Entire tool calls timing out (e.g. multi-section profiles, cold-start Chromium, slow containers): increase the per-tool execution timeout — --tool-timeout 300 or TOOL_TIMEOUT=300 (seconds, default 180).

  • First tool call with no session: if a locally logged-in browser has a live LinkedIn session, the server auto-imports it (see AUTO_IMPORT_FROM_BROWSER / --auto-import) instead of forcing a manual login. On macOS the keychain may prompt once for Safe Storage access. If no importable browser session exists, it falls back to opening a login window and waits up to LOGIN_INLINE_WAIT seconds (default 25, max 45; --login-inline-wait) so a quick sign-in resolves in one call. If the wait elapses, the tool returns a pending signal and the model retries in about 30 seconds. Neither the auto-import nor the inline wait applies under Docker or when the server is bound to a non-loopback HTTP host. Create the session on the host with --login, or use the explicit Docker --login --login-viewer command.

  • Users on slow connections may need higher values for either.

Told to run --login on the host when you already did:

  • If tool calls answer "No valid LinkedIn session is available in Docker" on a machine that is not a container, the runtime was misdetected. This happened on Linux hosts running a Docker daemon for unrelated services. Set LINKEDIN_MCP_CONTAINER=false to override the detection; true forces the opposite.

Using a proxy:

Most people should not use one. LinkedIn's own guidance for reducing security challenges is to avoid a VPN or proxy, and it scores the addresses a session signs in from. A home connection you have used for years is a trust signal; a commercial exit node with a history you cannot see is not, and switching to one is itself the kind of change that triggers a checkpoint. A proxy is worth it in one case: the server runs somewhere its address is obviously a data centre, or in a different country from the account's history. Even then, a WireGuard or Tailscale exit node on your own home network beats any paid provider, because the address really is yours. If you do buy one, take a dedicated static ISP address and keep it, rather than a rotating residential pool.

  • Route the browser through a proxy with --proxy-server http://host:port (http, https, socks4 and socks5 are accepted). Only browser traffic is routed, not the MCP transport.

  • Credentials go in PROXY_USERNAME and PROXY_PASSWORD. There is no --proxy-password flag on purpose: command-line arguments are readable by every other user on the machine. PROXY_SERVER also accepts the combined http://user:pass@host:port form most providers hand out.

  • Chromium cannot authenticate to a SOCKS proxy, so credentials require an http(s) endpoint. If your provider only offers authenticated SOCKS5, run a local relay that holds the credentials and point the server at that.

  • Local addresses go through the proxy too. Chromium's usual direct route for localhost is removed when a proxy is set, so add PROXY_BYPASS=localhost,127.0.0.1,::1 if you need local targets reached directly.

  • Auto-import is skipped while a proxy is configured: a session taken from a local browser was created on your real address, and moving it to the proxy is the very change that triggers a checkpoint. Use --login.

  • A wrong proxy password does not report itself: Chromium retries the authentication challenge until the page times out, so it surfaces as a timeout or a failed sign-in. If sessions stop working right after you add a proxy, check the credentials before assuming the session expired.

  • Set the proxy up before creating the session. Run --login with the proxy already configured. Turning a proxy on for an existing profile moves a logged-in session to a new IP, which is what triggers a LinkedIn checkpoint. The same applies to --import-from-browser, which imports a session created on your real IP. Use a sticky session, not a rotating pool, for the same reason.

Custom Chrome path:

  • If Chrome is installed in a non-standard location, use --chrome-path /path/to/chrome

  • Can also set via environment variable: CHROME_PATH=/path/to/chrome

  • On macOS and Linux the browser must be at least as new as the one that last opened your profile, and the server refuses the launch otherwise. (Not on Windows: a browser there cannot be asked its version without starting one, so the check is off.) An older browser can silently drop stores a newer one wrote, the saved session among them, and the failure then looks exactly like an expired login. The message names both versions. Going back to the bundled Chromium after running a newer Chrome once is the usual way to meet this; either run the newer browser again, whichever one that was, or run --login, which moves the stored session aside and signs in fresh with the browser you have. --logout also clears it but discards the old session instead of keeping it recoverable, and it asks for confirmation on the terminal, so it is not usable from a server an MCP client started.

  • Only Chrome, Chromium and Chrome for Testing are compared this way. Forks number themselves differently (Vivaldi is on 7.x, Edge's build number sits far below Chrome's under the same major), so pointing CHROME_PATH at one turns the check off rather than producing a refusal nothing could satisfy.

IMPORTANT

FAQ

Is this safe to use? Will I get banned? This tool controls a real browser session; it doesn't exploit undocumented APIs or bypass authentication. LinkedIn's User Agreement prohibits automated access, and accounts using automated tools can be restricted or banned. Use at your own risk; there is no guarantee of account safety. If you encounter any issues, let me know in the Discussions.

What if my agents execute too many actions? Tool calls run sequentially through a queue. You are responsible for the volume of automation you run; use it sparingly and prompt your agents responsibly.

Acknowledgements

Built with FastMCP and Patchright.

Use in accordance with LinkedIn's User Agreement. Automated access may violate LinkedIn's terms and can lead to account restrictions. This tool is for personal use only and comes with no warranty of any kind.

License

This project is licensed under the Apache 2.0 license.

Building on it is welcome, including under a different license. Apache-2.0 attaches conditions to that, set out in section 4 of the license. The one most often missed is that the attribution in NOTICE has to travel with what you ship.

Available Tools

17 tools
close_sessionClose SessionA
Destructive

Close the current browser session and clean up resources.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The destructiveHint annotation already signals destructiveness. The description adds 'clean up resources,' which is slightly more specific but does not disclose potential side effects like losing unsaved data or requiring an active session.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single, short sentence that is front-loaded with the main action. Every word adds value, no fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple action and no parameters, the description is fairly complete. It includes resource cleanup context beyond the annotation. However, it could mention that it ends the session and what happens after (e.g., connection closed).

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are no parameters, so the schema covers everything. The description does not need to add parameter information, earning a baseline score of 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it closes the browser session and cleans up resources, which distinguishes it from all other tools that perform different actions like searching, messaging, or fetching profiles.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description does not provide any guidance on when to use this tool versus alternatives, such as noting it should be the final action or that it is not reversible. There are no parameters to indicate context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

connect_with_personConnect With PersonB
Destructive

Send a LinkedIn connection request or accept an incoming one.

The tool is annotated with destructiveHint so MCP clients will prompt for user confirmation before execution.

ParametersJSON Schema
NameRequiredDescriptionDefault
linkedin_usernameYesLinkedIn username (e.g., "stickerdaniel", "williamhgates")
noteNoOptional note to include with the invitation

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description repeats the destructiveHint annotation and explains that clients will prompt for user confirmation, adding context. However, it does not disclose other behaviors such as error handling, idempotency, or what happens if the user is already connected.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, efficient and front-loaded with the key action. It could be slightly more structured, but no words are wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructiveHint tool, the description lacks important context: no mention of return value (despite output schema existing), success/failure conditions, or edge cases like already being connected. This leaves the agent underspecified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description does not add extra meaning beyond the schema; it does not explain note length limits or best practices for the note parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Send a LinkedIn connection request or accept an incoming one.' This is a specific verb+resource pair, and it distinguishes the tool from siblings like send_message or get_person_profile.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description offers no guidance on when to use this tool vs alternatives (e.g., send_message). It does not mention prerequisites, context for acceptance vs sending, or when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_company_employeesGet Company EmployeesA
Read-only

List employees at a company from the LinkedIn /people/ page, including the demographics aggregate that this view exposes: where employees live, where they studied, and a function breakdown (Engineering, Sales, Operations, etc.). The demographics are unique to this tool.

For filtered search by network degree (1st/2nd/3rd) or location, prefer search_people with current_company set to the company URN id. That path also returns more result pages than the /people/ tab.

The optional keywords filter narrows results by name, title, or skill.

company_name must be the exact LinkedIn URL slug (the path segment after /company/), not the display name. LinkedIn assigns unique slugs and the display name often does not match. For example, the AI lab Anthropic lives at /company/anthropicresearch/, not /company/anthropic/. If you are unsure of the slug, call search_companies first and pick the slug from the returned references.

ParametersJSON Schema
NameRequiredDescriptionDefault
company_nameYesLinkedIn company URL slug (e.g., "docker", "anthropicresearch", "microsoft")
keywordsNoOptional filter by name, job title, or skill (e.g., "engineer", "sales")

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint and openWorldHint. The description adds important context: the demographics aggregate is unique to this tool, the 'company_name' must be an exact LinkedIn URL slug (with examples of mismatches), and the keywords filter restricts by name, title, or skill. This surpasses what annotations convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise with five sentences, each carrying meaningful information. It is well-structured: main purpose, alternative usage, optional parameter details, slug requirement with example, and a fallback suggestion. No unnecessary text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the output schema exists (so return details are covered), annotations provide behavioral hints, and sibling tools are listed, the description covers all necessary context: what it does, how to use alternatives, parameter specifics, and how to resolve slug uncertainty. No gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, baseline 3. The description adds value by explaining that 'company_name' is the exact slug (not display name) and provides examples, and clarifies that 'keywords' is an optional filter for name, title, or skill. This goes beyond the schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool lists employees and demographics from the LinkedIn /people/ page, specifying the types of demographics included. It differentiates itself from the sibling 'search_people' tool by mentioning that filtered search by network degree or location should use the alternative.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use this tool versus alternatives: it recommends 'search_people' with current_company set for filtered searches by network degree or location, and advises using 'search_companies' first if uncertain about the company slug.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_company_postsGet Company PostsA
Read-only

Get recent posts from a company's LinkedIn feed.

ParametersJSON Schema
NameRequiredDescriptionDefault
company_nameYesLinkedIn company name (e.g., "docker", "anthropic", "microsoft")

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true and openWorldHint=true, so the description does not need to restate safety. However, it adds no extra behavioral details like pagination or rate limits, meeting only the baseline expectation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise at 8 words, front-loaded with the action, and every word adds value. No wasted text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple with one parameter and an output schema present, so the short description is sufficient. It covers the essential purpose without needing extra detail.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with a clear description for the single parameter. The description adds no additional meaning beyond what is already in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Get') and the resource ('recent posts from a company's LinkedIn feed'). It effectively distinguishes from sibling tools like get_company_profile and get_company_employees.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives (e.g., get_feed) or any prerequisites. The description only states what the tool does without context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_company_profileGet Company ProfileB
Read-only

Get a specific company's LinkedIn profile.

ParametersJSON Schema
NameRequiredDescriptionDefault
company_nameYesLinkedIn company name (e.g., "docker", "anthropic", "microsoft")
sectionsNoComma-separated list of extra sections to scrape. The about page is always included. Available sections: posts, jobs Examples: "posts", "posts,jobs" Default (None) scrapes only the about page.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint=true and openWorldHint=true, so the description need not reiterate safety. However, it adds no behavioral context beyond the schema, such as rate limits, authentication requirements, or response structure. With annotations, a score of 3 is appropriate as the description provides minimal added transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence with no extraneous words. It is appropriately front-loaded and concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the existence of an output schema and full schema parameter coverage, the description is minimally adequate. However, it lacks context to distinguish from sibling tools and does not clarify that the default behavior is to scrape only the about page. It is complete enough but not exemplary.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so parameters are fully documented in the schema. The description adds no additional semantics beyond what the schema already provides for company_name and sections. Baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description states verb 'Get' and resource 'a specific company's LinkedIn profile' clearly. However, it doesn't differentiate from sibling tools like get_company_employees or get_company_posts, which also fetch company data. The purpose is clear but not distinctive.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives like get_company_employees or get_company_posts. The description does not mention when to use it or what prerequisites exist.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_conversationGet ConversationA
Read-only

Read a specific messaging conversation.

Provide either linkedin_username or thread_id to identify the conversation.

When looked up by linkedin_username, resolution searches the messaging inbox for the participant's display name and click-visits every matching row to capture its thread ID — LinkedIn's sidebar has no anchor hrefs or thread-id attributes, so this is the only available path. Each visit selects the row in the LinkedIn UI and may mark it as read. Pass thread_id directly to skip this enumeration.

ParametersJSON Schema
NameRequiredDescriptionDefault
linkedin_usernameNoLinkedIn username of the conversation participant
thread_idNoLinkedIn messaging thread ID
indexNo0-based selector for which thread to open when the participant has multiple threads (e.g. an organic 1-on-1 plus an InMail). Ignored when thread_id is provided. To enumerate thread IDs first, call search_conversations.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses important behavioral detail: using linkedin_username involves click-visits that may mark conversations as read, which adds context beyond readOnlyHint=true. Also explains technical limitation (no anchor hrefs in LinkedIn sidebar). No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Reasonably concise and well-structured. Covers all needed information without excessive verbosity, though slightly longer than necessary.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Comprehensive given the tool complexity: explains input parameters, behavioral nuances, side effects, limitations, and alternative tools. Output schema exists to cover return values, so description focuses on input and behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Adds significant meaning beyond schema: explains how linkedin_username resolution works, that thread_id bypasses enumeration, and that index is 0-based and ignored when thread_id provided. References sibling tool search_conversations for enumeration.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Read a specific messaging conversation' and distinguishes between two identification methods (linkedin_username or thread_id). It differentiates from siblings like get_inbox or search_conversations by specifying what this tool uniquely does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly guides when to use each parameter, warns about side effects (marking as read) when using linkedin_username, and directs users to search_conversations for enumerating thread IDs. Provides clear context for the index parameter.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_feedGet FeedA
Read-only

Get posts from the authenticated user's LinkedIn feed.

ParametersJSON Schema
NameRequiredDescriptionDefault
num_postsNoNumber of posts to fetch (1-50, default 10). Posts are loaded in batches of ~5 as the page scrolls, so the actual count may slightly exceed the target.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint=true and openWorldHint=true. The description adds no extra behavioral context beyond what the schema and annotations convey, such as rate limits or data freshness.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence with no superfluous words, efficiently conveying the tool's purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the low parameter count, full schema coverage, presence of output schema, and informative annotations, the description adequately covers the essentials. It could mention the read-only nature but is otherwise complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter num_posts is fully described in the schema (100% coverage). The description does not add additional meaning beyond what the schema already provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Get posts from the authenticated user's LinkedIn feed', specifying the verb (Get) and resource (feed). This distinguishes it from sibling tools like get_company_posts or get_my_profile.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives. There are no explicit conditions or exclusions, leaving the agent to infer usage from the name alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_inboxGet InboxA
Read-only

List recent conversations from the LinkedIn messaging inbox.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of conversations to load (1-50, default 20)

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint=true and openWorldHint=true, indicating safe read operations with potentially incomplete results. The description adds that it lists 'recent' conversations, implying time-based ordering, which is a behavioral trait beyond annotations. However, it does not disclose pagination behavior or any limitations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence with clear front-loading of the main purpose. No extraneous information is included.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema and simple input (one optional parameter), the description is largely complete. It could be improved by noting the ordering (e.g., by time) or that it returns limited recent conversations, but for a low-complexity tool, it suffices.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100% for the single parameter 'limit', providing baseline value. The description adds no additional semantic meaning beyond the schema, so the score is at the baseline of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool retrieves a list of recent conversations from the LinkedIn messaging inbox, specifying the action, resource, and context. It distinguishes itself from sibling tools like get_conversation (single conversation) and search_conversations (filtered search).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for listing inbox conversations but provides no explicit guidance on when to use this tool versus alternatives like get_conversation or search_conversations. It does not state when not to use it or give context for minimal usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_job_detailsGet Job DetailsA
Read-only

Get job details for a specific job posting on LinkedIn.

ParametersJSON Schema
NameRequiredDescriptionDefault
job_idYesLinkedIn job ID (e.g., "4252026496", "3856789012")

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare readOnlyHint=true and openWorldHint=true, indicating a safe read operation. The description does not contradict these and adds minimal behavioral context (e.g., no mention of rate limits or response structure). With annotations present, the description's lack of additional detail is acceptable.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence that efficiently conveys the purpose. It is front-loaded and to the point, although it could be slightly more informative without verbosity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool with one parameter and an output schema, the description is sufficiently complete. It covers the essential purpose and differentiates from sibling tools, though it could mention the output format or typical use case after search_jobs for full completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the description provides a clear example for the job_id parameter. The description does not add meaning beyond the schema, which is adequate given full coverage. No additional parameter insights are provided.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool gets job details for a specific LinkedIn job posting, using a specific verb and resource. It distinguishes itself from sibling tools like search_jobs, which searches for jobs rather than retrieving details.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. For instance, it doesn't suggest using this after search_jobs or explain how it differs from similar get tools like get_company_profile. The user must infer usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_my_profileGet My ProfileA
Read-only

Get the authenticated user's own LinkedIn profile.

Navigates to /in/me/ and resolves the redirect to obtain the real username before scraping, so the url field in the result is the actual profile URL (e.g. linkedin.com/in/johndoe/) rather than /in/me/.

ParametersJSON Schema
NameRequiredDescriptionDefault
sectionsNoComma-separated list of extra sections to scrape. The main profile page is always included. Available sections: experience, education, interests, honors, languages, certifications, skills, projects, contact_info, posts Examples: "experience,education", "contact_info", "skills,projects" Default (None) scrapes only the main profile page.
max_scrollsNoMaximum pagination attempts per section (same as get_person_profile).

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint. The description adds behavioral details about URL resolution (navigates to /in/me/ and resolves redirect to get actual URL). No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise, with two short paragraphs. It front-loads the core purpose and then provides necessary behavioral detail. No wasted sentences.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the low parameter count, high schema coverage, and presence of output schema and annotations, the description is complete. It explains URL resolution and parameter usage adequately.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with detailed descriptions. The tool description adds context about sections and max_scrolls relating to scraping behavior, but schema already provides full parameter semantics. The description adds marginal value.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it retrieves the authenticated user's own LinkedIn profile, with a specific verb 'Get' and resource 'own profile'. It distinguishes from siblings like get_person_profile by emphasizing 'own' and mentioning URL resolution details.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies this tool is for the user's own profile by saying 'authenticated user's own'. It contrasts with get_person_profile, but does not explicitly state when to use alternatives. Context from sibling names helps, but could be more direct.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_person_profileGet Person ProfileB
Read-only

Get a specific person's LinkedIn profile.

ParametersJSON Schema
NameRequiredDescriptionDefault
linkedin_usernameYesLinkedIn username (e.g., "stickerdaniel", "williamhgates")
sectionsNoComma-separated list of extra sections to scrape. The main profile page is always included. Available sections: experience, education, interests, honors, languages, certifications, skills, projects, contact_info, posts Examples: "experience,education", "contact_info", "skills,projects", "honors,languages", "posts" Default (None) scrapes only the main profile page.
max_scrollsNoMaximum pagination attempts per section to load more content. On detail sections (experience, certifications, skills, etc.) this is the max number of "Show more" button clicks. On activity/posts it is the max scroll-to-bottom iterations. Applies to all sections in this call. Default (None) uses 5 for detail sections and 10 for posts. Increase when a profile has many items in a section (e.g., 30+ certifications, max_scrolls=20). To avoid slowing down other sections, request heavy sections in a separate call.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Description repeats the name and adds no behavioral insight beyond the readOnlyHint and openWorldHint annotations. No mention of data freshness, rate limits, or authorization requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One sentence, no unnecessary words, but could be slightly more informative while remaining concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having output schema, the description omits critical context like the need to obtain a LinkedIn username via search_people, and does not mention that sections can be expensive to scrape.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the description adds no parameter information beyond the schema, which already details sections and max_scrolls. Baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it gets a specific person's LinkedIn profile, directly matching the tool name and distinguishing it from sibling tools like get_my_profile or get_company_profile.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool vs alternatives (e.g., search_people to find username first), nor any prerequisites or conditions for use.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_sidebar_profilesGet Sidebar ProfilesA
Read-only

Get profile links from sidebar recommendation sections on a LinkedIn profile page.

Extracts profiles from "More profiles for you", "Explore premium profiles", and "People you may know" sidebar sections. Follows "Show all" links to return the full list from each section. Sections that redirect to linkedin.com/premium are skipped.

ParametersJSON Schema
NameRequiredDescriptionDefault
linkedin_usernameYesLinkedIn username of the profile page to scrape (e.g., "stickerdaniel", "williamhgates")

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations indicate readOnlyHint=true and openWorldHint=true, and the description adds valuable behavior: it lists specific sections, mentions following 'Show all' links, and notes that premium-redirect sections are skipped. This enriches the transparency beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three sentences long, front-loaded with the main purpose, followed by specific details. Every sentence provides value with no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (scraping multiple dynamic sections), the description covers key behaviors. Since an output schema exists, the return format is handled, but minor aspects like rate limiting or authentication are not mentioned, though acceptable for a read-only tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 100% coverage for the single parameter, with a clear description. The tool description does not add additional parameter semantics beyond what the schema already provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool extracts profile links from specific sidebar sections on a LinkedIn profile page. It specifies the sections and the behavior of following 'Show all' links, making it distinct from sibling tools like get_person_profile or search_people.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains what the tool does but does not explicitly state when to use it versus alternatives or when not to use it. The context from sibling tools implies usage, but direct guidelines are missing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_companiesSearch CompaniesB
Read-only

Search for companies on LinkedIn.

ParametersJSON Schema
NameRequiredDescriptionDefault
keywordsYesSearch keywords (e.g., "fintech", "anthropic", "electric vehicles")

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint. The description adds no additional behavioral details such as pagination, result limits, or operational semantics beyond what annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence with no fluff. However, it is too minimal; it could be structured to include more useful information while remaining concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the existence of an output schema, the description is adequate for a simple search tool with one parameter. However, it lacks any mention of common context such as result types or response structure.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the description does not need to compensate. The description does not add any meaning beyond the existing parameter description in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (Search) and resource (companies) on LinkedIn. It easily distinguishes from sibling tools like search_people, search_jobs, etc.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. No when-not conditions, no context about typical use cases or limitations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_conversationsSearch ConversationsC
Read-only

Search messages by keyword.

ParametersJSON Schema
NameRequiredDescriptionDefault
keywordsYesSearch keywords to filter conversations
limitNoMaximum number of search-result rows to enumerate as conversation references (1-50, default 20). Each enumeration selects the row in LinkedIn's UI and may mark it as read, so a low cap is preferable for noisy queries.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

C2.8/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description does not add any behavioral context beyond what the annotations already provide (readOnlyHint, openWorldHint). It misses the opportunity to disclose that each result row may be marked as read, which is hinted in the schema but not in the tool description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence, making it concise but overly terse. It could be slightly expanded to include usage context without losing conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having an output schema, the description is too minimal. It does not clarify the scope of search (e.g., user's conversations vs. public messages) or provide any guidance on expected output behavior, leaving ambiguity for an AI agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the tool description is not required to add parameter details. However, it adds no extra meaning beyond the schema, resulting in a baseline score of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Search messages') and the resource ('conversations'), using a specific verb and resource. It distinguishes from sibling tools like search_people or get_inbox, though it could be more precise about whether it searches messages within conversations or conversation titles.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives like get_conversation or get_inbox. There is no mention of prerequisites, limitations, or comparison with other search tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_jobsSearch JobsA
Read-only

Search for jobs on LinkedIn.

Returns job_ids that can be passed to get_job_details for full info.

ParametersJSON Schema
NameRequiredDescriptionDefault
keywordsYesSearch keywords (e.g., "software engineer", "data scientist")
locationNoOptional location filter (e.g., "San Francisco", "Remote")
max_pagesNoMaximum number of result pages to load (1-10, default 3)
date_postedNoFilter by posting date (past_hour, past_24_hours, past_week, past_month)
job_typeNoFilter by job type, comma-separated (full_time, part_time, contract, temporary, volunteer, internship, other)
experience_levelNoFilter by experience level, comma-separated (internship, entry, associate, mid_senior, director, executive)
work_typeNoFilter by work type, comma-separated (on_site, remote, hybrid)
easy_applyNoOnly show Easy Apply jobs (default false)
sort_byNoSort results (date, relevance)

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the description need not repeat those. It adds the useful behavioral detail that only job_ids are returned. However, it does not disclose pagination behavior (max_pages) or ordering (sort_by), which are relevant but are covered by the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with zero wasted words. The first sentence states the core purpose, the second describes the output and its intended use. Front-loaded and efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the primary purpose and output but omits nuanced details like pagination limits (though max_pages is in schema) or result ordering. With an output schema present, the bare-bones description is minimally acceptable but could be more complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, meaning all 9 parameters are fully described in the input schema. The description adds no new parameter semantics beyond stating that the output is a list of job_ids. This is adequate but does not enhance understanding of parameter usage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states 'Search for jobs on LinkedIn' providing a clear verb and resource. It also distinguishes from sibling search tools by mentioning the return type (job_ids) and the follow-on tool get_job_details, making its purpose and unique output unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly indicates that results (job_ids) are meant to be passed to get_job_details for full info, implying a workflow. While it does not formally state when not to use this tool, the context of sibling search tools and the explicit output hint provide sufficient guidance for typical usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_peopleSearch PeopleC
Read-only

Search for people on LinkedIn.

ParametersJSON Schema
NameRequiredDescriptionDefault
keywordsYesSearch keywords (e.g., "software engineer", "recruiter at Google")
locationNoOptional location filter (e.g., "New York", "Remote")
networkNoOptional connection-degree filter. Each element is one of "F" (1st-degree), "S" (2nd-degree), "O" (3rd-degree and beyond). Example: ["F"] to only return 1st-degree connections.
current_companyNoOptional current-employer filter. LinkedIn's currentCompany facet only filters on the numeric company URN id (e.g. "1115" for SAP); plain company names are accepted by the URL but ignored by LinkedIn and return the unfiltered result set. Look up a company's URN via get_company_profile -- it is exposed under references["about"]. For company-wide employee demographics (location/education/function breakdown) plus a slug-based lookup, use get_company_employees instead.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Description adds no behavioral context beyond the annotations. Annotations already mark it as read-only and open-world. No mention of rate limits, pagination, or result truncation. Given annotations exist, more context could be added but isn't.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Extremely concise single sentence. No wasted words, but could be slightly more informative without losing conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With output schema present and good parameter descriptions, the description is minimally complete. However, a search tool might benefit from mentioning result limits or the default behavior of filters, which is absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already explains parameters well. The description does not add meaning beyond the schema; it just restates the purpose. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches for people on LinkedIn, specifying the resource ('people') and action ('search'). It's unambiguous, though it could be more specific about the search scope or differentiate from siblings like get_person_profile.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives like get_person_profile or search_companies. The description does not mention context, prerequisites, or when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

send_messageSend MessageA
Destructive

Send a message to a LinkedIn user.

The recipient must be directly messageable from the profile page. This is a write operation when confirm_send is True.

ParametersJSON Schema
NameRequiredDescriptionDefault
linkedin_usernameYesLinkedIn username of the recipient
messageYesThe message text to send
confirm_sendYesMust be True to send the message
profile_urnNoOptional profile URN (e.g. ACoAAB...) to construct the compose URL directly. Providing this bypasses the Message-button lookup and is more reliable when available. Obtain via get_person_profile. Note: inbox may not always show all messages; use search_conversations as a fallback.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate destructiveHint and openWorldHint. The description adds the precondition about direct messageability and confirms the write operation, but does not disclose failure modes, side effects, or other behavioral traits beyond what annotations and schema provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences that each add value: one for purpose, one for usage condition. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given full schema coverage, annotations, and output schema, the description provides the essential precondition. It does not explain error handling or return values, but these are covered by the schema and annotations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds no additional parameter-level meaning beyond what the schema already documents.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Send' and the resource 'a message to a LinkedIn user', which is specific and distinguishes it from sibling tools like 'connect_with_person' or 'get_conversation'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides a key condition ('recipient must be directly messageable from the profile page') and notes the write behavior dependent on confirm_send. However, it does not explicitly compare with alternatives or state when not to use this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

TDQS

A3.6/5.0
Disambiguation5/5

Each tool targets a distinct resource or action (profile, company, job, messaging, search, session) with clear boundaries. Even similar tools like get_person_profile and get_my_profile are distinct through target user. Detailed descriptions prevent confusion.

Naming Consistency5/5

All tools follow a consistent verb_noun pattern using lowercase and underscores (e.g., get_company_profile, search_people, send_message). The naming is uniform and predictable.

Tool Count4/5

With 17 tools, the set is slightly above the typical 3-15 range but still well-scoped for the breadth of LinkedIn interactions covered (profiles, companies, jobs, messaging, feed, search). No tool feels redundant.

Completeness3/5

The tool set covers core read operations and limited write actions (connect, send message). Missing are posting, liking, commenting, or profile updates. For a general LinkedIn assistant, these gaps are notable but the existing tools handle key workflows.

Maintenance

ActivityActive
ResponsivenessWithin a week

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
    F
    maintenance
    Enables searching and scraping of LinkedIn for structured data on people, companies, and job listings. It allows AI clients to retrieve detailed profiles, experience, and activity sections using browser automation.
    7
    182
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants to search, filter, and extract job listings from LinkedIn using an automated headless browser with semantic AI filtering and deduplication.
    25
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Lets an AI assistant operate LinkedIn through an authenticated browser session, enabling profile management, posting, networking, messaging, job search, and automated applications.
    100
    383
    1
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Connects LinkedIn to AI assistants, enabling lead search, profile analysis, messaging, and workflow automation through a cloud browser. Supports sales, recruiting, and market research tasks.
    2
    59
    236
    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/stickerdaniel/linkedin-mcp-server'

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