内容
# FBEM — 脸书扩展爬虫 + MCP
**爬取/快照脸书原生创作器网页 API 并通过 Chrome 扩展发布短片 / 照片
通过 MCP 服务器向任何代理暴露。**
FBEM 驱动与 *登录的人在 facebook.com 上使用的相同内部网页 API*
(不是 Graph API,它会抑制 API 发布帖子的影响范围)。它通过**被动快照**
您手动进行的请求来学习确切的上传形状,然后**重放**该模板并带有新媒体 + 新的临时令牌 —
因此它是自我修复的,并且在脸书旋转其有效载荷时无需反向工程。
任何具有 MCP 功能的代理(Claude Code / Claude Desktop / Cursor / …)都可以插入并
调用 `post_reel`、`post_photos`、`switch_profile`、`get_identity`、`health` 和
`capture_status`。添加新工具只需要一个文件 — 请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。
> ⚠️ **本地工具,仅限环回。** 桥绑定 `127.0.0.1` 且按设计未经身份验证。
> 它在您的机器上针对您的登录浏览器会话运行。永远不要将其暴露在网络上。
> 仅在您拥有的帐户上使用,并符合脸书的条款。
---
## 架构

```
任意 MCP 代理 (Claude Code / Desktop / Cursor / …)
│ stdio (MCP 协议)
▼
fbem-mcp ──HTTP(loopback :47102)──► fbem-bridge ──WS(:9224)──► Chrome 扩展
(FastMCP; (FastAPI + (爬虫 + 重放,
工具/ = 一个文件一个工具) WS 服务器) 注入页面)
│ 在内部运行
▼
facebook.com 标签页
(您的实时会话)
```
- **`fbem-bridge`** (持久)持有与扩展的 WebSocket,提供
媒体服务并存储捕获的模板。运行一次;保持运行。
- **`fbem-mcp`** (由代理生成)是一个薄的 stdio 层,通过 HTTP 调用桥。
因为它是独立的,所以每个代理都可以生成自己的 MCP 进程,而不必争夺扩展的 WebSocket 端口。
- **Chrome 扩展** 在页面的上下文中执行三个任务:
1. **爬虫** — monkeypatches `fetch`/`XHR` 以**被动**快照真实的
原生上传请求,当您手动发布时。永远不会阻止或改变它们。
2. **令牌** — 抓取新的临时令牌(`fb_dtsg`、`lsd`、`jazoest`,…)。
3. **重放** — 使用新媒体 + 新令牌重放捕获的模板。
## 工作原理:捕获-重放
重放**基于模板**,仅在每种类型捕获一次后激活。
1. 启动桥并加载扩展;打开一个**登录**的 facebook.com 标签页。
2. **手动发布一个项目**(短片、照片/相册、页面切换)。
3. 爬虫快照真实请求(`rupload`/照片上传 + `ComposerStoryCreateMutation` 发布)
并将它们 POST 到桥,桥将它们折叠到 `template.json` 中。
4. 从那时起,匹配的 MCP 工具会自动重放 — 新视频/照片,新令牌,相同的证明形状。
当脸书旋转其有效载荷且重放开始失败时,只需**重新捕获**
(重复步骤 2-3)。永远不需要更改代码。
## 快速入门
### 1. 安装
```sh
git clone https://github.com/crisng95/fbem.git FBEM && cd FBEM
python3.11 -m venv .venv
.venv/bin/pip install -e .
```
### 2. 运行桥(保持运行)
```sh
.venv/bin/fbem-bridge # HTTP :47102 + WS :9224, 仅限环回
```
### 3. 加载 Chrome 扩展
`chrome://extensions` → 启用 **开发者模式** → **加载解包** → 选择
`extension/` 目录。参见 [extension/README.md](extension/README.md)。保持一个
登录的 `www.facebook.com` 标签页打开 — 重放在其中运行。
### 4. 种子一个模板(一个手动帖子)
在所有运行的情况下,手动发布**一个** 短片(和一个照片/相册)。确认:
```sh
curl -s http://127.0.0.1:47102/api/health | python3 -m json.tool
# 查找:extension_connected: true, has_template: true, has_photo_template: true
```
> **已经在其他地方捕获了模板?** 将桥指向该捕获
> 目录并完全跳过重新快照:
> ```sh
> FBEM_CAPTURES_DIR=/path/to/existing/captures fbem-bridge
> ```
### 5. 将 MCP 连接到您的代理
**Claude Code:**
```sh
claude mcp add fbem -- /abs/path/to/FBEM/.venv/bin/fbem-mcp
```
**Claude Desktop** (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"fbem": { "command": "/abs/path/to/FBEM/.venv/bin/fbem-mcp" }
}
}
```
**任意 MCP 客户端:** 运行命令 `fbem-mcp`,传输 **stdio**。
代理现在可以调用以下工具。(步骤 2 中的桥必须保持运行。)
## MCP 工具
| 工具 | 功能 |
|------|--------------|
| `post_reel` | 从本地 `.mp4` 发布短片。参数:`video_path`、`caption`、`page_id?`、`scheduled_publish_time?`。 |
| `post_photos` | 从本地 `.jpg/.png` 发布照片(1 个文件)或相册(N 个文件)。参数:`image_paths[]`、`caption`、`page_id?`、`scheduled_publish_time?`。 |
| `switch_profile` | 切换执行页面/个人资料,以便后续帖子以该页面发布。参数:`target_id`。 |
| `get_identity` | 读取当前页面/个人资料的标签页发布为(只读)。 |
| `health` | 桥 + 扩展健康(连接、模板、标签页 TTL)。 |
| `capture_status` | 爬虫/快照状态:捕获了什么,准备好发布什么,以及如果没有准备好应该(重新)捕获什么。 |
## 何时需要(重新)捕获?
- **首次运行 / 从未捕获的种类** → 是:手动发布该种类一次。
- **短片 vs 照片 vs 切换是独立模板** → 捕获短片不允许发布照片;每次捕获一个种类。
- **已经捕获并正常工作** → 否。模板保存在 `FBEM_CAPTURES_DIR` 中。
- **脸书旋转了其有效载荷(重放错误,如 `story_create=null`、`no_template_captured`、重复 502)** → 重新捕获该种类一次。
随时要求代理调用 `capture_status` — 它报告准确的准备情况和应该做的事情。
## 配置
所有配置项均为可选;合理的环回默认值。参见 [.env.example](.env.example)。
| 环境变量 | 默认值 | 用途 |
|-----|---------|---------|
| `FBEM_HTTP_PORT` | `47102` | 桥 HTTP API 端口 |
| `FBEM_WS_PORT` | `9224` | 扩展 WebSocket 端口 |
| `FBEM_WS_HOST` | `127.0.0.1` | WebSocket 绑定主机(必须是环回) |
| `FBEM_HOME` | `~/.fbem` | 状态基准目录 |
| `FBEM_CAPTURES_DIR` | `$FBEM_HOME/captures` | 捕获的模板(包含实时令牌) |
| `FBEM_MEDIA_DIR` | `$FBEM_HOME/media` | 向扩展提供的媒体 |
| `FBEM_BRIDGE_URL` | `http://127.0.0.1:47102` | MCP 访问桥的位置 |
| `FBEM_TAB_TTL_S` | `7200` | *高级:* 标签页自动重新加载窗口(秒)以保持令牌新鲜 |
| `FBEM_TAB_ACTIVE_WINDOW_S` | `90` | *高级:* 最近捕获必须到达的时间,以将标签页视为“活动”(秒) |
| `FBEM_PUBLISH_OP_RE` | *(内置正则表达式)* | *高级:* 重写匹配短片发布变异的正则表达式(仅在脸书更改操作名称时设置) |
## 安全
- **仅限环回且按设计未经身份验证。** 启动拒绝非环回
`FBEM_WS_HOST`。永远不要端口转发或代理它。
- **捕获包含实时 FB 令牌** (`fb_dtsg`、`lsd`、`cookies`)。`captures/` 被
git 忽略 — 永远不要提交它。将 `FBEM_CAPTURES_DIR` 视为秘密。
- **代码中没有令牌。** 回调秘密每个进程都是随机的;临时 FB
令牌在重放时实时从页面抓取。
## 项目布局
```
fbem/
bridge/ 持久后端(FastAPI :47102 + 扩展 WebSocket :9224)
server.py · ws_server.py · bridge_client.py · capture_store.py · config.py · run.py
mcp/ MCP 服务器(任意代理可插拔)
server.py · registry.py · bridge_api.py
tools/ 一个文件一个工具 ← 贡献界面
_template.py · post_reel.py · post_photos.py · switch_profile.py
get_identity.py · health.py · capture_status.py
extension/ Chrome MV3(爬虫快照 + 重放)
docs/PROTOCOL.md 解码的原生上传协议
```
## 贡献
添加工具只需要一个文件。复制 `fbem/mcp/tools/_template.py`,编写一个类型化异步
函数,完成 — 它会自动被发现。参见 **[CONTRIBUTING.md](CONTRIBUTING.md)**。
## 许可证
[MIT](LICENSE)。