├── .gitignore ├── .python-version ├── Dockerfile ├── LICENSE ├── README.md ├── pyproject.toml ├── smithery.yaml └── src └── mcp_linkedin ├── __init__.py └── client.py /.gitignore: -------------------------------------------------------------------------------- 1 | explore.py 2 | __pycache__ -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.13 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile 2 | # Use the official Python image with a slim variant 3 | FROM python:3.9-slim 4 | 5 | # Set the working directory in the container 6 | WORKDIR /app 7 | 8 | # Copy the pyproject.toml file and src directory into the container 9 | COPY pyproject.toml /app/ 10 | COPY src /app/src 11 | 12 | # Install the dependencies specified in the pyproject.toml 13 | RUN pip install --upgrade pip 14 | RUN pip install hatchling 15 | RUN pip install . 16 | 17 | # Set environment variables for LinkedIn credentials 18 | # These should be set during container run or through a Docker secret mechanism 19 | ENV LINKEDIN_EMAIL=your_linkedin_email 20 | ENV LINKEDIN_PASSWORD=your_linkedin_password 21 | 22 | # Set the entry point for the container 23 | ENTRYPOINT ["uvicorn", "mcp_linkedin.client:mcp.run", "--host", "0.0.0.0", "--port", "8000"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MCP LinkedIn 2 | 3 | A Model Context Protocol (MCP) server that provides tools to interact with LinkedIn's Feeds and Job API. 4 | 5 | This is using unofficial LinkedIn API via [Linkedin-api](https://github.com/tomquirk/linkedin-api). Use at your own risk. 6 | 7 | Smithery Badge 8 | mcp-linkedin MCP server 9 | 10 | ### Installing via Smithery 11 | 12 | To install LinkedIn Interaction Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/mcp-linkedin): 13 | 14 | ```bash 15 | npx -y @smithery/cli install mcp-linkedin --client claude 16 | ``` 17 | 18 | ## Configuration 19 | 20 | ```json 21 | { 22 | "mcpServers": { 23 | "linkedin": { 24 | "command": "uvx", 25 | "args": ["--from", "git+https://github.com/adhikasp/mcp-linkedin", "mcp-linkedin"], 26 | "env": { 27 | "LINKEDIN_EMAIL": "your_linkedin_email", 28 | "LINKEDIN_PASSWORD": "your_linkedin_password" 29 | } 30 | } 31 | } 32 | } 33 | ``` 34 | 35 | ## Sample usage 36 | 37 | Using [mcp-client-cli](https://github.com/adhikasp/mcp-client-cli) 38 | 39 | ### Get feed posts 40 | 41 | ``` 42 | $ llm whats happening in linkedin feed? Do not mention names 43 | 44 | I'll retrieve the recent LinkedIn feed posts without mentioning any specific names. 45 | 46 | Tool Calls: 47 | get_feed_posts 48 | Args: 49 | limit: 10 50 | 51 | Based on the LinkedIn feed, here are the key highlights without mentioning specific names: 52 | 53 | 1. Data Science & Cybersecurity Insights: 54 | - A post about a massive dataset from a major tech company 55 | - Includes 13 million pieces of evidence 56 | - Covers SIEM telemetry from 6,100 organizations 57 | - Represents 441 MITRE ATT&CK techniques 58 | 59 | 2. DevOps Community Event: 60 | - A local DevOps meetup was held at a tech company office 61 | - Discussed topics like AWS Lambda, Kubernetes scaling, Terraform 62 | - Highlighted the importance of networking and continuous learning 63 | - Shared experiences of transitioning between tech roles 64 | 65 | 3. Linux Engineering Perspectives: 66 | - Insights into what makes a great Linux engineer 67 | - Key skills include: 68 | * Command-line troubleshooting 69 | * Process management 70 | * Networking fundamentals 71 | * System architecture understanding 72 | * Task automation 73 | * Shell scripting 74 | 75 | 4. Professional Networking: 76 | - Some posts about job seeking 77 | - Discussions about career transitions 78 | - Sharing professional experiences and learnings 79 | 80 | 5. Tech Community Reflections: 81 | - Posts about inspirational figures 82 | - Discussions on professional growth 83 | - Reflections on learning and personal development 84 | 85 | The feed showcases a mix of technical insights, professional networking, career development, and community engagement across various tech domains. 86 | ``` 87 | 88 | ### Search for jobs 89 | 90 | ``` 91 | $ llm search for 3 data engineer jobs in jakarta. For each job check if it a good match for me by analyzing it against my resume in file resume.md. Do not mention company name in your result but only generic area where the company business is about 92 | 93 | I'll help you find data engineer jobs in Jakarta and analyze them against your resume. I'll break this down into steps: 94 | 95 | 1. First, I'll search for data engineer jobs in Jakarta 96 | 2. Then, I'll read your resume 97 | 3. Finally, I'll analyze the job matches 98 | 99 | Let's start by searching for jobs: 100 | 101 | Tool Calls: 102 | search_jobs 103 | Args: 104 | keywords: data engineer 105 | location: Jakarta 106 | limit: 3 107 | 108 | 109 | Now, I'll read your resume: 110 | 111 | Tool Calls: 112 | read_file 113 | Args: 114 | path: resume.md 115 | Secure MCP Filesystem Server running on stdio 116 | Allowed directories: [ '/home/adhikasp', '/mnt/e' ] 117 | 118 | 119 | Now, I'll analyze the jobs against your resume: 120 | 121 | 1. Job in Financial Technology Sector (Lead Backend Engineer): 122 | - Match Score: 85% 123 | - Strengths: 124 | * Requires experience in developing scalable microservices (aligns with your Grab experience) 125 | * Strong focus on cloud infrastructure (AWS, GCP) - matches your cloud skills 126 | * Requires experience with event-driven architecture and message brokers 127 | * Requires Go or Python programming (your proficient languages) 128 | - Potential Gaps: 129 | * Specific focus on credit risk and fraud detection might require additional domain knowledge 130 | * Emphasis on team leadership (6+ years experience leading teams) 131 | 132 | 2. Job in Financial Technology Sector (DevOps Engineer): 133 | - Match Score: 75% 134 | - Strengths: 135 | * Requires cloud platform experience (AWS, GCP) 136 | * Needs containerization skills (Docker, Kubernetes) 137 | * Requires scripting in Python and Bash 138 | * Needs infrastructure as code experience 139 | - Potential Gaps: 140 | * More DevOps focused compared to your backend engineering background 141 | * Specific monitoring tools might require additional learning 142 | 143 | 3. Job in Enterprise Collaboration Software (Solution Engineer): 144 | - Match Score: 60% 145 | - Strengths: 146 | * Requires strong communication skills 147 | * Technical consulting background 148 | * Experience with enterprise software 149 | - Potential Gaps: 150 | * Less technical compared to your current role 151 | * More sales-oriented solution engineering 152 | * Less hands-on technical development 153 | 154 | Recommendation: 155 | The first job (Lead Backend Engineer) appears to be the best match for your skills and experience. It closely aligns with your current work, particularly in terms of backend development, cloud infrastructure, and working with distributed systems. The role's requirements in Go/Python, microservices, and event-driven architecture are very compatible with your professional background. 156 | 157 | Would you like me to elaborate on any of these job analyses or provide more detailed insights? 158 | ``` 159 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "mcp-linkedin" 7 | version = "0.1.1" 8 | description = "MCP server to interact with LinkedIn" 9 | readme = "README.md" 10 | requires-python = ">=3.7" 11 | dependencies = [ 12 | "linkedin-api", 13 | "fastmcp", 14 | "requests", 15 | "uvicorn", 16 | ] 17 | authors = [ 18 | { name = "Adhika Setya Pramudita", email = "adhika.setya.p@gmail.com" } 19 | ] 20 | 21 | [project.urls] 22 | Homepage = "https://github.com/adhikasp/mcp-linkedin" 23 | 24 | [project.scripts] 25 | mcp-linkedin = "mcp_linkedin.client:mcp.run" -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | type: object 8 | required: 9 | - linkedinEmail 10 | - linkedinPassword 11 | properties: 12 | linkedinEmail: 13 | type: string 14 | description: Your LinkedIn email address 15 | linkedinPassword: 16 | type: string 17 | description: Your LinkedIn password 18 | commandFunction: 19 | # A function that produces the CLI command to start the MCP on stdio. 20 | |- 21 | (config) => ({command: 'uvicorn', args: ['mcp_linkedin.client:mcp.run', '--host', '0.0.0.0', '--port', '8000'], env: {LINKEDIN_EMAIL: config.linkedinEmail, LINKEDIN_PASSWORD: config.linkedinPassword}}) -------------------------------------------------------------------------------- /src/mcp_linkedin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adhikasp/mcp-linkedin/9a29ece56c675cd910d6bfbd2e47a93f3b99140c/src/mcp_linkedin/__init__.py -------------------------------------------------------------------------------- /src/mcp_linkedin/client.py: -------------------------------------------------------------------------------- 1 | from linkedin_api import Linkedin 2 | from fastmcp import FastMCP 3 | import os 4 | import logging 5 | 6 | mcp = FastMCP("mcp-linkedin") 7 | logger = logging.getLogger(__name__) 8 | 9 | def get_client(): 10 | return Linkedin(os.getenv("LINKEDIN_EMAIL"), os.getenv("LINKEDIN_PASSWORD"), debug=True) 11 | 12 | @mcp.tool() 13 | def get_feed_posts(limit: int = 10, offset: int = 0) -> str: 14 | """ 15 | Retrieve LinkedIn feed posts. 16 | 17 | :return: List of feed post details 18 | """ 19 | client = get_client() 20 | try: 21 | post_urns = client.get_feed_posts(limit=limit, offset=offset) 22 | except Exception as e: 23 | logger.error(f"Error: {e}") 24 | return f"Error: {e}" 25 | 26 | posts = "" 27 | for urn in post_urns: 28 | posts += f"Post by {urn["author_name"]}: {urn["content"]}\n" 29 | 30 | return posts 31 | 32 | @mcp.tool() 33 | def search_jobs(keywords: str, limit: int = 3, offset: int = 0, location: str = '') -> str: 34 | """ 35 | Search for jobs on LinkedIn. 36 | 37 | :param keywords: Job search keywords 38 | :param limit: Maximum number of job results 39 | :param location: Optional location filter 40 | :return: List of job details 41 | """ 42 | client = get_client() 43 | jobs = client.search_jobs( 44 | keywords=keywords, 45 | location_name=location, 46 | limit=limit, 47 | offset=offset, 48 | ) 49 | job_results = "" 50 | for job in jobs: 51 | job_id = job["entityUrn"].split(":")[-1] 52 | job_data = client.get_job(job_id=job_id) 53 | 54 | job_title = job_data["title"] 55 | company_name = job_data["companyDetails"]["com.linkedin.voyager.deco.jobs.web.shared.WebCompactJobPostingCompany"]["companyResolutionResult"]["name"] 56 | job_description = job_data["description"]["text"] 57 | job_location = job_data["formattedLocation"] 58 | 59 | job_results += f"Job by {job_title} at {company_name} in {job_location}: {job_description}\n\n" 60 | 61 | return job_results 62 | 63 | if __name__ == "__main__": 64 | print(search_jobs(keywords="data engineer", location="Jakarta", limit=2)) --------------------------------------------------------------------------------