qzone-mcp
Provides tools for interacting with QQ Qzone, including publishing, listing, deleting, liking, and commenting on posts; viewing friend feeds and recent visitors; managing post visibility; handling confession-wall submissions and review; and scheduling posts.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@qzone-mcp帮我发条说说:今天天气真好,设为公开可见"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
qzone-mcp —— QQ 工具箱(空间 + 资料),我们的一个 MCP 插件
把上游两个 AstrBot 插件的能力合成一个插件,改成我们这套栈能直接调的形式:
上游 | 贡献的能力 |
发/看/删/赞/评说说、好友动态、访客、可见范围、表白墙投稿审核、定时说说 | |
改头像 / 昵称 / 签名 / 在线状态、自定义状态、查资料、名片赞、好友分组 |
为什么不原样装:这两份都是 AstrBot 插件(star.Star + event.bot.*),AstrBot 已经不在我们的栈里了。
所以保留它们的接口语义(端点、参数、可见范围、审核流程、状态码表),换成本机现有认证与调用方式。
从零安装(换台机器 / 换个容器)
三步:放代码 → 配环境变量 → 让 DSH 拉起 MCP。
代码放到
/opt/qzone-mcp(server.py是入口,wire.py负责接线)。依赖:
pip3 install --break-system-packages aiohttp==3.14.5 edge-tts==7.3.1环境变量(compose 的
environment:或 systemdEnvironment=):
变量 | 必填 | 说明 |
| ✅ | OneBot HTTP,例如 |
| ✅ | SnowLuma 的 HTTP token(不是 WS token,两者不同) |
| 建议 | 数据目录( |
| 发图/头像需要 | core 侧看到的共用目录,例如 |
| 发图/头像需要 | 同一目录在 SnowLuma 容器里的路径,例如 |
| 可选 | 临时图 HTTP 端口,默认 |
| 可选 | 临时图对外基址,默认容器 IP |
| 可选 | 默认音色,默认 |
| 可选 | 一次合成最大字数,默认 |
config.example.json 是可选覆盖(放 QZONE_DATA 或 /opt/qzone-mcp/config.json),只管 qzone.timeout / cookieTtl / defaultUgcRight、表白墙、定时说说这些。
接线(让 DSH 把它当 MCP 服务器拉起来):
python3 /opt/qzone-mcp/wire.py # 幂等;写 $DSH_HOME/profiles/<profile>/cordis.patch.yml 的 mcp-qzone 段
docker restart core # profile 层要重启才重读wire.py 写进去的行长这样(serverName: qzone → 工具名就是 mcp__qzone__*):
- insert:
- id: mcp-qzone
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: qzone
command: /usr/bin/python3
args: ['/opt/qzone-mcp/server.py']
toolCallTimeoutMs: 120000QQ 会话里要能用,还得放行工具前缀:
qq-tool-restrict的SAFE_PREFIXES里要有'mcp__qzone__';mcp__qzone__*默认只给管理员(按 qq-bridge 的 owner 会话判定)。
自检:
printf '%s\n%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| python3 /opt/qzone-mcp/server.py | head -c 400Related MCP server: qzone-mcp
它长在哪
QQ ──► SnowLuma(注入 QQ 的 OneBot 运行时)
│ HTTP 3000
│ · 空间:get_cookies → p_skey → 自算 g_tk → 空间网页版 CGI
│ · 资料:set_qq_avatar / set_qq_profile / set_self_longnick /
│ set_online_status / set_diy_online_status / get_stranger_info …
▼
core 容器里的 qzone-mcp(stdio MCP server,DSH 拉起)
│ 工具调用
▼
DSH agent(qq-bridge 转来的 QQ 消息 → 挑工具 → 回消息)运行:
/opt/qzone-mcp/server.py,MCP over stdio(JSON-RPC 2.0,按行),只依赖标准库 + aiohttp。注册:
/opt/qzone-mcp/wire.py往/opt/dsh-home/profiles/web/cordis.patch.yml幂等追加一条mcp-qzone,entrypoint 每次启动跑一遍。文件:
server.py(协议 + 工具注册)、qzone_api.py(空间 CGI)、profile_api.py(资料动作 + 状态码表)、msg_api.py(往 QQ 发图片消息)、img_bridge.py(取图 + 临时 HTTP + core↔SnowLuma 共用目录)、bridge_admin.py(改 qq-bridge 白名单)、wall.py(表白墙 sqlite)、scheduler.py(定时说说)、wire.py(profile 接线)。
工具(38 个)
空间
工具 | 作用 |
| 发说说( |
| 看说说列表, |
| 单条说说完整内容(评论、图片、转发) |
| 删自己的说说(走 SnowLuma 原生 |
| 点赞 / 取消赞 |
| 评论 / 楼中楼回复 |
| 好友动态流 |
| 最近访客 |
| 空间名 / 简介 |
| 改已发说说的可见范围 |
| 表白墙投稿、看稿、看详情 |
| 过稿(发到空间)、拒稿、撤稿 |
| 定时说说(时间点 + ±随机浮动 + 候选文案) |
资料
工具 | 作用 |
| 机器人自己的资料:QQ、昵称、连接状态、性别、年龄、等级 |
| 改昵称(可选 |
| 改个性签名( |
| 改头像(图片 URL 或容器内文件路径) |
| 改在线状态,认官方名也认白话(见下) |
| 自定义状态( |
| 列出全部支持的官方状态名 |
| 查任意 QQ 的资料(昵称/性别/年龄/等级/签名) |
| 名片赞:谁赞过、总次数、今日新增 |
| 好友分组列表(含备注) |
状态白话归一:profile_set_status 先精确匹配 status.py 的官方名(43 个),再走别名表,
最后做包含匹配。举例:忙→忙碌、挂机→离开、睡了→睡觉中、在摸鱼→摸鱼中、
coding→搬砖中、好累→被掏空、开心→元气满满、emo→emo中、没电→我的电量。
也接受纯数字(1300 → 摸鱼中)。
没搬的东西:上游的「切换人格 / 人格列表」依赖 AstrBot 的 conversation_manager +
persona_manager,我们这套栈里没有对应概念(人设由 DSH 的 agent preset + feeling 插件承担),所以不做。
发图
工具 | 作用 |
| 把一张图当图片消息发到 QQ(私聊或群)。 |
这条是 v1.5.0 加的,解决的是「本地生成的图发不进 QQ」:收藏表情只能走 qq_send_sticker
(发的是表情包收藏里的东西),而 meme_render / 自己用 PIL 画的图原先只落在容器里、
控制台能看、QQ 发不出去。
// 发 meme_render 刚搓出来的图
{"name": "qq_send_image", "arguments": {
"image": "/opt/dsh-home/nekowhale-stickers/renders/a0985f7f45247229.gif",
"target": "123456789", "kind": "private", "text": "喏,本鱼自己搓的"
}}
// → 已把图片发到私聊 123456789:发送成功(797.1 KB image/gif,走共用目录,message_id=2105297674,前面带了文字,回读确认有图片段)实现要点:
img_bridge.load(image)统一把 URL / base64 / data URL / 容器内路径读成字节;优先
img_bridge.to_snowluma()写到 core↔SnowLuma 共用目录(宿主/opt/snowluma/data/stickers, core 里/opt/snowluma-data/stickers,snowluma 里/app/data/stickers),把 snowluma 侧路径 交给send_private_msg/send_group_msg的image段;失败退base64://;发完用
get_msg回读确认图片段真的在(返回里那句「回读确认有图片段」就是它)。v1.5.1 加的重试自愈:
call_action遇到unauthorized/retcode 1401会从/opt/snowluma-data/config/onebot_*.json重挑 HTTP token 再试一次;send_image每条通道 各试 2 次(鉴权失败换 token 后 0.6s 重试,其它失败退避 0.8s)。失败信息会把三条尝试的 原话拼出来,不再是光秃秃一句unauthorized。
入参可以是 dsh web 的路由地址:meme_render / sticker_send 返回的是
 这种 markdown(web profile 下
origin() 有值,插件就不给文件路径了)。这种地址可以原样填进 image —— img_bridge.load
会自己去 127.0.0.1:8080 把图取下来(实测 /r/ 814 KB、/s/ 1.90 MB 都能发)。
为什么不用别的路子:set_qq_avatar 对 base64:// 直接报 avatar file is empty;QQ 消息里自带的
图片地址带 rkey,SnowLuma 自己去拉会 401。发消息这条对三种写法都放行(实测 A 容器内路径、B base64://、
C file:///… 全部 retcode 0,回读都能看到 multimedia.nt.qq.com.cn 的下载地址)。
meme_render 的产物落在 /opt/dsh-home/nekowhale-stickers/renders/<hash>.gif,它返回的
Markdown 行里就带这个路径——直接把同一个路径喂给 qq_send_image 即可。
语音(让机器人开口说话,v1.6.0 起)
工具 | 作用 |
| 把文字念成 QQ 语音条发出去。 |
| 列出常用中文音色名,不发消息 |
{"name": "qq_speak", "arguments": {
"text": "我是大肥鱼,这条语音是本鱼自己说的,听到没。",
"target": "123456789", "kind": "private"
}}
// → 已经用语音说了(28.3 KB,音色 zh-CN-XiaoyiNeural,message_id=1771999584,回读确认是语音条)链路(和发图完全同构):
text → edge-tts(微软 Edge 朗读接口,免密钥,装 pip 包即可)合成 mp3
→ 写进 core↔SnowLuma 共用目录(img_bridge.to_snowluma(..., "audio/mpeg", ext=".mp3"))
→ OneBot send_private_msg / send_group_msg 的 {"type":"record","data":{"file":"/app/data/stickers/xxx.mp3"}}
→ SnowLuma 自己把 mp3 转 SILK 发出去 → get_msg 回读确认 record 段为什么不直接用 qq-bridge 的 qq_send_voice:它只认 /opt/qq-bridge/audio/ 语音库里的现成文件
(allowAbsolutePath:false),而且 cfg.socialV2.tools.sendVoice 默认 false → 工具根本不注册,
还要 core 里有 ffmpeg/ffprobe 才能过音量归一化。我们要的是「现说现发」,所以绕开它。
snowluma 容器里有 ffmpeg 5.1.9,转码那一步由它做,core 不必装 ffmpeg(省 ~250 MB)。
音色默认 zh-CN-XiaoyiNeural(少女感女声,最像「大肥鱼」);东北话 zh-CN-liaoning-XiaobeiNeural、
陕西话 zh-CN-shaanxi-XiaoniNeural、粤语 zh-HK-HiuMaanNeural、台腔 zh-TW-HsiaoChenNeural 都有。
换默认音色:compose 里给 core 加 QZONE_TTS_VOICE=zh-CN-XiaoxiaoNeural。
已知边界:edge-tts 要出网访问 speech.platform.bing.com(本机已验证通,4.8 秒 mp3 用了约 1 秒);
合成失败会明确区分「出网不通/音色名不对」;单条上限 400 字(QZONE_TTS_MAX_CHARS 可调)。
配置
/opt/qzone-data/config.json(容器内),宿主上是 /opt/core/qzone-data/config.json:
{
"onebot": { "httpUrl": "http://snowluma:3000", "accessToken": "<SnowLuma HTTP token>" },
"qzone": { "timeout": 30, "cookieTtl": 1800, "defaultUgcRight": 1 },
"wall": { "enabled": true, "name": "表白墙", "template": "【表白墙】\n{text}",
"anonymousSuffix": "\n—— 匿名", "maxLength": 800 },
"schedule": { "enabled": false, "times": ["09:30","14:30","21:00"],
"offsetMinutes": 20, "ugcRight": 1, "texts": [] }
}QZONE_ONEBOT_URL/QZONE_ONEBOT_TOKEN覆盖onebot段。不要用ONEBOT_TOKEN(那是 qqd 的 WS token)。SnowLuma 的 HTTP(3000) 与 WS(3001) token 是两个不同的值,这里要 HTTP 那个。
表白墙数据:
/opt/qzone-data/wall.db(sqlite)。定时说说只在 MCP 进程活着时生效;
qzone_schedule_set会即时重载,不用重启。
运维
# 改了插件代码必须重建(COPY 进镜像的,compose up -d 不重建)
cd /opt/core && docker compose up -d --build
# 看 MCP 是否被 DSH 拉起来
docker logs core 2>&1 | grep -i qzone
# 协议自检
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| docker exec -i core python3 /opt/qzone-mcp/server.py
# 直接调一个工具
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"profile_get","arguments":{}}}' \
| docker exec -i core python3 /opt/qzone-mcp/server.py已知边界
get_status只回{online, good, time},拿不到当前status/ext_status,所以profile_get只报「在线/离线」。 改完状态要确认效果,看 QQ 客户端上的机器人名片。get_unidirectional_friend_list在 SnowLuma 这版会抛protobuf invalid wire type 6(SnowLuma 侧 bug), 所以这个工具没做,别去加。profile_likes里 QQ 常常不下发昵称(nick为空),只能显示 QQ 号与次数。profile_set_avatar已实现但未实机验证(换头像对用户可见,等你自己点头再动)。点赞接口上游注明「接口显示成功但实测点赞无效」;这里沿用同一端点(
http://的 unikey 才记赞)。图片上传(
up.qzone.qq.com)上游标为「较脆弱」,失败会报错而不是静默。访客接口只返回
items,字段随 QQ 版本变化时可能少名字;动态流深翻页需时间游标,这里只取首页。
白名单管理(admin_* 五个工具,v1.2.0 起)
为什么要这组工具:qq-bridge 的白名单闸门在「消息进 agent 之前」,所以以前想加人只能登录服务器改
config.json 再重启容器 —— 你在 QQ 里跟机器人说「把谁加白」是没人接的。
现在怎么做:直接说人话,agent 会调工具。
你说 | agent 调 | 效果 |
「看下白名单」 |
| 列出模式 / 管理员 / 私聊白名单 / 群白名单 |
「把 123456 加进白名单」 |
| 那个人能私聊机器人 |
「把群 987654321 加白」 |
| 那个群能 @ 机器人 |
「把 123456 删掉」 |
| 立即不再响应 |
「白名单只留 123456789」 |
| 整份替换 |
「管理员改成 xxx」 |
| 换 ownerQQ |
实现路径(为什么不用重启):
qzone-mcp (MCP server)
└─ POST http://127.0.0.1:3100/api/whitelist ← qq-bridge 自带控制台 API
header: x-console-token: $(cat /opt/qq-bridge/state/console-token)
└─ qq-bridge: atomicWriteJson(config.json) + cfg.allow 热更新 + reconcileSessionPolicies()踩过的坑(重要):/opt/qq-bridge/config.json 是单文件 bind mount(./qbridge/config.json:/opt/qq-bridge/config.json),
容器内对它 rename(2) 恒返回 EBUSY → qq-bridge 的 atomicWriteJson 抛错 → 控制台所有「改配置」接口 500,
工具报 OSError(16, 'Device or resource busy')。修法:构建期用 core/patch-qqbridge.js 给 renameWithRetry
打一条退化路径(rename 反复 EBUSY 就原地覆盖写),core/Dockerfile 里 RUN node /tmp/patch-qqbridge.js。
本插件自己的 patch_file() 也有同样的 EBUSY 退化,作为控制台彻底不通时的保底(那种情况要
docker restart core 才生效,返回值里会写明)。
验证过:加群 → config.json 里 groups: [987654321] 落盘 → qq-bridge 日志
控制台:白名单已更新(群: 987654321,私聊: 123456789,管理员: 123456789) → 移除后回到 groups: [],全程没重启容器。
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for QPost — lets AI agents publish video and image posts to YouTube, TikTok, Instagram.
Agent-first social media publishing, scheduling, analytics, and unified inbox API + 16-tool MCP server across 18 social platforms (X, LinkedIn, Instagram, YouTube, TikTok, Threads, Bluesky, Reddit, Telegram, Discord, Mastodon, Facebook, Pinterest).
All 20 YourAITools agent APIs in one MCP server. Most tools free; pay with credits or x402.
A registry of AI agent tools — MCP servers, APIs, CLIs, SDKs — kept current by automated ingestion.
Related MCP Servers
- AlicenseAqualityCmaintenanceAn MCP server that enables AI clients to send and receive QQ messages through NapCatQQ (OneBot v11) for both private and group chats. It supports message context management, real-time WebSocket listening, and human-like typing simulation.726MIT
- AlicenseBqualityCmaintenanceMCP server for QZone (QQ space) integration, enabling publishing, listing, liking, and commenting on posts through the QzoneUltra daemon and NapCat.121MIT
- AlicenseAqualityCmaintenanceExposes NapCat/OneBot v11 QQ-bot APIs as MCP tools for any MCP client, and includes an LLM-driven QQ bot with memory, knowledge base, tool calling, web search, multimodal recognition, and permission controls.45MIT
- AlicenseBqualityAmaintenanceEnables AI agents to control and monitor QQ accounts through the NapCatQQ OneBot 11 protocol, covering private/group messaging, forwards, group administration, file transfer, OCR, friend and profile management, and real-time message listening. It also handles QR-code login, hot-switching between multiple QQ accounts, and one-click self-check, deployment, and updates.743MIT