Skip to main content
Glama
README.md
# LinkedIn MCP 服务

本项目实现了一个模型上下文协议(MCP)服务器,使用 Playwright 自动化 LinkedIn 招聘流程中的关键功能。它提供以下工具:

- 检查和执行 LinkedIn 登录(支持可选的验证码提交)
- 应用人员搜索过滤器
- 获取分页的人员搜索结果
- 获取任何 LinkedIn 成员的个人资料详情

> ⚠️ 请负责任地使用此集成。遵守 LinkedIn 的服务条款和速率限制;缓存并重用会话,而不是每次调用都重新登录。

## 快速开始

1. 安装依赖(Playwright 会自动下载 Chromium):
   ```bash
   pnpm install
   pnpm exec playwright install chromium
   ```
2. 复制 `.env.example` 到 `.env`(或 `.env.local`)并设置凭据:
   ```bash
   cp .env.example .env.local
   ```
3. 以监视模式运行 MCP 服务器:
   ```bash
   pnpm dev
   ```

环境变量:

- `LINKEDIN_EMAIL` / `LINKEDIN_PASSWORD` – 当 MCP 客户端未提供凭据时的可选默认值
- `PLAYWRIGHT_HEADLESS` – 设置为 `false` 可观察浏览器运行
- `PLAYWRIGHT_USER_DATA_DIR` – 持久化 Chromium 配置文件的自定义路径

## 可用工具

- `linkedin.login` – 检查或执行登录。输入支持 `email`、`password`、`verificationCode` 和可选的 `sessionKey`(用于管理多个用户存储)
- `linkedin.search.applyFilters` – 应用过滤器并返回生成的搜索 URL,不抓取结果
- `linkedin.search.people` – 返回搜索结果元数据,支持分页(`filters.page`)
- `linkedin.profile.details` – 抓取成员个人资料(工作经历、教育背景、技能、简介)

所有工具都接受 `verificationCode` 来完成检查点挑战,并返回结构化的 JSON(或描述下一步操作的错误信息)。

## 使用说明

### 1. 配置 MCP 客户端

