内容
# RappiMCP
<!-- mcp-name: io.github.bgirardi99/rappi-mcp -->
```
程序 : RappiMCP
作者 : Bastian Girardi
日期 : 2026年4月2日
目的 : 用于在Rappi Chile上自动进行购物和配送购买,通过Claude — 消除了手动浏览应用程序的需要,
当您只需要告诉AI您需要什么并让它下订单时。
```
---
一个MCP(模型上下文协议)服务器,给予Claude对Rappi Chile的完全控制权 — 浏览商店、搜索产品、管理购物车、打开结账页面 — 所有操作都通过自然语言进行。
## 工作原理
该服务器使用[FastMCP](https://github.com/jlowin/fastmcp)构建,并暴露出Claude可以调用的工具。它通过Rappi的内部网络API(`services.rappi.cl`)与Rappi进行通信,使用通过Chrome DevTools协议(CDP)直接从Chrome浏览器会话中提取的令牌进行身份验证。
```
Claude ──tools──▶ rappi_mcp.py ──HTTP──▶ services.rappi.cl
│
└──CDP──▶ Chrome (身份验证 + 结账导航)
```
## 设置
### 1. 安装依赖项
```bash
pip install -r requirements.txt
```
### 2. 配置Claude代码
将以下内容添加到您的Claude代码MCP设置中(`claude_desktop_config.json`或等效文件):
```json
{
"mcpServers": {
"RappiMCP": {
"command": "python",
"args": ["C:/path/to/RappiMCP/rappi_mcp.py"]
}
}
}
```
### 3. 身份验证
在第一次使用时,调用`rappi_auth`以从Chrome中提取您的Rappi凭证并保存到`.env`中。不需要手动复制令牌。
## 身份验证
Rappi使用存储为浏览器cookie的旋转JWT令牌。身份验证流程:
1. **`rappi_auth(use_existing_chrome=False)`** — 启动一个独立的Chrome窗口,访问rappi.cl(在CDP端口`9223`上),并等待最多2分钟,让您登录
2. 登录后,它通过CDP读取三个cookie:
- `rappi.data`(Base64 JSON)→ `user_id`
- `deviceid` → `device_id`
- `rappi_refresh_token` → `refresh_token`
3. 通过`POST /api/rocket/refresh-token`将刷新令牌交换为新的`access_token`
4. 将所有四个值写入`.env`
**`rappi_auth(use_existing_chrome=True)`** (默认)— 附加到一个已经运行的Chrome浏览器,并使用`--remote-debugging-port=9223`立即读取cookie(您必须已经登录)。
### 令牌刷新
`RappiClient`自动处理令牌轮换。如果任何API响应包含头部`x-refresh-token: true`,它将透明地刷新两个令牌并重试请求。
### 环境变量
```
RAPPI_ACCESS_TOKEN — API调用的Bearer令牌
RAPPI_REFRESH_TOKEN — 用于获取新的访问令牌
RAPPI_DEVICE_ID — 标识浏览器会话的UUID
RAPPI_USER_ID — 数字Rappi用户ID
```
参见`.env.example`了解预期的格式。
## 工具参考
### `rappi_auth(use_existing_chrome=True)`
从Chrome中提取凭证并写入`.env`。设置`use_existing_chrome=False`以启动一个全新的Chrome窗口并等待登录。
---
### `rappi_reload()`
从`.env`重新加载令牌并重启API客户端 — 无需重启MCP服务器。在手动编辑`.env`后非常有用。
---
### `rappi_list_stores(category, query, limit)`
列出在您保存的地址附近可配送的商店。
| 参数 | 默认值 | 描述 |
|-------|---------|-------------|
| `category` | `"market"` | 商店类别:`"market"`、`"restaurant"`、`"farma"`、`"licores"`、`"express-big"`或任何Rappi store_type slug |
| `query` | `""` | 可选的名称筛选器 |
| `limit` | `50` | 最大结果数 |
返回:`store_id`、`name`、`store_type`、`lat`、`lng`、`eta`、`shipping_cost`。
> `store_id`由`rappi_search_products`使用。`store_type`由所有购物车工具使用。
---
### `rappi_get_store(store_id)`
通过其数字ID查找特定商店的元数据。返回`store_type`(购物车操作所需的slug)。
---
### `rappi_search_products(store_id, query, size, offset)`
在商店内搜索产品。西班牙语查询效果最佳。
| 参数 | 默认值 | 描述 |
|-------|---------|-------------|
| `store_id` | 必需 | 来自`rappi_list_stores` |
| `query` | 必需 | 搜索词(例如 `"cerveza"`、`"preservativos"`) |
| `size` | `40` | 每页结果数(最大40) |
| `offset` | `0` | 分页偏移量 |
返回:`composite_id`、`name`、`trademark`、`price`、`real_price`、`discount`、`quantity`、`unit_type`、`sale_type`、`in_stock`。
> 在添加到购物车时使用`composite_id`和`sale_type`。
---
### `rappi_get_cart(store_type)`
读取给定商店的当前购物车内容。返回一个包含`composite_id`、`units`、`sale_type`、`name`、`price`的商品列表。
---
### `rappi_add_to_cart(store_id, store_type, composite_id, units, sale_type)`
将产品添加到购物车(或如果已存在则增加其数量)。内部:读取当前购物车 → 追加/增加 → 通过完整的PUT写入。
---
### `rappi_remove_from_cart(store_id, store_type, composite_id)`
通过其`composite_id`从购物车中删除特定产品。
---
### `rappi_clear_cart(store_id, store_type)`
清空商店的整个购物车。
---
### `rappi_checkout(store_type)`
导航到商店的结账页面。每个商店都有自己的结账URL:
```
https://www.rappi.cl/checkout/{store_type}
```
示例:
- `turbo_rappidrinks_nc` → `rappi.cl/checkout/turbo_rappidrinks_nc`
- `cruz_verde_rappido_nc` → `rappi.cl/checkout/cruz_verde_rappido_nc`
- `lider` → `rappi.cl/checkout/lider`
> Rappi不支持合并的多店结账。每个商店的购物车必须单独结账。
## 典型流程
```
1. rappi_auth() # 身份验证一次
2. rappi_list_stores(category="farma") # 查找药店
3. rappi_search_products(store_id, "preservativos") # 搜索
4. rappi_add_to_cart(store_id, store_type, composite_id, 1, "U")
5. rappi_checkout(store_type) # 在Chrome中打开结账页面
```
## 文件结构
```
RappiMCP/
├── rappi_mcp.py # MCP服务器 — 工具定义、身份验证逻辑、CDP帮助程序
├── rappi_client.py # Rappi API客户端,自动刷新令牌
├── requirements.txt # Python依赖项
├── .env # 凭证(git-ignored)
└── .env.example # 凭证模板
```
## 备注
- 服务器使用CDP端口`9223`(而非默认的`9222`)以避免与其他工具冲突
- 结账导航也使用CDP — 与身份验证相同的Chrome实例
- `store_type` slugs来自`rappi_list_stores`,必须按返回的原样传递(例如 `"turbo_rappidrinks_nc"`,而非 `"turbo"`)