内容
# grok-mcp
[简体中文](./README_CN.md)
`grok-mcp` 是一个仅限 HTTP 的 [模型上下文协议(MCP)](https://modelcontextprotocol.io/) 服务器,暴露由 Grok 提供的实时网络搜索、X/Twitter 搜索和模型发现,供 MCP 客户端使用。
它 **不** 直接调用官方 xAI API,而是连接到现有的 [CLIProxyAPI (CPA)](https://github.com/router-for-me/CLIProxyAPI) 部署。CPA 拥有上游 xAI 身份验证,而 `grok-mcp` 提供 MCP 传输、客户端 API 密钥、配额、使用情况跟踪和管理面板。
> [!IMPORTANT]
> 该项目仅支持 **可流式传输的 HTTP**。它不提供_stdio_传输或内置 TLS 终止。
- `grok-mcp` 必须作为独立的 HTTP 服务运行,MCP 客户端连接到 `http://<host>:<port>/mcp`。
- 它不能配置为_stdio_服务器,MCP 客户端通过标准输入和输出启动和通信。
- 服务监听纯 HTTP,不加载 HTTPS 证书或私钥或执行 TLS 握手。
- 对于面向互联网的部署,请在 `grok-mcp` 前面放置一个受信任的反向代理,如 Nginx、Caddy、Traefik、Kubernetes Ingress 或云负载均衡器。该代理应公开 HTTPS 并通过内部 HTTP 将请求转发给 `grok-mcp`。
典型的生产请求路径如下:
```text
MCP 客户端 -- HTTPS --> 反向代理 / 负载均衡器 -- HTTP --> grok-mcp /mcp
(TLS 在此处终止)
```
## 功能
- `/mcp` 处的可流式传输 HTTP MCP 端点
- 三个只读 MCP 工具:
- `grok_web_search`
- `grok_x_search`
- `grok_list_models`
- 可选的 CPA 上游协议:OpenAI 响应、OpenAI 聊天补全或Anthropic 消息
- 上游搜索轮次的 MCP 进度通知
- 按用户划分的客户端 API 密钥,启用/禁用控制
- 基于层级的 RPM 和每月成功调用配额
- 有效转发 IP 触发的 `/mcp` 和面板身份验证保护
- 用户、密钥、层级、使用情况、邀请代码和服务器设置的 SQLite 持久性
- 嵌入式管理面板,无需单独的前端构建步骤
- 上游设置、代理设置、注册模式和调试模式的运行时更新
- 带有非 root 运行时镜像的 Docker Compose 部署
## 架构
```text
可流式传输的 HTTP MCP 客户端
|
| POST /mcp
| Authorization: Bearer <MCP 客户端 API 密钥>
v
grok-mcp
| |
| +---- /panel/ 和 /panel/v1/* ---- 管理员和用户
|
+---------- SQLite -------------------- 用户、密钥、层级、使用情况、设置
|
| POST /v1/responses、/v1/chat/completions 或 /v1/messages
| GET /v1/models
| Authorization: Bearer <CPA API 密钥>
v
CLIProxyAPI
|
v
xAI / Grok
```
### 凭证不可互换
| 凭证 | 使用于 | 目的 |
|---|---|---|
| CPA API 密钥 | `grok-mcp` -> CPA | 验证选定的上游搜索端点和 `/v1/models` 请求。 |
| MCP 客户端 API 密钥 | MCP 客户端 -> `/mcp` | 在面板中创建并按需复制。数据库存储身份验证哈希和可恢复的密文加密,加密密钥源自 `GROK_JWT_SECRET`。 |
| 面板 JWT | 浏览器/API 客户端 -> `/panel/v1` | 由面板登录返回。不能验证 `/mcp`。 |
## 要求
- Linux 是目前记录的本地运行时目标
- Go 1.25.0 或更高版本用于本地构建
- 可访问的 CPA 部署,带有 `/v1/models` 和至少一个兼容的搜索端点:`/v1/responses`、`/v1/chat/completions` 或 `/v1/messages`
- Docker 和 Docker Compose,用于容器工作流(如果需要)
- 支持可流式传输的 HTTP 和自定义 Bearer 头的 MCP 客户端
应用程序使用纯 Go SQLite (`modernc.org/sqlite`),不需要 CGO。
## 快速入门
### 1. 构建
```bash
go build -o grok-mcp ./cmd/grok-mcp
```
可选地在构建时注入版本号:
```bash
go build \
-ldflags "-X github.com/grok-mcp/internal/version.Version=1.2.3" \
-o grok-mcp ./cmd/grok-mcp
./grok-mcp -version
```
### 2. 配置
```bash
cp .env.example .env
mkdir -p data
${EDITOR:-vi} .env
```
至少需要一个新的安装:
```dotenv
CPA_API_KEY=替换为您的cpa-api-key
GROK_JWT_SECRET=替换为一个强壮的随机密钥,至少32字节
```
加载环境并启动服务器:
```bash
set -a
source .env
set +a
./grok-mcp
```
默认端点:
| 服务 | URL |
|---|---|
| MCP | `http://127.0.0.1:8080/mcp` |
| 管理面板 | `http://127.0.0.1:8080/panel/` |
| 面板 REST API | `http://127.0.0.1:8080/panel/v1/` |
### 3. 登录并创建 MCP 客户端密钥
当没有启用的管理员存在时,服务器会引导一个 `admin` 账户,并将其一次性随机密码写入启动日志。登录后,尽快轮换凭证并创建 MCP 客户端 API 密钥。
```bash
login_token="$(curl -sS -X POST "http://127.0.0.1:8080/panel/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"password-from-startup-log"}' | jq -r '.token')"
curl -sS -X POST "http://127.0.0.1:8080/panel/v1/keys" \
-H "Authorization: Bearer ${login_token}" \
-H "Content-Type: application/json" \
-d '{"name":"local-client"}'
```
响应中的 `api_key` 仅返回一次。请安全存储。
### 4. 连接 Claude Code
Claude Code 是带有记录示例设置的客户端:
```bash
export GROK_MCP_API_KEY="grok_xxx"
claude mcp add --transport http grok-mcp http://127.0.0.1:8080/mcp \
--header "Authorization: Bearer ${GROK_MCP_API_KEY}"
```
项目级 `.mcp.json` 可以使用环境扩展:
```json
{
"mcpServers": {
"grok-mcp": {
"type": "http",
"url": "http://127.0.0.1:8080/mcp",
"headers": {
"Authorization": "Bearer ${GROK_MCP_API_KEY}"
}
}
}
}
```
不要提交实际的 API 密钥。其他 MCP 客户端可以连接,如果它们支持带有自定义 `Authorization: Bearer ...` 头的可流式传输 HTTP,但此处未记录的客户端特定配置应被视为未经验证。
## MCP 工具
所有工具都是只读的。搜索失败作为 MCP 工具结果返回,带有 `isError=true`,因此正常的工具错误不会终止 MCP 会话。
### `grok_web_search`
通过 Grok 执行实时公共网络搜索。
| 参数 | 类型 | 必需 | 描述 |
|---|---|:---:|---|
| `query` | 字符串 | 是 | 非空搜索请求。 |
| `model` | 字符串 | 否 | 覆盖配置的默认值;值必须包含 `grok`。 |
| `allowed_domains` | 字符串[] | 否 | 仅搜索这些域名;最多 5 个。 |
| `excluded_domains` | 字符串[] | 否 | 排除这些域名;最多 5 个。 |
| `enable_image_understanding` | 布尔值 | 否 | 为网络搜索启用图像理解。 |
| `enable_image_search` | 布尔值 | 否 | 启用图像搜索结果。 |
`allowed_domains` 和 `excluded_domains` 互相排斥。条目必须是纯域名,而不是 URL。通配符、IP地址、端口、路径、`localhost` 和 `.local` 域名被拒绝。
### `grok_x_search`
通过 Grok 搜索 X/Twitter 上的实时帖子。
| 参数 | 类型 | 必需 | 描述 |
|---|---|:---:|---|
| `query` | 字符串 | 是 | 非空搜索请求。 |
| `model` | 字符串 | 否 | 覆盖配置的默认值;值必须包含 `grok`。 |
域名过滤器和图像相关参数仅适用于 `grok_web_search`。
线路映射取决于所选的上游协议。响应使用 CPA 的原生 `x_search` 工具,聊天补全使用 `x` 搜索源,Anthropic 消息使用支持的服务器端网络搜索工具,限制在 `x.com` 内。Anthropic 映射是必要的,因为自定义 Anthropic `x_search` 声明被 CPA 视为客户端执行的工具调用,它本身不会产生最终的搜索答案。
### `grok_list_models`
不接受参数。它读取 CPA 的 `GET /v1/models`,修剪并去重 ID,保留包含 `grok` 的 ID,并排除包含 `imagine` 或 `video` 的 ID。
### 搜索结果形状
```json
{
"answer": "Grok 合成的答案",
"citations": [
"https://example.com/source"
],
"sources": [
{
"url": "https://example.com/source",
"title": "示例源"
}
],
"usage": {
"input_tokens": 120,
"output_tokens": 340,
"total_tokens": 460,
"reasoning_tokens": 0
}
}
```
当上游响应不提供时,`citations`、`sources` 和 `usage` 可能会被省略。故意拒绝 JSON-RPC 批量请求,因为批量请求可能会绕过每次调用的配额预留和使用情况核算。
## 配置
### 启动环境
| 变量 | 默认值 | 描述 |
|---|---|---|
| `GROK_JWT_SECRET` | 无 | 必需的 HS256 面板签名密钥;必须至少 32 字节。始终通过环境提供。 |
| `CPA_API_KEY` | 无 | 新数据库需要此密钥。后续启动时,持久化的服务器设置可能会提供它。 |
| `CPA_BASE_URL` | `http://127.0.0.1:8317` | CPA 根 URL。 |
| `GROK_UPSTREAM_PROTOCOL` | `responses` | 搜索协议:`responses`、`chat_completions` 或 `anthropic_messages`。 |
| `GROK_MODEL` | `grok-4.3` | 默认 Grok 模型。 |
| `GROK_HTTP_TIMEOUT` | `120` | 上游超时(秒)。 |
| `GROK_HTTP_ADDR` | `:8080` | HTTP 监听地址。需要重启才能更改。 |
| `GROK_DB_PATH` | `./grok-mcp.db` | SQLite 数据库路径。需要重启才能更改。 |
| `GROK_MCP_IP_RPM` | `300` | 在 MCP API 密钥身份验证之前应用的源 IP RPM,仅当 `X-Real-IP` 或 `X-Forwarded-For` 包含有效 IP 时。 |
| `GROK_MCP_DEBUG` | `false` | 接受 `1`、`true` 或 `yes`。可能会在用法记录中捕获调试请求/响应上下文。 |
| `GROK_PROXY_URL` | 空 | 显式上游 HTTP(S) 代理 URL。 |
| `GROK_PROXY_ENABLED` | 推断 | 显式代理开关。当未设置时,非空的 `GROK_PROXY_URL` 启用它。 |
| `HTTP_PROXY`、`HTTPS_PROXY`、`NO_PROXY` | Go 默认值 | 标准传输在未启用显式代理时使用。 |
### 转发客户端 IP 保护
仅当可以从 `X-Real-IP` 或 `X-Forwarded-For` 解析出有效 IP 时,应用程序级 IP 保护才启用。相同的策略用于:
- `/mcp` 令牌桶,在 API 密钥身份验证之前运行;
- 面板登录和注册端点令牌桶;
- 面板用户名/IP 登录失败锁定。
请求行为:
| 请求状态 | IP 保护行为 |
|---|---|
| 两个头部都不存在、为空或不包含有效 IP | 应用程序级 IP 速率限制和登录 IP 锁定被跳过。MCP 身份验证后仍应用用户分层 RPM 和配额检查。 |
| 存在有效的转发客户端 IP 头 | 使用有效的 `X-Real-IP`;否则,使用 `X-Forwarded-For` 中的第一个有效 IP。不需要受信任的代理白名单。 |
> [!IMPORTANT]
> 应用程序直接信任 `X-Real-IP` 和 `X-Forwarded-For`。它必须只能通过受信任的反向代理访问,该代理始终注入有效的客户端 IP 并首先删除客户端提供的数值。如果客户端可以直接访问 `grok-mcp`,它们可以省略或无效这些头部以跳过 IP 保护,或者伪造它们以选择任意速率限制桶。保持 `/mcp`、`/panel/v1/auth/login` 和 `/panel/v1/auth/register` 的代理层速率限制启用。
代理必须覆盖 `X-Real-IP` 并从连接源重建 `X-Forwarded-For`。因为应用程序选择 `X-Forwarded-For` 的第一个有效条目,因此不要保留不受信任的客户端提供的链。
Nginx 转发配置示例:
```nginx
location / {
proxy_pass http://grok-mcp:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
```
### 持久性和实时更新
启动时,首先加载环境变量。如果 SQLite 已经包含服务器设置,则持久化的上游设置将优先。管理员可以从 **服务器设置** 更新以下值而无需重启:
- CPA 基本 URL 和 API 密钥
- 上游搜索协议
- 默认模型和超时
- 显式代理 URL 和启用状态
- 注册模式
- 调试模式
监听地址、数据库路径、JWT 密钥和源 IP RPM 仍然是仅限启动的设置。
> [!WARNING]
> CPA API 密钥在 SQLite 中持久化。保护并备份数据库作为敏感数据。面板仅返回此密钥的掩码预览。
### 上游协议映射
| 设置 | 端点 | 搜索映射 |
|---|---|---|
| `responses` | `POST /v1/responses` | CPA 响应内置(`web_search` / `x_search`);这保持向后兼容,提供搜索轮次进度事件。 |
| `chat_completions` | `POST /v1/chat/completions` | xAI 兼容的 `search_parameters`,带有 `web` 或 `x` 源以及流式聊天补全块。仅当 CPA 包括兼容的非标准搜索事件时,才会继续发送简短的状态响应,例如“searching...”。 |
| `anthropic_messages` | `POST /v1/messages` | Anthropic 服务器端 `web_search_20250305` 带有消息 SSE 事件。网络搜索保留配置的域名过滤器;X 搜索使用相同的服务器工具,限制在 `x.com` 并添加指令以返回直接 X 帖子 URL。 |
协议支持最终取决于所选的 CPA 版本、提供商和模型能力。响应是最安全的兼容性选择,用于现有的 Grok/CPA 部署。图像搜索选项是响应特有的;其他协议忽略它们,因为没有等效的线路选项存在。
协议可以暴露不同的元数据,即使它们的答案文本是等效的:
- 响应通常提供最丰富的搜索轮次进度和结构化引用数据。
- 聊天补全仅在 CPA 包括兼容的非标准搜索事件时发出进度。标准聊天块可能仅包含最终文本和使用情况。
- Anthropic 消息可能在答案文本中包含源 URL,而不发出结构化引用块,取决于 CPA 的提供商翻译。
- `usage` 在所有协议中标准化,当上游响应包含令牌计数时。
## 用户、注册、分层和配额
注册可以更改为运行时:
| 模式 | 行为 |
|---|---|
| `free` | 允许公共自注册。 |
| `invite` | 需要有效的启用的非耗尽邀请代码。 |
| `disabled` | 禁止公共注册。 |
管理员可以创建、禁用和删除邀请代码并设置他们的注册限制。
每个用户属于一个层级。用户的所有 API 密钥共享该层级的 RPM 和每月成功调用次数。只有实际的 `tools/call` 请求会被计费;初始化、ping 和工具列表请求不计入。
新数据库的默认层级:
| 层级 | RPM | 每月成功调用次数 |
|---|---:|---:|
|---|---:|---:|
| `tier0` | 10 | 800 |
| `tier1` | 20 | 4,000 |
| `tier2` | 40 | 16,000 |
| `tier3` | 60 | 40,000 |
| `tier4` | 120 | 160,000 |
| `tier5` | 300 | 800,000 |
| `tier6` | 无限制 | 无限制 |
成功的调用期间使用 UTC 日历月份。调用会预留配额,然后失败会取消预留。层级值可以在面板中自定义。
`/mcp` 中间件顺序如下所示:
```text
最大请求体 -> IP RPM -> API 密钥 -> 提取工具名称 -> 用户 RPM -> 配额 -> 用法 -> MCP 处理程序
```
## 管理 API 概述
嵌入式面板位于 `/panel/`。其 API 在 `/panel/v1` 下。
公共身份验证路由:
```text
GET /panel/v1/auth/registration-settings
POST /panel/v1/auth/register
POST /panel/v1/auth/login
```
身份验证后的用户路由涵盖个人资料信息、API 密钥管理和用法:
```text
GET /panel/v1/me
GET /panel/v1/keys
POST /panel/v1/keys
POST /panel/v1/keys/{id}/reveal
PATCH /panel/v1/keys/{id}
DELETE /panel/v1/keys/{id}
GET /panel/v1/keys/{id}/usage
GET /panel/v1/usage
```
管理员路由在 `/panel/v1/admin/` 下管理用户、分层、服务器设置、邀请代码、模型和用法。所有非公共面板请求需要:
```text
Authorization: Bearer <panel JWT>
```
## Docker Compose
```bash
cp .env.example .env
${EDITOR:-vi} .env
docker compose up -d --build
```
如果 CPA 直接在 Docker 主机上运行,请使用:
```dotenv
CPA_BASE_URL=http://host.docker.internal:8317
```
提供的容器:
- 使用 `CGO_ENABLED=0` 构建
- 作为非 root `app` 用户运行
- 监听 8080 端口
- 在 `/app/data` 中存储 SQLite 数据
- 在 Docker Compose 中使用 `grok-mcp-data` 命名卷
- 健康检查 `/panel/`
Docker Compose 文件不转发每个可选的外部代理变量。如果容器需要 `GROK_PROXY_URL`、`GROK_PROXY_ENABLED` 或标准代理变量,请扩展 `environment`。