Skip to main content
Glama
akinabudu

LinkedIn MCP Server

by akinabudu
README.md
# LinkedIn MCP Server

A production-ready Model Context Protocol (MCP) server for LinkedIn integration with AI-powered content generation and advanced screenshot capabilities. Automate LinkedIn posting, generate engaging content, and capture beautiful screenshotsβ€”all through a unified MCP interface.

## Features

- πŸ€– **AI-Powered Content Generation** - Generate engaging LinkedIn posts using OpenAI, Google Gemini, or OpenRouter
- πŸ“Έ **Advanced Screenshot Capture** - Capture websites, code snippets, and project demos
- πŸ”„ **Approval Workflow** - Preview and approve posts before publishing
- πŸ“Š **Analytics** - Track post performance and engagement
- 🎨 **Image Processing** - Create collages, optimize for LinkedIn, and format code beautifully
- πŸ” **OAuth 2.0 Authentication** - Secure LinkedIn API integration
- πŸ“ **Project Analysis** - Automatically analyze projects and generate contextual posts

## Installation

### Prerequisites

- **Bun** runtime (latest stable version)
- **Linux** environment (tested on Ubuntu/Debian)
- **Chromium** browser for screenshots
- **LinkedIn API credentials** (see LinkedIn API Setup)

### System Dependencies

```bash
# Install Chromium and required libraries for Puppeteer
sudo apt-get update
sudo apt-get install -y chromium-browser libgbm1 libasound2

# Optional: Native screenshot tools
sudo apt-get install -y imagemagick
```

### Install Project

```bash
# Clone the repository
git clone <your-repo-url>
cd linkedin-mcpserver

# Install dependencies
bun install

# Copy environment template
cp .env.example .env

# Edit .env with your credentials
nano .env
```

## LinkedIn API Setup

### 1. Create a LinkedIn App

