linkedin-mcp-server
README.md
# LinkedIn MCP Server
A Model Context Protocol (MCP) server implementation that provides seamless integration with LinkedIn's API. This server enables applications to authenticate, access LinkedIn data, and perform various operations through the MCP interface.
## ๐ Features
- **OAuth2 Authentication**: Secure LinkedIn OAuth2 flow with PKCE support
- **User Profile Access**: Retrieve authenticated user profile information
- **Connections Management**: List and manage user connections
- **Search Functionality**: Search for people on LinkedIn
- **Feed Management**: Access and interact with user feed
- **Messaging**: Send direct messages to connections
- **Skills & Recommendations**: Access skills and recommendations data
- **Token Management**: Automatic token refresh and validation
- **Rate Limiting**: Built-in rate limiting to respect API quotas
- **Error Handling**: Comprehensive error handling with detailed messages
- **Logging**: Winston-based logging for debugging and monitoring
- **Security**: CSRF protection, HTTPS support, secure credential storage
## ๐ Prerequisites
- Node.js 16.0.0 or higher
- npm 8.0.0 or higher
- LinkedIn Developer Account
- LinkedIn App credentials (Client ID, Client Secret)
## ๐ Installation
### 1. Clone the Repository
```bash
git clone https://github.com/yourusername/linkedin_mcp_server.git
cd linkedin_mcp_server
```
### 2. Install Dependencies
```bash
npm install
```
### 3. Configure Environment Variables
Create a `.env` file in the root directory with your LinkedIn credentials:
```env
# LinkedIn OAuth Configuration
LINKEDIN_CLIENT_ID=your_client_id_here
LINKEDIN_CLIENT_SECRET=your_client_secret_here
LINKEDIN_REDIRECT_URI=http://localhost:3000/callback
# Server Configuration
PORT=3000
HOST=localhost
NODE_ENV=development
# Optional: Anthropic/OpenAI API Keys
CLAUDE_API_KEY=your_claude_api_key
OPENAI_API_KEY=your_openai_api_key
```
### 4. Get LinkedIn Credentials
1. Visit [LinkedIn Developers](https://www.linkedin.com/developers)
2. Create a new app
3. Copy your **Client ID** and **Client Secret**
4. Add your redirect URI (default: `http://localhost:3000/callback`)
5. Request access to required API endpoints
## ๐ง Configuration
### Environment Variables
See `.env` file for all available configuration options:
| Variable | Description | Default |
|----------|-------------|---------|
| `LINKEDIN_CLIENT_ID` | LinkedIn OAuth Client ID | Required |
| `LINKEDIN_CLIENT_SECRET` | LinkedIn OAuth Client Secret | Required |
| `LINKEDIN_REDIRECT_URI` | OAuth callback URI | http://localhost:3000/callback |
| `PORT` | Server port | 3000 |
| `NODE_ENV` | Environment (dev/prod) | development |
| `RATE_LIMIT_PER_MINUTE` | API rate limit | 60 |
| `LOG_LEVEL` | Logging level | info |
## ๐ Usage
### Starting the Server
**Development Mode** (with auto-reload):
```bash
npm run dev
```
**Production Mode**:
```bash
npm start
```
### OAuth Authentication Flow
#### 1. Get Authorization URL
```javascript
import LinkedInOAuth from './src/oauth.js';
const oauth = new LinkedInOAuth(
process.env.LINKEDIN_CLIENT_ID,
process.env.LINKEDIN_CLIENT_SECRET,
process.env.LINKEDIN_REDIRECT_URI
);
const authUrl = oauth.getAuthorizationUrl();
console.log('Visit:', authUrl);
```
#### 2. Handle Callback
```javascript
// After user authorizes, LinkedIn redirects with 'code' and 'state'
const token = await oauth.exchangeCodeForToken(code, state);
console.log('Access Token:', token.accessToken);
```
#### 3. Use Access Token
```javascript
import LinkedInClient from './src/linkedin.js';
const client = new LinkedInClient(token.accessToken);
const profile = await client.getProfile();
console.log('User Profile:', profile);
```
## ๐ API Documentation
### LinkedInClient Methods
#### Profile Operations
```javascript
// Get authenticated user's profile
const profile = await client.getProfile();
// Get specific user's profile
const userProfile = await client.getUserProfile(userId);
// Get user's email
const email = await client.getEmail();
```
#### Connections
```javascript
// Get user's connections (paginated)
const connections = await client.getConnections(start = 0, count = 10);
```
#### Search
```javascript
// Search for people
const results = await client.searchPeople('software engineer', count = 10);
```
#### Social Features
```javascript
// Get user's skills
const skills = await client.getSkills();
// Get received recommendations
const recommendations = await client.getRecommendations();
// Get job experience
const experience = await client.getExperience();
// Get user's feed
const feed = await client.getFeed(count = 10);
```
#### Messaging
```javascript
// Send a message to a connection
const result = await client.sendMessage(recipientId, 'Hello!');
```
#### Posting
```javascript
// Create a share (post)
const share = await client.createShare('This is my new post!', 'TEXT_ONLY');
```
### LinkedInOAuth Methods
```javascript
// Get authorization URL
const authUrl = oauth.getAuthorizationUrl();
// Exchange authorization code for token
const token = await oauth.exchangeCodeForToken(code, state);
// Refresh an access token
const newToken = await oauth.refreshAccessToken(refreshToken);
// Revoke a token
await oauth.revokeToken(accessToken);
// Validate token
const validation = await oauth.validateToken(accessToken);
// Get user info
const userInfo = await oauth.getUserInfo(accessToken);
```
## ๐งช Testing
Run all tests:
```bash
npm test
```
Run tests in watch mode:
```bash
npm run test:watch
```
Generate coverage report:
```bash
npm run test:coverage
```
## ๐ Code Quality
### Linting
```bash
# Check code style
npm run lint
# Fix linting issues
npm run lint:fix
```
### Formatting
```bash
# Format code with Prettier
npm run format
# Check formatting
npm run format:check
```
## ๐ Project Structure
```
linkedin_mcp_server/
โโโ src/
โ โโโ index.js # Server entry point
โ โโโ linkedin.js # LinkedIn API client
โ โโโ oauth.js # OAuth2 implementation
โ โโโ ...
โโโ tests/
โ โโโ linkedin.test.js
โ โโโ oauth.test.js
โโโ .env # Environment configuration
โโโ .gitignore # Git ignore rules
โโโ package.json # Project dependencies
โโโ README.md # This file
```
## ๐ Security Considerations
1. **Never commit `.env` file** - Contains sensitive credentials
2. **Use HTTPS in production** - Set `USE_HTTPS=true` and provide SSL certificates
3. **Validate state parameter** - CSRF protection in OAuth flow
4. **Store tokens securely** - Use environment variables or secure storage
5. **Implement rate limiting** - Configured via `RATE_LIMIT_PER_MINUTE`
6. **Refresh tokens regularly** - Call `refreshAccessToken()` before expiration
7. **Use PKCE flow** - Enable with `usePKCE: true` in OAuth methods
## ๐ Troubleshooting
### Common Issues
**Issue: "Invalid client credentials"**
- Verify `LINKEDIN_CLIENT_ID` and `LINKEDIN_CLIENT_SECRET` are correct
- Check credentials in LinkedIn Developer Portal
**Issue: "Redirect URI mismatch"**
- Ensure `LINKEDIN_REDIRECT_URI` matches app configuration in LinkedIn Developer Portal
- URIs are case-sensitive
**Issue: "Token expired"**
- Call `refreshAccessToken()` with refresh token
- Implement automatic token refresh before expiration
**Issue: "Rate limit exceeded"**
- Reduce request frequency
- Increase `RATE_LIMIT_PER_MINUTE` in `.env`
- Implement exponential backoff
## ๐ฆ Dependencies
### Core Dependencies
- **express**: Web framework
- **node-fetch**: HTTP requests
- **jsonwebtoken**: JWT handling
- **dotenv**: Environment variables
- **axios**: HTTP client
- **winston**: Logging
- **helmet**: Security headers
- **cors**: CORS handling
### Dev Dependencies
- **jest**: Testing framework
- **eslint**: Code linting
- **prettier**: Code formatting
- **nodemon**: Development auto-reload
## ๐ Examples
### Basic Usage Example
```javascript
import LinkedInOAuth from './src/oauth.js';
import LinkedInClient from './src/linkedin.js';
import 'dotenv/config';
async function main() {
// Initialize OAuth
const oauth = new LinkedInOAuth(
process.env.LINKEDIN_CLIENT_ID,
process.env.LINKEDIN_CLIENT_SECRET,
process.env.LINKEDIN_REDIRECT_URI
);
// Get authorization URL
const authUrl = oauth.getAuthorizationUrl();
console.log('Please visit:', authUrl);
// After user authorizes, exchange code for token
const token = await oauth.exchangeCodeForToken(code, state);
// Initialize client with access token
const client = new LinkedInClient(token.accessToken);
// Fetch profile
const profile = await client.getProfile();
console.log('Profile:', profile);
// Get connections
const connections = await client.getConnections(0, 10);
console.log('Connections:', connections);
// Search for people
const searchResults = await client.searchPeople('AI Engineer', 5);
console.log('Search Results:', searchResults);
}
main().catch(console.error);
```
### Express Integration
```javascript
import express from 'express';
import LinkedInOAuth from './src/oauth.js';
import LinkedInClient from './src/linkedin.js';
const app = express();
const oauth = new LinkedInOAuth(
process.env.LINKEDIN_CLIENT_ID,
process.env.LINKEDIN_CLIENT_SECRET,
process.env.LINKEDIN_REDIRECT_URI
);
// Redirect to LinkedIn login
app.get('/login', (req, res) => {
const authUrl = oauth.getAuthorizationUrl();
res.redirect(authUrl);
});
// Handle OAuth callback
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
try {
const token = await oauth.exchangeCodeForToken(code, state);
req.session.token = token;
res.redirect('/dashboard');
} catch (error) {
res.status(400).send('Authentication failed');
}
});
// Protected route
app.get('/dashboard', async (req, res) => {
const client = new LinkedInClient(req.session.token.accessToken);
const profile = await client.getProfile();
res.json(profile);
});
app.listen(3000, () => console.log('Server running on :3000'));
```
## ๐ค Contributing
Contributions are welcome! Please follow these steps:
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
3. Commit changes (`git commit -m 'Add AmazingFeature'`)
4. Push to branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request
### Development Guidelines
- Write tests for new features
- Follow ESLint and Prettier rules
- Update documentation
- Add comments for complex logic
## ๐ License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## ๐ Links
- [LinkedIn API Documentation](https://docs.microsoft.com/en-us/linkedin/)
- [Model Context Protocol](https://modelcontextprotocol.io/)
- [OAuth 2.0 Standard](https://tools.ietf.org/html/rfc6749)
- [PKCE RFC](https://tools.ietf.org/html/rfc7636)
## ๐ง Support
For issues, questions, or suggestions:
- Open an [GitHub Issue](https://github.com/yourusername/linkedin_mcp_server/issues)
- Contact: your.email@example.com
## ๐ Acknowledgments
- LinkedIn API Documentation
- MCP Community
- Contributors and testers
---
**Last Updated**: 2024
**Version**: 1.0.0
**Status**: Active Development
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues