skynet-mcp
by losophy
README.md
# skynet-mcp
把 skynet 游戏服务器框架的 [DebugConsole](https://github.com/cloudwu/skynet/wiki/DebugConsole)
调试命令封装成 MCP(Model Context Protocol)工具,让 coding agent(如 opencode)
通过**自然语言**直接驱动 skynet 调试控制台——不用再记 `list` / `mem` / `call` / `inject` 这些命令。
```
用户: "看看现在 skynet 里跑了哪些服务"
AI: → 调用 list 工具
用户: "帮我把 watchdog 服务的卡住的任务栈打出来"
AI: → 调用 task 工具(地址来自 list 输出)
```
## 功能
- **32 个 MCP 工具**,覆盖 debug console 全部命令(见下方工具清单)
- `raw_command` 兜底工具:原样透传任意命令行,兼容未来新增的命令
- 2 个资源:`skynet://services`(实时服务列表)、`skynet://help`(命令帮助)
- 1 个提示词模板:`skynet_troubleshoot`(按"只读 → 危险"顺序生成排查步骤)
- 有副作用的命令(kill/exit/inject/call/signal/...)**绝不自动重试**;只读命令传输失败时自动重试一次
## 通信原理
skynet debug console 支持 HTTP 通道(`POST / HTTP/1.0`,body 即命令行,响应为裸文本 +
`<CMD OK>` / `<CMD Error>` 标记后断开)。本项目用标准库 socket 手工构造该请求:
- 为什么不用 http.client/requests:skynet 的响应**没有 HTTP 状态行**(curl 需 `--http0.9`),
标准 HTTP 客户端无法解析
- 为什么用 POST 而不是 GET:POST 的 body 被服务端 `docmd(body)` 原样当命令行执行,
`call 3 "foo", 1, "bar"` / `inject 3 /home/x/patch.lua` 里的引号、逗号、斜杠路径都不会被 URL 编码破坏
## 项目结构
```
skynet-mcp/
├── skynet_mcp/
│ ├── main.py # FastMCP 入口(工具注册 + 资源 + 提示词)
│ ├── config.py # host/port/timeout(env + 命令行参数)
│ ├── backend.py # 裸 socket HTTP POST 通信层
│ ├── parser.py # 裸文本响应解析(去 Welcome/CMD 标记)
│ └── tools.py # 32 个工具定义
├── tests/ # mock console + 单元测试
├── examples/ # opencode 集成示例
└── scripts/smoke_test.py
```
## 安装(Linux,与 skynet 同机)
```bash
# 1. 获取代码(git clone,或拷贝已有目录到 ~/skynet-mcp)
mkdir -p ~/skynet-mcp && cp -r <代码路径>/* ~/skynet-mcp/
# 2. 创建 venv 并安装依赖(python3 需 >= 3.10)
cd ~/skynet-mcp
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/pip install -e .
# 3. 验证
.venv/bin/python scripts/smoke_test.py --port 8000
```
## 启动
MCP 服务器以 **streamable-http** 方式**独立启动**(手动,或交给 systemd / supervisor 等
进程管理器托管),监听固定端口,由 opencode 等客户端通过 HTTP 远程连接——不再由客户端
自动拉起子进程。
```bash
# WSL 内启动,默认监听 127.0.0.1:8765(Windows 侧经 WSL2 localhost 转发访问)
.venv/bin/python -m skynet_mcp.main
# 自定义 HTTP 监听端口
.venv/bin/python -m skynet_mcp.main --http-port 8765
```
HTTP 监听参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
| `--http-host` | `127.0.0.1` | HTTP 监听地址 |
| `--http-port` | `8765` | HTTP 监听端口(与 skynet console 端口区分) |
skynet debug console 连接参数:
| 参数 | 环境变量 | 默认值 |
|---|---|---|
| `--host` | `SKYNET_CONSOLE_HOST` | `127.0.0.1` |
| `--port` | `SKYNET_CONSOLE_PORT` | `8000` |
| `--timeout` | `SKYNET_CONSOLE_TIMEOUT` | `30`(秒) |
- 端点 URL:`http://127.0.0.1:8765/mcp`(MCP streamable-http 协议),
opencode / skynet-mcp-client 等客户端均通过该端点连接
- 安全:默认绑定 `127.0.0.1` 并开启 DNS rebinding 保护;若需跨机访问,
用 `--http-host 0.0.0.0` 并确保网络可信(或走 SSH 隧道),勿暴露公网
## 接入 opencode
先按上文**独立启动 MCP 服务器**,再用 `type: "remote"` 连接。**配置要写在 opencode 运行
的那一侧**——opencode 只读自己进程侧的全局配置 `~/.config/opencode/opencode.json` +
当前目录项目级 `opencode.json`;Windows 侧启动的 opencode 看不到 WSL 里的配置
(表现为 `opencode mcp list` 显示 `No MCP servers configured`)。
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"skynet": {
"type": "remote",
"url": "http://127.0.0.1:8765/mcp",
"enabled": true
}
}
}
```
- **WSL 内启动 opencode**:写 WSL 的项目根 `opencode.json`(本仓库已带)或全局
`~/.config/opencode/opencode.json`
- **Windows 侧启动 opencode**(PowerShell / Desktop):写 Windows 全局
`C:\Users\Admin\.config\opencode\opencode.json`(已有内容如 `instructions` 时合并保留)
或启动目录的项目级 `opencode.json`。`url` 仍用 `http://127.0.0.1:8765/mcp`——WSL2
localhost 转发会把 Windows 侧 `127.0.0.1:8765` 直通到 WSL 里监听的 MCP 进程,
**无需改 MCP 监听地址**
- **MCP 服务器需独立启动**(手动或进程管理器托管),opencode 不再自动拉起子进程;
服务器未启动时 opencode 会显示连接失败
- 局域网/公网访问需把 MCP 重启为 `--http-host 0.0.0.0`——但服务器**无鉴权**且有
`kill` / `inject` / `raw_command` 等危险命令,只建议走 SSH 隧道
(`ssh -L 8765:127.0.0.1:8765 user@remote`)或加 Bearer Token 鉴权,勿直接暴露公网
改完重启 opencode,在对话里输入 `/mcp` 确认 `skynet` 已连接,然后让它
"用 skynet 工具列出当前所有服务"即可端到端验证。完整测试与排障步骤见
`examples/opencode-mcp.md`;覆盖全部 32 个工具的测试提示词见 `examples/mcp-test-prompts.md`。
## 部署方式
1. **WSL/Linux 内直连(推荐)**:opencode、MCP 进程与 skynet 都在 WSL 内,
直连 `127.0.0.1:<port>`,零转发
2. **SSH 隧道(远端生产机)**:MCP 进程与 skynet 不同机时,
`ssh -L 8000:127.0.0.1:8000 user@remote`,opencode 连本地 8000 即可。
**切勿把 debug console 端口直接暴露到公网**
## 工具清单(32 个)
| 工具 | 底层命令 | 说明 |
|---|---|---|
| `help` | `help` | 全部命令帮助 |
| `list` | `list` | 列出所有服务及地址 |
| `service` | `service` | 列出唯一服务与挂起请求 |
| `stat [ti]` | `stat` | 消息队列/挂起请求/消息总数 |
| `mem [ti]` | `mem` | 各服务 lua 内存 |
| `gc [ti]` | `gc` | 全服强制 GC + 内存报告 |
| `netstat` | `netstat` | 网络连接概况 |
| `cmem` / `jmem` | `cmem` / `jmem` | C 层 / jemalloc 内存 |
| `dumpheap` / `profactive` | `dumpheap` / `profactive` | 堆分析 |
| `start` / `log` / `snax` | 同名 | 启动新服务(⚠) |
| `kill` / `exit` | 同名 | 中止服务(【危险】) |
| `signal` | `signal` | 打断死循环拿到调用栈(【危险】) |
| `task` / `uniqtask` | 同名 | 挂起请求调用栈 |
| `killtask` | `killtask` | 终止线程(⚠) |
| `info` | `info` | 服务内部信息 |
| `inject` | `inject` | 注入补丁脚本(【危险】,路径为 skynet 视角) |
| `dbgcmd` | `dbgcmd` | 任意 debug 协议命令(⚠) |
| `ping` | `ping` | 往返耗时 |
| `trace` | `trace` | 协议跟踪 |
| `logon` / `logoff` | 同名 | 记录服务输入消息 |
| `call` | `call` | 调用服务 lua 接口(【危险】) |
| `getenv` / `setenv` | 同名 | 环境变量读写 |
| `raw_command` | 透传 | 任意命令兜底(【危险】) |
地址写法:`:01000001`(八位 hex)、`1`(简写)、`.名字`(本地服务名)。
## 安全注意事项
- skynet debug console **无鉴权**,且只监听 `127.0.0.1`——远程使用请走 SSH 隧道,不要暴露端口
- 【危险】命令(kill/exit/signal/inject/call/raw_command)会影响运行中的服务,
工具描述中已标注;coding agent 调用前应让用户确认
- `debug` 交互式命令需要持久终端会话,HTTP 通道不支持,已显式拒绝(请用 telnet/nc 手动连接)
- `inject` 的脚本路径是 **skynet 服务器视角**(MCP 与 skynet 可能在不同文件系统)
## 开发与测试
> 通过 opencode 端到端测试全部 32 个 `skynet_*` 工具的可复制提示词见
> `examples/mcp-test-prompts.md`;下面是开发者侧的单元/冒烟测试。
```bash
# 单元测试
python -m pytest tests/ -v
# 冒烟测试(先起 mock console)
python -m tests.mock_console # 打印 mock 端口
python scripts/smoke_test.py --port <mock端口>
# 或对真实 skynet 冒烟
python scripts/smoke_test.py --port 8000
# 手工验证(nc 直连真实 console)
printf 'POST / HTTP/1.0\r\nContent-Length: 4\r\n\r\nlist' | nc 127.0.0.1 8000
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues