Skip to main content
Glama
Ramankumar6566

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