在 Claude Desktop 或其他 MCP 客户端中配置此服务器。编辑配置文件(例如 `~/Library/Application Support/Claude/claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "linkedin": {
      "command": "node",
      "args": ["/path/to/linkedin-mcp/dist/index.js"],
      "env": {
        "LINKEDIN_EMAIL": "your-email@example.com",
        "LINKEDIN_PASSWORD": "your-password",
        "PLAYWRIGHT_HEADLESS": "true"
      }
    }
  }
}
```

### 2. 基本使用流程

#### 第一步:登录 LinkedIn

```json
{
  "tool": "linkedin.login",
  "arguments": {
    "email": "your-email@example.com",
    "password": "your-password"
  }
}
```

**成功响应:**
```json
{
  "service": "linkedin.login",
  "headless": true,
  "result": {
    "status": "success",
    "message": "已成功登录 LinkedIn"
  }
}
```

**需要验证码时的响应:**
```json
{
  "service": "linkedin.login",
  "error": "需要验证码",
  "loginResult": {
    "status": "challenge",
    "challengeUrl": "https://www.linkedin.com/checkpoint/challenge/...",
    "message": "LinkedIn 要求验证。请提供验证码。"
  }
}
```

**提交验证码:**
```json
{
  "tool": "linkedin.login",
  "arguments": {
    "email": "your-email@example.com",
    "password": "your-password",
    "verificationCode": "123456"
  }
}
```

#### 第二步:搜索人员

**应用搜索过滤器(仅获取 URL):**

```json
{
  "tool": "linkedin.search.applyFilters",
  "arguments": {
    "email": "your-email@example.com",
    "filters": {
      "keywords": "软件工程师",
      "locations": ["中国", "北京"],
      "currentCompanies": ["腾讯", "阿里巴巴"],
      "industries": ["互联网"]
    }
  }
}
```

**响应:**
```json
{
  "service": "linkedin.search.applyFilters",
  "searchUrl": "https://www.linkedin.com/search/results/people/?keywords=...",
  "loginResult": {
    "status": "already_logged_in"
  }
}
```

**获取搜索结果(带分页):**

```json
{
  "tool": "linkedin.search.people",
  "arguments": {
    "email": "your-email@example.com",
    "filters": {
      "keywords": "产品经理",
      "locations": ["上海"],
      "page": 1
    }
  }
}
```

**响应:**
```json
{
  "service": "linkedin.search.people",
  "results": [
    {
      "name": "张三",
      "headline": "高级产品经理 @ 某科技公司",
      "location": "上海",
      "profileUrl": "https://www.linkedin.com/in/zhangsan"
    }
  ],
  "totalResults": 150,
  "currentPage": 1,
  "loginResult": {
    "status": "already_logged_in"
  }
}
```

#### 第三步:获取个人资料详情

```json
{
  "tool": "linkedin.profile.details",
  "arguments": {
    "email": "your-email@example.com",
    "profileUrl": "https://www.linkedin.com/in/zhangsan"
  }
}
```

**响应:**
```json
{
  "service": "linkedin.profile.details",
  "profile": {
    "name": "张三",
    "headline": "高级产品经理",
    "summary": "拥有 8 年产品经验...",
    "location": "上海",
    "experiences": [
      {
        "title": "高级产品经理",
        "company": "某科技公司",
        "duration": "2020 - 至今",
        "description": "负责产品规划和设计..."
      }
    ],
    "education": [
      {
        "school": "清华大学",
        "degree": "计算机科学学士",
        "years": "2010 - 2014"
      }
    ],
    "skills": ["产品管理", "用户体验设计", "数据分析"]
  }
}
```

### 3. 高级功能

#### 使用 sessionKey 管理多个账户

```json
{
  "tool": "linkedin.login",
  "arguments": {
    "email": "account1@example.com",
    "password": "password1",
    "sessionKey": "recruiter-account-1"
  }
}
```

不同的 `sessionKey` 将创建独立的浏览器会话,允许同时管理多个 LinkedIn 账户。

#### 使用低级查询参数

对于高级搜索需求,可以直接传递 LinkedIn 的原始查询参数:

```json
{
  "tool": "linkedin.search.people",
  "arguments": {
    "email": "your-email@example.com",
    "filters": {
      "queryParams": {
        "geoUrn": "103644278",
        "network": ["F", "S"],
        "resultType": "PEOPLE"
      }
    }
  }
}
```

### 4. 错误处理

所有工具调用都可能返回以下错误状态:

- **认证错误**:需要重新登录或提供验证码
- **网络错误**:LinkedIn 服务不可用
- **速率限制**:请求过于频繁,需要等待
- **无效参数**:输入参数格式错误

**错误响应示例:**
```json
{
  "isError": true,
  "content": [
    {
      "type": "text",
      "text": "{\"service\":\"linkedin.search.people\",\"error\":\"认证失败,请重新登录\"}"
    }
  ]
}
```

### 5. 最佳实践

1. **会话复用**:登录后,浏览器会话会被缓存。后续调用无需重复登录。
2. **尊重速率限制**:避免短时间内发送大量请求,建议在请求之间添加延迟。
3. **错误重试**:遇到临时错误时,等待几秒后重试。
4. **验证码处理**:当收到 `status: "challenge"` 响应时,及时处理验证码。
5. **数据缓存**:对于不经常变化的数据(如个人资料),建议在应用层进行缓存。

## 开发说明

- 浏览器会话使用 Playwright 持久化配置文件按 `sessionKey` 缓存
- `SearchService` 允许使用底层的 `filters.queryParams` 来转发原始 LinkedIn 查询参数,当你已经知道精确的 URN 时(例如 `geoUrn` 或 `network` facets)
- 当 LinkedIn 提示检查点验证时,工具会返回 `status: "challenge"` 以及 `challengeUrl`;从用户那里收集验证码并使用 `verificationCode` 重新运行

## 测试

当前测试套件是一个占位符。你可以通过指向受控的 LinkedIn 沙盒账户来添加集成冒烟测试(使用模拟凭据):

```bash
pnpm test
```

对于生产环境,在没有明确合规审查的情况下,避免针对真实个人资料运行自动化测试。