Skip to main content
Glama
print-yuhuan

qq-mcp-server

by print-yuhuan
README.md
<div align="center">

# QQ-MCP-Server

**把已登录的 NapCatQQ 机器人封装成 MCP Streamable HTTP 服务,让 Codex、Claude Desktop、Cursor、Cline、Cherry Studio 等客户端安全调用 QQ 能力。**

[![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/)
[![MCP](https://img.shields.io/badge/MCP-Streamable_HTTP-111827?style=for-the-badge)](https://modelcontextprotocol.io/)
[![NapCatQQ](https://img.shields.io/badge/NapCatQQ-OneBot_HTTP-12B886?style=for-the-badge)](https://github.com/NapNeko/NapCatQQ)
[![Tools](https://img.shields.io/badge/Tools-12-blue?style=for-the-badge)](#工具一览)
[![Deploy](https://img.shields.io/badge/Deploy-systemd-F59F00?style=for-the-badge&logo=linux&logoColor=white)](#linux-一键部署)
[![License](https://img.shields.io/badge/License-MIT-2F9E44?style=for-the-badge)](LICENSE)

</div>

---

## 项目简介

`QQ-MCP-Server` 是一个轻量级 Python MCP 后端。它对外提供 **MCP Streamable HTTP** 接口,对内调用你已经部署好的 **NapCatQQ OneBot HTTP API**,从而让 MCP 客户端读取 QQ 机器人状态、群/好友列表、聊天记录,并执行发送消息、群禁言等操作。

本项目只负责 MCP 服务本身,不负责安装、登录、维护 NapCatQQ。

```text
MCP Client
  └─ Streamable HTTP / JSON-RPC
     └─ QQ-MCP-Server
        └─ OneBot HTTP
           └─ NapCatQQ
              └─ QQ
```

## 目录

- [功能亮点](#功能亮点)
- [环境要求](#环境要求)
- [Linux 一键部署](#linux-一键部署)
- [快速开始](#快速开始)
- [MCP 客户端配置](#mcp-客户端配置)
- [配置项](#配置项)
- [工具一览](#工具一览)
- [返回结构](#返回结构)
- [验证服务](#验证服务)
- [systemd 手动部署](#systemd-手动部署)
- [安全建议](#安全建议)
- [开发与测试](#开发与测试)
- [常见问题](#常见问题)

## 功能亮点

- **标准 MCP Streamable HTTP**:基于 JSON-RPC over HTTP,适配常见 MCP 客户端。
- **三种客户端鉴权方式**:`Authorization: Bearer`、`X-API-Key`、`?token=`。
- **NapCatQQ OneBot HTTP 封装**:统一处理 token、超时、HTTP 错误和 OneBot API 错误。
- **12 个 MCP 工具**:覆盖机器人状态、群/好友列表、群成员、禁言列表、群/私聊历史、发送/撤回消息、群管理。
- **统一响应结构**:所有工具返回 `{ "ok": true, "data": ... }` 或 `{ "ok": false, "error": ... }`。
- **部署友好**:支持 `.env` 本地运行,也提供 `deploy.sh` 和 systemd 部署方案。

## 环境要求

| 组件 | 要求 |
| --- | --- |
| Python | `3.10+` |
| MCP 传输 | Streamable HTTP |
| NapCatQQ | 已登录 QQ,并启用 OneBot HTTP Server |
| NapCat 消息格式 | 建议使用 `Array` |
| 系统部署 | Linux + systemd 推荐 |

NapCatQQ 资料:

- 官方仓库:<https://github.com/NapNeko/NapCatQQ>
- 官方文档:<https://napneko.github.io/>

## Linux 一键部署

推荐在 Linux 云服务器上使用 `deploy.sh`。脚本会检查 Python、创建虚拟环境、安装依赖、生成配置、安装 systemd 服务并启动健康检查。

```bash
curl -O https://raw.githubusercontent.com/print-yuhuan/QQ-MCP-Server/refs/heads/main/deploy.sh
bash deploy.sh
```

已经克隆仓库时:

```bash
cd QQ-MCP-Server
bash deploy.sh
```

常用参数:

```bash
bash deploy.sh --no-start
bash deploy.sh --user appuser
bash deploy.sh --dir /opt/QQ-MCP-Server
```

脚本重复运行是安全的:已有配置不会被覆盖,源码和依赖会更新,服务会重启加载新代码。

## 快速开始

### 1. 安装

```bash
git clone <YOUR_REPO_URL> QQ-MCP-Server
cd QQ-MCP-Server

python3 -m venv .venv
source .venv/bin/activate

pip install -e .
```

Windows PowerShell:

```powershell
git clone <YOUR_REPO_URL> QQ-MCP-Server
cd QQ-MCP-Server

python -m venv .venv
.\.venv\Scripts\Activate.ps1

pip install -e .
```

### 2. 配置

```bash
cp .env.example .env
```

编辑 `.env`,至少填入:

```dotenv
QQ_MCP_ACCESS_TOKEN=<MCP_ACCESS_TOKEN>
NAPCAT_BASE_URL=http://<NAPCAT_HOST>:<NAPCAT_PORT>
NAPCAT_ACCESS_TOKEN=<NAPCAT_ACCESS_TOKEN>
```

如果 `QQ-MCP-Server` 和 NapCat 在同一台宿主机,并且 NapCat HTTP 端口已经暴露到宿主机,可使用:

```dotenv
NAPCAT_BASE_URL=http://127.0.0.1:<NAPCAT_PORT>
```

如果 NapCat 在 Docker 容器内但没有把 HTTP 端口映射到宿主机,`127.0.0.1` 指向的是宿主机本身,不是容器内部。此时请使用已映射的宿主机端口、容器网络地址,或把 MCP 服务部署到同一 Docker 网络中。

### 3. 启动

```bash
python -m qq_mcp_server
```

或使用安装后的命令:

```bash
QQ-MCP-Server
```

默认端点:

```text
http://<HOST>:8888/mcp
```

健康检查:

```text
http://<HOST>:8888/health
```

## MCP 客户端配置

下面示例均使用占位符。请将 `<HOST>`、`<PORT>`、`<MCP_ACCESS_TOKEN>` 替换为你的真实配置。

### Authorization Bearer

```json
{
  "mcpServers": {
    "QQ-MCP-Server": {
      "type": "streamable-http",
      "url": "http://<HOST>:<PORT>/mcp",
      "headers": {
        "Authorization": "Bearer <MCP_ACCESS_TOKEN>"
      }
    }
  }
}
```

### X-API-Key

```json
{
  "mcpServers": {
    "QQ-MCP-Server": {
      "type": "streamable-http",
      "url": "http://<HOST>:<PORT>/mcp",
      "headers": {
        "X-API-Key": "<MCP_ACCESS_TOKEN>"
      }
    }
  }
}
```

### Query Token

需显式开启 `QQ_MCP_ENABLE_QUERY_TOKEN=true`(默认关闭)。注意 `?token=` 会把 token 暴露在访问日志 / 反向代理 / 浏览器历史中(CWE-598),优先使用 `Authorization: Bearer`。

```json
{
  "mcpServers": {
    "QQ-MCP-Server": {
      "type": "streamable-http",
      "url": "http://<HOST>:<PORT>/mcp?token=<MCP_ACCESS_TOKEN>"
    }
  }
}
```

## 配置项

| 变量 | 必填 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `QQ_MCP_HOST` | 否 | `0.0.0.0` | MCP 服务监听地址。`0.0.0.0` 绑定所有网卡(公网可达,请配防火墙);仅本机访问设 `127.0.0.1`。 |
| `QQ_MCP_PORT` | 否 | `8888` | MCP 服务监听端口(1–65535)。 |
| `QQ_MCP_PATH` | 否 | `/mcp` | MCP Streamable HTTP 路径。不可设为 `/health`(保留给健康检查)。 |
| `QQ_MCP_ACCESS_TOKEN` | 是 | - | MCP 客户端访问 token。不接受 `<...>` 占位符。 |
| `QQ_MCP_ENABLE_QUERY_TOKEN` | 否 | `false` | 是否允许 `?token=` 鉴权。默认关闭以防 token 进日志(CWE-598)。 |
| `NAPCAT_BASE_URL` | 是 | - | NapCat OneBot HTTP API 基础 URL。不接受 `<...>` 占位符。 |
| `NAPCAT_ACCESS_TOKEN` | 否 | 空 | NapCat OneBot HTTP token。 |
| `NAPCAT_TIMEOUT_SECONDS` | 否 | `30` | 单次 NapCat 请求超时时间(秒,支持小数,须 > 0)。 |
| `QQ_MCP_LOG_LEVEL` | 否 | `INFO` | `DEBUG`、`INFO`、`WARNING`、`ERROR`、`CRITICAL`;非法值启动即报错。 |
| `QQ_MCP_LOG_MESSAGE_CONTENT` | 否 | `false` | 是否记录发送消息正文。生产环境建议关闭。 |
| `QQ_MCP_MAX_MESSAGE_CHARS` | 否 | `5000` | 单条发送文本最大字符数。 |
| `QQ_MCP_ALLOW_RICH_MEDIA` | 否 | `false` | 是否允许 image/record/video/file 等富媒体段。默认关闭以防 SSRF / 本地文件读取。 |
| `QQ_MCP_DEFAULT_HISTORY_COUNT` | 否 | `5` | 默认历史消息条数。 |
| `QQ_MCP_MAX_HISTORY_COUNT` | 否 | `1000` | 单次允许拉取的最大历史消息条数。 |

## 工具一览

`读` 工具只查询数据;`写` 工具会真实发送消息或修改 QQ 群状态。

| 工具 | 类型 | 参数 | 用途 |
| --- | --- | --- | --- |
| `qq_get_bot_status` | 读 | 无 | 获取机器人在线状态、QQ 号、昵称。 |
| `qq_list_groups` | 读 | 无 | 列出机器人已加入的群。 |
| `qq_list_friends` | 读 | 无 | 列出机器人好友。 |
| `qq_get_group_members` | 读 | `group_id` | 获取指定群成员列表。 |
| `qq_get_group_banned_members` | 读 | `group_id` | 列出群内当前被禁言的成员及禁言到期时间。 |
| `qq_get_group_messages` | 读 | `group_id`、`count`、`start_message_seq`、`reverse_order`、`parse_forward` | 拉取指定群聊天记录。 |
| `qq_get_private_messages` | 读 | `user_id`、`count`、`start_message_seq`、`reverse_order`、`parse_forward` | 拉取指定好友私聊记录。 |
| `qq_send_group_message` | 写 | `group_id`、`message` | 向指定群发送消息,支持 @ 提醒。 |
| `qq_send_private_message` | 写 | `user_id`、`message` | 向指定好友发送私聊消息。 |
| `qq_delete_message` | 写 | `message_id` | 撤回一条消息(机器人自己发的,或作为管理员撤回他人消息)。 |
| `qq_set_group_ban` | 写 / 高风险 | `group_id`、`user_id`、`duration` | 禁言或解禁群成员,`duration=0` 表示解禁。 |
| `qq_set_group_whole_ban` | 写 / 高风险 | `group_id`、`enable` | 开启或关闭全员禁言。 |

发送消息工具说明:

- `qq_send_group_message` / `qq_send_private_message` 的 `message` 参数支持两种写法:
  - **字符串**:纯文本即可,同时支持 CQ 码。例如 `"[CQ:at,qq=123] 早点睡"` 会解析出真正的 @ 提醒。
  - **消息段数组**:OneBot 消息段列表,例如
    `[{"type": "at", "data": {"qq": "123"}}, {"type": "text", "data": {"text": " 早点睡"}}]`。
- @ 全体成员使用 `{"type": "at", "data": {"qq": "all"}}`。
- 文本部分合计长度受 `QQ_MCP_MAX_MESSAGE_CHARS`(默认 5000)限制。

撤回工具说明:

- `qq_delete_message` 的 `message_id` 可来自发送工具的返回值,或从群/私聊历史记录中获取。
- 机器人只能撤回自己发送的消息;撤回他人消息需要在目标群具备管理员权限。
- `message_id` 仅校验为整数(不同 OneBot 实现允许负数),是否存在由 NapCat 判定,失败会返回 `NAPCAT_API_ERROR`。
- 返回里的 `recalled: true` 表示 NapCat 已受理该撤回请求,并不二次校验消息是否真的从客户端消失。

历史工具说明:

- `count` 默认取 `QQ_MCP_DEFAULT_HISTORY_COUNT`。
- `count` 不能超过 `QQ_MCP_MAX_HISTORY_COUNT`。
- `start_message_seq` 会映射到 NapCat 的 `message_seq`。
- `reverse_order` 会映射到 NapCat 的 `reverseOrder`。
- `parse_forward=true` 时会尝试解析合并转发消息。

群管理工具说明:

- `qq_set_group_ban` 和 `qq_set_group_whole_ban` 会真实修改群管理状态。
- 机器人必须在目标群具备管理员权限,否则 NapCat 会返回失败。
- `qq_get_group_banned_members` 为只读工具,返回当前被禁言成员;每条记录的 `shut_up_time` 是禁言到期的 Unix 秒级时间戳。

## 返回结构

成功:

```json
{
  "ok": true,
  "data": {}
}
```

失败:

```json
{
  "ok": false,
  "error": {
    "code": "NAPCAT_REQUEST_FAILED",
    "message": "Could not reach the NapCat HTTP server",
    "detail": {}
  }
}
```

错误码:

| 错误码 | 含义 |
| --- | --- |
| `MCP_AUTH_FAILED` | MCP HTTP 鉴权失败。 |
| `INVALID_PARAMS` | 参数缺失、类型错误或格式不合法。 |
| `MESSAGE_TOO_LONG` | 发送文本超过 `QQ_MCP_MAX_MESSAGE_CHARS`。 |
| `INVALID_DURATION` | 禁言时长不是非负整数。 |
| `NAPCAT_REQUEST_FAILED` | NapCat 不可达、超时、HTTP 状态异常或响应不是 JSON。 |
| `NAPCAT_AUTH_FAILED` | NapCat 拒绝 OneBot token。 |
| `NAPCAT_API_ERROR` | NapCat API 返回失败,例如群号错误、好友不存在、机器人离线、权限不足。 |
| `INTERNAL_ERROR` | 服务端未预期异常。 |

## 验证服务

### 健康检查

```bash
curl -i "http://<HOST>:<PORT>/health"
```

预期返回:

```json
{
  "ok": true,
  "service": "QQ-MCP-Server",
  "version": "0.1.0"
}
```

### MCP initialize

调试底层 MCP Streamable HTTP 时必须带上 `Accept: application/json, text/event-stream`。

```bash
curl -i "http://<HOST>:<PORT>/mcp" \
  -H "Authorization: Bearer <MCP_ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"test"}}}'
```

预期结果:

- HTTP 状态码为 `200`。
- `Content-Type` 为 `application/json` 或 `text/event-stream`。
- 响应内容包含 JSON-RPC `initialize` 结果。

如果返回 `401`,优先检查 MCP token、请求头名称和 `QQ_MCP_ENABLE_QUERY_TOKEN`。

## systemd 手动部署

假设项目位于 `/root/QQ-MCP-Server`:

```bash
cd /root/QQ-MCP-Server
python3 -m venv .venv
.venv/bin/pip install -e .

cp deploy/QQ-MCP-Server.env.example /root/QQ-MCP-Server/QQ-MCP-Server.env
chmod 600 /root/QQ-MCP-Server/QQ-MCP-Server.env
nano /root/QQ-MCP-Server/QQ-MCP-Server.env

cp deploy/QQ-MCP-Server.service /etc/systemd/system/QQ-MCP-Server.service
systemctl daemon-reload
systemctl enable --now QQ-MCP-Server.service
```

服务管理:

```bash
systemctl status QQ-MCP-Server.service
systemctl restart QQ-MCP-Server.service
systemctl stop QQ-MCP-Server.service
systemctl start QQ-MCP-Server.service
journalctl -u QQ-MCP-Server.service -f
```

如果安装目录不是 `/root/QQ-MCP-Server`,请同步修改 unit 文件中的 `WorkingDirectory`、`EnvironmentFile` 和 `ExecStart`。

## 安全建议

- 使用足够长的随机 `QQ_MCP_ACCESS_TOKEN`。
- 不要把 `.env`、`QQ-MCP-Server.env`、真实 token、真实 QQ 号或真实群号提交到公开仓库。
- NapCat HTTP Server 应启用自身 token,本服务会通过 `Authorization: Bearer` 调用它。
- 不需要公网直连 NapCat 时,不建议开放 NapCat HTTP 端口。
- 服务默认监听 `0.0.0.0`(所有网卡,公网可达),务必配置防火墙 / 安全组;仅本机使用时设 `QQ_MCP_HOST=127.0.0.1`。
- `?token=` 方式会把 token 暴露在代理日志、浏览器历史或服务日志中(CWE-598),默认已关闭;确需开启再设 `QQ_MCP_ENABLE_QUERY_TOKEN=true`,并优先用 `Authorization: Bearer`。
- 默认不要记录消息正文;仅在可信测试环境临时启用 `QQ_MCP_LOG_MESSAGE_CONTENT=true`。
- `qq_send_*` 和 `qq_set_group_*` 是真实写操作,只应暴露给可信 MCP 客户端。
- 当消息内容可能来自不可信来源(群消息 / LLM 输出)时,保持 `QQ_MCP_ALLOW_RICH_MEDIA=false`,避免富媒体段被用于 SSRF / 本地文件读取。
- 生产环境建议通过反向代理启用 HTTPS,并使用防火墙或安全组限制访问来源。

## 开发与测试

安装开发依赖:

```bash
pip install -e ".[dev]"
```

运行测试:

```bash
pytest
```

运行本地 MCP initialize smoke:

```bash
python tests/smoke_initialize.py
```

测试使用假 token、假 URL 和 mock NapCat 响应,不需要真实 QQ、真实 NapCat 或公网网络。

## 常见问题

### NapCat 在 Docker 容器内,为什么 `127.0.0.1:<PORT>` 不通?

`127.0.0.1` 总是指当前进程所在的网络命名空间。若 `QQ-MCP-Server` 跑在宿主机,`127.0.0.1:<PORT>` 指向宿主机端口;如果 NapCat 只在容器内监听且没有端口映射,宿主机访问会失败。

可选方案:

- 给 NapCat 容器映射 HTTP 端口,例如 `-p <HOST_PORT>:<CONTAINER_PORT>`。
- 让 `QQ-MCP-Server` 和 NapCat 加入同一个 Docker 网络,并使用容器名访问。
- 在宿主机上把 `NAPCAT_BASE_URL` 配成可达的容器网络地址。

### 为什么 MCP initialize 需要 `Accept` 请求头?

Streamable HTTP 传输允许 JSON 和事件流响应。调试请求应显式带上:

```text
Accept: application/json, text/event-stream
```

缺少该请求头时,部分 MCP 实现会拒绝或无法正确协商响应格式。

### 群管理工具返回权限不足怎么办?

确认机器人在目标群内,并且具备管理员或群主权限。NapCat 会把 QQ 侧权限不足、成员不存在、群不存在等结果返回为 API 错误,本服务会统一包装为 `NAPCAT_API_ERROR`。

## 许可证

本项目基于 [MIT](LICENSE) 许可证发布。