1. Go to [LinkedIn Developers](https://www.linkedin.com/developers/apps)
2. Click **Create app**
3. Fill in app details:
   - **App name**: Your MCP Server
   - **LinkedIn Page**: Select or create a page
   - **App logo**: Upload an icon
   - **Privacy policy URL**: Your privacy policy
4. Click **Create app**

### 2. Get API Credentials

1. Navigate to **Auth** tab
2. Copy **Client ID** and **Client Secret**
3. Add redirect URL: `http://localhost:3000/callback`
4. Request necessary permissions:
   - `w_member_social` - Create posts
   - `r_liteprofile` - Read profile
   - `r_organization_social` - Organization analytics

### 3. Get Access Token

There are two methods:

#### Method A: OAuth Flow (Recommended)

```typescript
import { LinkedInAuth } from './src/linkedin/auth.js';

const auth = new LinkedInAuth();
const authUrl = auth.getAuthorizationUrl();
console.log('Visit:', authUrl);

// After authorization, exchange code for token
const tokens = await auth.exchangeCodeForToken(code);
// Save tokens to .env
```

#### Method B: LinkedIn Token Inspector

1. Go to [LinkedIn Token Tools](https://www.linkedin.com/developers/tools/oauth)
2. Select your app
3. Select required scopes
4. Click **Request access token**
5. Copy the access token to `.env`

### 4. Configure Environment

Update `.env` file:

```env
LINKEDIN_CLIENT_ID=your_client_id_here
LINKEDIN_CLIENT_SECRET=your_client_secret_here
LINKEDIN_ACCESS_TOKEN=your_access_token_here
LINKEDIN_REDIRECT_URI=http://localhost:3000/callback

AI_PROVIDER=openai
OPENAI_API_KEY=your_openai_key_here
```

## Configuration

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `LINKEDIN_CLIENT_ID` | LinkedIn app client ID | Required |
| `LINKEDIN_CLIENT_SECRET` | LinkedIn app client secret | Required |
| `LINKEDIN_ACCESS_TOKEN` | LinkedIn access token | Required |
| `LINKEDIN_REFRESH_TOKEN` | LinkedIn refresh token | Optional |
| `AI_PROVIDER` | AI provider: openai, gemini, openrouter | openai |
| `OPENAI_API_KEY` | OpenAI API key | Required if using OpenAI |
| `GEMINI_API_KEY` | Google Gemini API key | Required if using Gemini |
| `OPENROUTER_API_KEY` | OpenRouter API key | Required if using OpenRouter |
| `AI_MODEL` | AI model name | gpt-4 |
| `SCREENSHOT_OUTPUT_DIR` | Screenshot output directory | ./screenshots |
| `SCREENSHOT_DEFAULT_WIDTH` | Default viewport width | 1920 |
| `SCREENSHOT_DEFAULT_HEIGHT` | Default viewport height | 1080 |
| `SCREENSHOT_QUALITY` | Image quality (1-100) | 90 |

## Usage

### Running the Server

```bash
# Development mode
bun run dev

# Production mode
bun run start
```

### Using with MCP Clients

#### VS Code / Cline Configuration

Add to your MCP settings (e.g., `~/.cline/mcp_settings.json`):

```json
{
  "mcpServers": {
    "linkedin": {
      "command": "bun",
      "args": ["run", "/path/to/linkedin-mcpserver/src/index.ts"],
      "env": {
        "LINKEDIN_CLIENT_ID": "your_client_id",
        "LINKEDIN_CLIENT_SECRET": "your_client_secret",
        "LINKEDIN_ACCESS_TOKEN": "your_access_token",
        "OPENAI_API_KEY": "your_openai_key"
      }
    }
  }
}
```

## Available Tools

### 1. `analyze_project`

Analyze a project and generate LinkedIn post content.

```json
{
  "name": "analyze_project",
  "arguments": {
    "project_path": "./my-project",
    "include_commits": true,
    "commit_count": 5,
    "tone": "professional",
    "length": "medium",
    "capture_screenshots": true,
    "screenshot_config": {
      "include_demo": true,
      "localhost_url": "http://localhost:3000",
      "pages_to_demo": ["/", "/dashboard", "/settings"]
    }
  }
}
```

### 2. `create_linkedin_post`

Create and publish a LinkedIn post.

```json
{
  "name": "create_linkedin_post",
  "arguments": {
    "content": "Excited to share my new project! πŸš€\n\nBuilt with React, TypeScript, and Tailwind CSS...",
    "visibility": "PUBLIC",
    "media_urls": ["/path/to/screenshot.png"],
    "require_approval": true
  }
}
```

### 3. `confirm_post`

Confirm or reject a pending post.

```json
{
  "name": "confirm_post",
  "arguments": {
    "request_id": "preview-1234567890",
    "approved": true
  }
}
```

### 4. `preview_post`

Preview a post before publishing.

```json
{
  "name": "preview_post",
  "arguments": {
    "content": "My post content...",
    "visibility": "PUBLIC"
  }
}
```

### 5. `capture_screenshot`

Capture screenshot from URL.

```json
{
  "name": "capture_screenshot",
  "arguments": {
    "source_type": "url",
    "source": "https://example.com",
    "viewport_width": 1920,
    "viewport_height": 1080,
    "full_page": false
  }
}
```

### 6. `capture_project_demo`

Capture multiple pages for project demo.

```json
{
  "name": "capture_project_demo",
  "arguments": {
    "project_url": "http://localhost:3000",
    "pages_to_capture": ["/", "/features", "/pricing"],
    "include_mobile_view": true,
    "create_collage": true
  }
}
```

### 7. `capture_code_snippet`

Capture beautifully formatted code.

```json
{
  "name": "capture_code_snippet",
  "arguments": {
    "file_path": "./src/components/Hero.tsx",
    "line_start": 10,
    "line_end": 30,
    "theme": "github-dark",
    "show_line_numbers": true
  }
}
```

### 8. `create_screenshot_collage`

Combine multiple screenshots.

```json
{
  "name": "create_screenshot_collage",
  "arguments": {
    "screenshot_paths": ["./img1.png", "./img2.png"],
    "layout": "horizontal",
    "spacing": 20
  }
}
```

### 9. `get_profile_info`

Get LinkedIn profile information.

```json
{
  "name": "get_profile_info",
  "arguments": {}
}
```

### 10. `get_post_analytics`

Get post engagement metrics.

```json
{
  "name": "get_post_analytics",
  "arguments": {
    "post_id": "urn:li:share:1234567890"
  }
}
```

## Example Workflows

### Example 1: Full Project Announcement

```typescript
// 1. Analyze project and generate content
const analysis = await analyzedProject({
  project_path: "./my-app",
  capture_screenshots: true,
  screenshot_config: {
    include_demo: true,
    localhost_url: "http://localhost:3000",
    pages_to_demo: ["/", "/dashboard"]
  },
  tone: "professional",
  length: "medium"
});

// 2. Create post with preview
const post = await createLinkedInPost({
  content: analysis.generated_content,
  media_urls: analysis.screenshots,
  visibility: "PUBLIC",
  require_approval: true
});

// 3. Confirm after review
await confirmPost({
  request_id: post.preview_id,
  approved: true
});
```

### Example 2: Code Snippet Share

```typescript
// 1. Capture code screenshot
const code = await captureCodeSnippet({
  file_path: "./src/auth/oauth.ts",
  line_start: 45,
  line_end: 75,
  theme: "github-dark"
});

// 2. Post with manual content
await createLinkedInPost({
  content: "Just implemented OAuth 2.0 authentication! πŸ”\n\nHere's the core logic...",
  media_urls: [code.path],
  require_approval: false
});
```

### Example 3: Weekly Progress Update

```typescript
// Analyze recent commits
const analysis = await analyzeProject({
  include_commits: true,
  commit_count: 20,
  tone: "casual",
  length: "short"
});

// Post update
await createLinkedInPost({
  content: analysis.generated_content,
  visibility: "PUBLIC"
});
```

## Troubleshooting

### Authentication Issues

**Error: "Invalid access token"**
- Verify token is correct in `.env`
- Check if token has expired (LinkedIn tokens expire)
- Request a new token from LinkedIn Developer Portal

**Error: "Insufficient permissions"**
- Verify your app has required scopes: `w_member_social`, `r_liteprofile`
- Re-authorize with correct permissions

### Screenshot Issues

**Error: "Failed to launch browser"**
```bash
# Install Chromium
sudo apt-get install chromium-browser

# If on WSL, you may need:
export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
export PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
```

**Error: "Page load timeout"**
- Increase timeout in code or use `delay_ms` parameter
- Check if localhost server is running
- Verify URL is accessible

### API Rate Limits

LinkedIn has rate limits:
- **Posts**: 100 per day
- **API calls**: Varies by endpoint

The server implements rate limiting and will throw `RateLimitError` when exceeded.

### Memory Issues

If browser instances aren't being cleaned up:
```typescript
// The server automatically cleans up, but you can force cleanup
import { puppeteerService } from './src/screenshots/puppeteer-service.js';
await puppeteerService.cleanup();
```

## Development

### Project Structure

```
linkedin-mcpserver/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ index.ts                 # MCP server entry point
β”‚   β”œβ”€β”€ config.ts               # Configuration management
β”‚   β”œβ”€β”€ linkedin/               # LinkedIn API integration
β”‚   β”‚   β”œβ”€β”€ auth.ts
β”‚   β”‚   β”œβ”€β”€ client.ts
β”‚   β”‚   β”œβ”€β”€ posts.ts
β”‚   β”‚   β”œβ”€β”€ media.ts
β”‚   β”‚   └── types.ts
β”‚   β”œβ”€β”€ project-analysis/       # Project analysis & AI
β”‚   β”‚   β”œβ”€β”€ file-reader.ts
β”‚   β”‚   β”œβ”€β”€ git-analyzer.ts
β”‚   β”‚   β”œβ”€β”€ content-generator.ts
β”‚   β”‚   β”œβ”€β”€ context-builder.ts
β”‚   β”‚   └── ai-providers/
β”‚   β”œβ”€β”€ screenshots/            # Screenshot system
β”‚   β”‚   β”œβ”€β”€ capturer.ts
β”‚   β”‚   β”œβ”€β”€ puppeteer-service.ts
β”‚   β”‚   β”œβ”€β”€ code-formatter.ts
β”‚   β”‚   β”œβ”€β”€ image-processor.ts
β”‚   β”‚   └── types.ts
β”‚   β”œβ”€β”€ tools/                  # MCP tools
β”‚   β”‚   β”œβ”€β”€ analyze-project.ts
β”‚   β”‚   β”œβ”€β”€ create-post.ts
β”‚   β”‚   β”œβ”€β”€ capture-screenshot.ts
β”‚   β”‚   └── ...
β”‚   └── utils/                  # Utilities
β”‚       β”œβ”€β”€ logger.ts
β”‚       β”œβ”€β”€ error-handler.ts
β”‚       └── rate-limiter.ts
β”œβ”€β”€ .env.example
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
└── README.md
```

### Running Tests

```bash
bun test
```

### Building

```bash
bun run build
```

## Security Considerations

- **Never commit `.env` file** with credentials
- **Validate all inputs** before posting to LinkedIn
- **Be careful with screenshots** - don't capture sensitive data
- **Use approval workflow** for important posts
- **Monitor API usage** to avoid rate limits
- **Review generated content** before posting

## Rate Limits

LinkedIn API limits:
- Posts: 100 per day
- Profile reads: More lenient
- Analytics: Limited per hour

The server implements rate limiting to prevent exceeding these limits.

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests if applicable
5. Submit a pull request

## License

MIT License - see LICENSE file for details

## Support

For issues and questions:
- GitHub Issues: [Your repo issues page]
- Documentation: This README
- LinkedIn API Docs: https://docs.microsoft.com/en-us/linkedin/

## Acknowledgments

- Built with [Model Context Protocol](https://modelcontextprotocol.io)
- Uses [Puppeteer](https://pptr.dev) for screenshots
- Powered by [Bun](https://bun.sh) runtime
- AI providers: OpenAI, Google Gemini, OpenRouter

---

**Happy posting! πŸš€**