内容
# LinkedIn MCP
本地Electron桌面应用程序,通过真正的、由Playwright控制的Chromium浏览器驱动 **LinkedIn**,并通过 **stdio** 将该自动化暴露给 **Claude Desktop**(或任何MCP客户端)作为 **MCP服务器**。
不使用LinkedIn的(受限)官方API,应用程序以真实浏览器窗口登录您,保持会话在磁盘上活跃,并允许AI助手调用一组小型、定义明确的工具(查看个人资料、搜索人员/工作/公司、发送消息、发送连接请求、读取动态和通知等)。您可以保持控制:登录是手动和有界面的,因此您自己完成任何2FA/验证码,并且永远不会存储您的密码。
> **注意事项/状态:** 该存储库目前在Electron主进程和驱动程序/MCP层之间存在多个线路错误(不匹配的导入路径、方法名称和构造函数签名)。请参见底部的“已知问题”部分。下文中的README描述了预期的设计和用法。
---
## 功能
- **有界面的、持久的浏览器会话。** 通过`chromium.launchPersistentContext()`启动一个Chromium浏览器,具有磁盘上的配置文件和可移植的`storageState`快照,用于快速验证和恢复。
- **控制面板UI。** Electron窗口显示驱动程序/会话状态、登录面板、MCP客户端指示器和实时活动日志。应用程序还驻留在系统托盘中,并作为MCP服务器在后台运行。
- **通过stdio的MCP服务器。** 将LinkedIn操作作为MCP工具暴露给Claude Desktop,以便调用。所有诊断信息都输出到stderr,因此stdout上的JSON-RPC流保持干净。
- **速率限制感知自动化。** 每个状态更改的操作都经过间隔 (>= ~2s with jitter),选择器更倾向于ARIA/`data-*`/语义锚,而不是LinkedIn的随机CSS类。
---
## 快速启动(npx)
无需克隆 — 直接从npm运行MCP服务器:
```bash
npx -y @mehmoodqureshi/linkedin-mcp
```
第一次安装会自动下载Playwright的Chromium浏览器(通过`postinstall`钩子)。服务器通过stdio与MCP客户端通信,因此通常由MCP客户端启动,而不是手动启动 — 将其添加到您的客户端配置中(参见[连接到MCP客户端](#连接到MCP客户端))。在第一次使用时,调用`linkedin_login`工具:一个有界面的Chromium窗口打开,您可以在其中登录(并清除任何2FA/验证码);然后会话被保存在`~/.linkedin-mcp`下以供未来运行。
```bash
npx -y @mehmoodqureshi/linkedin-mcp --help # 用法 + 配置片段
npx -y @mehmoodqureshi/linkedin-mcp --version
```
同一个软件包还提供了可选的Electron桌面控制面板(托盘 + 活动日志) — 参见[如何运行](#如何运行)以从源代码构建它。
---
## 先决条件
- **Node.js 20+** (项目针对ES2022 / 现代Electron)。
- **npm** (随Node附带)。
- **Playwright的Chromium浏览器。** 通过`postinstall`脚本自动安装,或手动安装:
```bash
npx playwright install chromium
```
安装依赖项:
```bash
npm install
```
这也会通过`postinstall`钩子运行`playwright install chromium`。
---
## 如何运行
> 只需要MCP服务器?使用 [npx快速启动](#快速启动-npx)。本节涵盖可选的Electron桌面应用程序,从源代码构建。
```bash
npm start
```
这会启动Electron控制面板(UI模式)。从那里您可以:
1. **启动驱动程序** – 启动持久的Chromium浏览器。
2. **登录** – 在受控浏览器中打开LinkedIn的登录页面。输入您的凭证并手动完成任何2FA/验证码 **。** 一旦您到达动态页面,会话就会持续。
3. 监视 **会话** 面板切换到 *已认证* 状态,并查看 **活动日志** 获取进度。
要重建编译后的输出(主进程 + 渲染进程)而不进行打包:
```bash
npm run build # tsc for main/driver/mcp + renderer + copy index.html
npm run typecheck # type-check only, no emit
```
打包(需要缺少的`assets/`和`build/`文件,参见已知问题):
```bash
npm run pack # unpacked build
npm run dist # platform installer(s)
```
---
## 连接到MCP客户端
MCP客户端从JSON配置中发现服务器 — 对于Claude Desktop:
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\\Claude\\claude_desktop_config.json`
(Claude Code:`claude mcp add`; 其他客户端使用相同的`command`/`args`形状。)
### 推荐 — npx(无需克隆)
```json
{
"mcpServers": {
"linkedin": {
"command": "npx",
"args": ["-y", "@mehmoodqureshi/linkedin-mcp"]
}
}
}
```
可选的`env`覆盖:`LINKEDIN_MCP_USERDATA`(数据/配置文件目录,默认为`~/.linkedin-mcp`)和`LINKEDIN_HEADLESS=1`(以无界面模式运行Chromium — 但为首次登录保留有界面模式)。
### 替代 — Electron桌面应用程序(从源代码克隆)
在托盘应用程序中运行相同的MCP服务器嵌入式服务器,因此您会看到一个控制面板窗口:
```json
{
"mcpServers": {
"linkedin": {
"command": "./node_modules/.bin/electron",
"args": ["."],
"cwd": "/absolute/path/to/linkedin-mcp",
"env": { "LINKEDIN_MCP_STDIO": "1" }
}
}
}
```
注意:
- **重启客户端** 编辑配置后。
- 两个路径共享磁盘上的相同持久会话,因此通过任一路径登录一次就足够了。
- MCP服务器保留stdout用于JSON-RPC;所有诊断信息都输出到stderr。
---
## MCP工具
所有工具都命名为`linkedin_*`。身份验证/状态工具在未登录时起作用;每个数据/操作工具都需要身份验证会话(首先调用`linkedin_login`)。
| 工具 | 参数 | 描述 |
| --- | --- | --- |
| `linkedin_login` | `email?`, `password?` | 打开有界面的登录流程。可选地预填充凭证;您手动完成2FA/验证码。等待会话,然后使其持久化。密码永远不会存储。 |
| `linkedin_logout` | – | 清除保存的`storageState`快照和配置文件cookie;下一次操作需要新的登录。 |
| `linkedin_status` | – | 返回 `{ status, isLoggedIn, sessionValid }`。在浏览器启动前有效。 |
| `linkedin_get_profile` | `profileUrl` (完整URL或`/in/`前缀) | 打开并抓取成员个人资料:姓名、头衔、位置、简介、经验、教育、技能、连接数。 |
| `linkedin_search_people` | `query`, `filters?` | 人员搜索;返回姓名、头衔、位置、个人资料URL、连接程度。 |
| `linkedin_search_jobs` | `query`, `filters?` | 工作搜索;返回标题、公司、位置、发布时间、简易申请、工作URL。 |
| `linkedin_search_companies` | `query`, `filters?` | 公司搜索;返回名称、行业、关注者、公司URL。 |
| `linkedin_send_message` | `profileUrl`, `message` | 向1级开放个人资料的成员发送直接消息。如果不允许消息,则会清楚地失败。 |
| `linkedin_send_connection` | `profileUrl`, `note?` (<=300字符) | 发送连接请求,可选地附带备注。返回结果:已发送 / 已发送 / 已连接 / 不可用。 |
| `linkedin_get_feed` | `limit?` (1–50,默认为10) | 读取首页动态:作者、文本、时间戳、点赞/评论数、动态URL。 |
| `linkedin_get_notifications` | `limit?` (1–50,默认为20) | 读取通知:类型、执行者、文本、时间戳、URL、已读/未读。 |
| `linkedin_get_conversations` | – | 列出收件箱线程:参与者、片段、对话ID、时间戳、未读状态。 |
每个工具都返回一个JSON有效载荷,位于标准MCP文本内容信封内。如果失败,结果将带有`isError: true`和可操作的消息(例如“未登录到LinkedIn。运行linkedin_login…”)。
> 搜索工具的广告过滤器模式比驱动程序当前应用的过滤器更丰富;目前将高级过滤器视为尽力而为(参见已知问题)。
---
## 会话持久性
两个协作层在重启之间保持登录状态:
1. **持久的Chromium配置文件(主要)。** 浏览器运行在磁盘上的配置文件目录(`<userData>/playwright-profile`)中。Cookie、localStorage和IndexedDB存储在那里,并在应用程序重启时自行存活 — 就像一个普通的浏览器一样“记住”您一样。
2. **便携式`storageState`快照(辅助)。** 登录时(以及正常关闭时),应用程序还会写入`<userData>/linkedin-session.json`。这提供了:
- 快速的 **无浏览器** “我登录了吗?”检查,通过检查LinkedIn `li_at` cookie及其过期时间(无需启动Chromium),以及
- **恢复路径**:如果配置文件损坏,可以通过`context.addCookies(...)`重新注入快照的cookie来重新水化新上下文。
`<userData>`是Electron的每个应用程序的数据目录(例如,macOS上的`~/Library/Application Support/LinkedIn MCP/`)。在Electron之外,它会回落到 `$LINKEDIN_MCP_USERDATA` 或 `~/.linkedin-mcp`。
**有效性** 由存在一个未过期的`li_at` cookie决定。会话cookie(`expires === -1`)被视为有效。`linkedin_status` / UI将此显示为`sessionValid`,而`isLoggedIn`还会针对活动页面进行确认。
**注销** (`linkedin_logout` / 注销按钮)会清除配置文件cookie并删除快照,强制下次手动登录。
会话文件包含等效于实时凭证的cookie — `.gitignore`已经排除了`userData/`、`pw-profile/`和任何`storageState.json`。**永远不要提交登录会话。**
---
## 配置(`.env`)
复制`.env.example`到`.env`。所有值都是可选的:
- `LINKEDIN_EMAIL` / `LINKEDIN_PASSWORD` – 自动登录的预填充。可选;不推荐;首选手动登录,需要2FA/验证码帐户。
- `MCP_LOG_LEVEL` – `error|warn|info|debug|trace`.
- `LINKEDIN_MCP_STDIO` – 设置为`1`,当由Claude Desktop作为stdio MCP服务器启动时(配置注入此)。
- `PLAYWRIGHT_BROWSERS_PATH` – 覆盖Playwright浏览器位置(打包构建指向捆绑的Chromium)。
---
## 项目布局
```
src/
main/ Electron主进程:生命周期、窗口、托盘、IPC、引导
index.ts
ipc-handlers.ts
preload/ 上下文隔离的桥梁,暴露为window.linkedinMCP
index.ts
driver/ Playwright自动化层
browser.ts 浏览器管理器(持久上下文,单例)
session.ts 会话管理器(存储状态持久/验证/恢复)
linkedin.ts LinkedInDriver门面(组成操作模块)
types.ts 共享规范化结果类型
actions/ 身份验证、个人资料、搜索、消息、连接、动态(+ 常见、索引)
mcp/ MCP服务器 + 工具目录/调度
server.ts
tools.ts
claude-desktop-config.ts
renderer/ 控制面板UI(沙盒化,无Node)
index.html
app.ts
```
---
## 已知问题
该代码库 **尚未** 干净地构建/运行。主进程、驱动程序门面和MCP/IPC层是针对略微不同的API编写的。最重要的不匹配之处:
- **`src/main`中的错误导入路径。** `index.ts`和`ipc-handlers.ts`导入`./driver/PlaywrightDriver`和`./mcp/server`,但实际文件是`../driver/linkedin`和`../mcp/server`。没有`PlaywrightDriver`文件或`createDriver`导出(使用`getInstance()` / `LinkedInDriver`)。
- **`linkedin.ts`导入`./actions/messaging`** 但文件是`actions/messages.ts`.
- **驱动程序门面与主进程契约漂移:** 主进程将驱动程序视为`EventEmitter`(`driver.on('status', …)`)并读取`getStatus().state`,而驱动程序既不提供这些也不提供`getStatus()`(返回`{ status, isLoggedIn, sessionValid }`)。
- **MCP服务器API:** `index.ts`导入`getMcpStatus`并调用`startMcpServer({ driver, transport })`,但`server.ts`不公开`getMcpStatus`,而`startMcpServer()`不接受参数。
- **IPC处理程序方法名称与操作模块不匹配:** 例如`profile.view`(应为`getProfile`),`search.people/jobs/companies(query, page)`(应为`searchPeople/searchJobs/searchCompanies(query, filters?)`),`connection.sendRequest`(应为`connections.sendConnectionRequest`),`message.send`(应为`messages.sendMessage`),`notifications.list`(应为`feed.getNotifications`),`feed.read`(应为`feed.getFeed`),以及`auth.login({email,password})`(应为`login(email, password)`)。
- **`AuthActions`构造函数不匹配:** `linkedin.ts`构建`new AuthActions(page, session, {email,password})`,但构造函数是`(page, paths?)`。
- **`tsconfig.json`路径别名** 指向不存在的`src/main/driver`、`src/main/mcp`、`src/shared`。
- **打包资产缺失:** `electron-builder.json`引用`assets/icon.*`、`build/entitlements.mac.plist`和`node_modules/playwright-core/.local-browsers`(项目安装`playwright`,而不是`playwright-core`);`package.json`的`register-mcp`脚本指向不存在的`scripts/register-mcp.ts`。
- _(已解决)_ MCP模式检测:Electron入口现在依赖于`LINKEDIN_MCP_STDIO=1` / `--mcp` / 非TTY stdout,与上述配置匹配。独立的`npx`二进制文件(`dist/cli.js`)不需要此标志 — 它始终是一个MCP stdio服务器。
在期望`npm run build` / `npm start`成功之前,请协调这些。
---
## 免责声明
自动化LinkedIn可能会违反其服务条款,并可能导致速率限制或帐户限制。请使用真实的、被同意的帐户,保持低量,并仅用于个人、合法的目的。您负责如何使用它。