Skip to main content
Glama

mcp-mc-agent —— 给任何 MCP 客户端一副能玩 Minecraft 的身体

Minecraft 的 MCP server:34 个 mc_* 工具,覆盖进服/寻路/挖矿/合成/熔炼/存取箱子/看第一人称画面/记账。 任何支持 Model Context Protocol 的客户端(Claude Desktop、Cursor、DSH……) 连上它,模型就能真的去玩:走路、挖建、说话、看图、记事 —— 并在"值得你注意"的时候被叫醒。 不再绑定 DSH:DSH 只是众多 MCP 客户端之一。

本仓库是 whale-craft(作者 yzi1b)的魔改版。 上游那条线是 DSH 插件(旧名 dsh-mc-agent),本仓库是它的 MCP server 分支; CHANGELOG.md 里的旧名字(whale-craft)保持原样。

项

值

仓库

https://github.com/blueziii/mcp-mc-agent

入口

mcp/server.mjs(package.json 的 bin.mcp-mc-agent 指向它;npm start = node mcp/server.mjs)

传输

stdio 默认(--stdio);HTTP 可选(--http [端口,默认 3921],Streamable HTTP —— MCP 端点在 POST /mcp,GET /health 给探活)

状态目录

~/.mcp-mc-agent(Windows %USERPROFILE%\.mcp-mc-agent):accounts.json + credentials.json(mode 0600)+ config.json + 日志

会话工作区

--cwd,默认 process.cwd();记忆与落盘图片挂它下面的 .mcp-mc-agent/

前提

Node.js >= 22、一个能进的 Minecraft 服务器、一个支持 MCP 的客户端

一个 server 进程 = 一个 Minecraft 角色(MCP_SESSION_ID = 'mcp'):一份记忆、一个机器人。

接到 MCP 客户端

npm install          # @modelcontextprotocol/sdk + mineflayer + vec3;sharp 可选(装不上只影响 mc_kit_image 渲染)

然后在客户端的 MCP 配置里加一条 server。stdio(默认,推荐):

{
  "mcpServers": {
    "minecraft": {
      "command": "node",
      "args": ["<仓库绝对路径>/mcp/server.mjs"],
      "env": { "MCP_MC_AGENT_CWD": "<想当会话工作区的目录>" }
    }
  }
}
  • Claude Desktop 写进 claude_desktop_config.json,Cursor 写进 .cursor/mcp.json,其它客户端找各自文档里的 mcpServers 段 —— 形状都是上面这个。

  • args 用绝对路径。Windows 上 JSON 里要转义反斜杠(C:\\path\\mcp\\server.mjs),或者干脆写正斜杠。

  • env 可以整段省掉:不设 MCP_MC_AGENT_CWD 就用客户端拉起进程时的当前目录当工作区。

  • 想省掉路径:npm link(或 npm i -g .)之后把 command 写成 "mcp-mc-agent"。

HTTP(可选,给远程 / 网页客户端):

node mcp/server.mjs --http 3921                                    # 端口默认 3921
node mcp/server.mjs --http 3921 --cwd /path/to/world --state-dir /path/to/state

客户端那边填 http://<主机>:3921/mcp(Streamable HTTP);探活用 GET http://<主机>:3921/health。

命令行 / 环境变量速查:

参数

作用

--stdio

用 stdio 传输(默认;写了也只是显式声明)

--http [端口]

用 Streamable HTTP 传输,默认端口 3921

--cwd <目录>

会话工作区(记忆、落盘图片挂它下面;默认当前目录)

--state-dir <目录>

状态目录(配置 + 账户库 + 凭据;默认 ~/.mcp-mc-agent)

--version / -v

打印版本后退出(读 package.json)

-h / --help

这条帮助

🔴 --version / -h 在起 server 之前就短路退出 —— 不然进程会先进 stdio 模式挂住等 stdin, 命令看起来就是"卡死"。 矛盾 / 非法参数直接报错退出(exit 2),不悄悄挑一个:--stdio 与 --http 同时给、 端口不是 1–65535 的整数,都会打一行 stderr 就退。

环境变量:MCP_MC_AGENT_STATE_DIR · MCP_MC_AGENT_HOME · MCP_MC_AGENT_CWD · MCP_MC_AGENT_HTTP_PORT · MCP_MC_AGENT_VERBOSE=1(多打 stderr 日志)。 没有 DSH 时代的兼容别名 —— 只有 MCP_MC_AGENT_* 这一套。

  • 没装 mineflayer-pathfinder 时 mc_move 会直接拒绝(返回 via:"pathfinder-unavailable"), 只剩"在水里游"和"原地跳"能用 —— 寻路能力完全依赖它。

  • 依赖树体检:node tools\check-mc-deps.mjs(8 项)。

  • 不想连服务器也能跑的自检:node tools\check-all.mjs。

那五个生态插件(pathfinder / collectblock / tool / pvp / auto-eat)是可选的,装哪儿由你定: src\plugins.mjs 的 loadMcDep 依次找 ① MCP_MC_AGENT_MC_DEPS 指的目录(目录本身或它的 node_modules) ② 包旁边的 vendor\mc-deps\node_modules\ ③ 状态目录下的 vendor\mc-deps\node_modules\ ④ 包自己的 node_modules\ (package.json 里已列为 dependencies,npm i 之后就在这儿)。 缺哪个只是少那项能力,其它照旧。

Related MCP server: haksnbot-tools

账户(第一次用先做这个)

MCP 协议里没有"填账号密码"这种交互,所以账户从命令行管。账户库和凭据都落在状态目录里, 和 MCP 那边共用同一份 —— 在终端建好,AI 那边 mc_accounts 立刻就能看到。

node mcp/server.mjs accounts list                          # 看有哪些账户(★ = 默认)
node mcp/server.mjs accounts add offline 史蒂夫             # 离线(盗版服 / 单机)—— 最常用
node mcp/server.mjs accounts add yggdrasil \
  --url https://littleskin.cn/api/yggdrasil \
  --login 你的账号 --password-stdin --name 游戏名           # 皮肤站 / 第三方认证服
node mcp/server.mjs accounts add microsoft --email a@b.com  # 正版(微软):先只登记邮箱
node mcp/server.mjs accounts login acc-xxxxxxxx            # 正版:打设备码,去 microsoft.com/link 授权
node mcp/server.mjs accounts refresh acc-xxxxxxxx          # 换令牌 / 修登录状态
node mcp/server.mjs accounts use acc-xxxxxxxx              # 设为默认(之后 mc_connect 用它)
node mcp/server.mjs accounts remove acc-xxxxxxxx
  • accounts 与 --version / -h 一样在起 server 之前就短路退出 —— 不会挂住等 stdin。

  • --password-stdin 从管道读密码(别把密码写进命令行,那会进 shell 历史与进程列表); accounts 的输出从不打印密码或令牌。

  • accounts add microsoft 只登记邮箱,真正登录要再跑一次 accounts login(设备码得你去网页点)。

  • 忘了怎么用:node mcp/server.mjs accounts help。

跑自检

node tools\check-all.mjs            # 49 个套件:48 个 tools\check-*.mjs + mcp\selfcheck-mcp.mjs(几分钟)
node tools\check-all.mjs --fast     # 跳过端到端那一个(快)
node tools\check-all.mjs --only check-core-dig   # 只跑名字含这个子串的(`check-core-dig` 会连 `check-core-digtime` 一起跑)

任何 ❌ 都会让整体 exit 1。套件自己收拾现场(--keep 保留现场供排查)。

端到端那个(mcp\selfcheck-mcp.mjs)真起一个 server 子进程 + 真 MCP 客户端走完整 stdio 握手, 验的是"从客户端看这 server 像不像样",共 43 项:握手(instructions 非空)/ 34 个工具一个不少 / 一批离线工具真调 / 图片内联 / 34 个工具全传 {} 都不崩 / 并发不串台 / 4 个 resources 逐个读 / 状态落盘(含"凭据库不在工作区里")/ 协议通道干净(stdout 只有 JSON-RPC)。单跑:node mcp\selfcheck-mcp.mjs。

各套件的期望值(2026-10-16 实测:49/49 全过):

套件

期望

盯的是什么

check-mc-craft

184/0

合成链条、合成格不吞料

check-mc-furnace

267/267

熔炼、燃料挑选、产物名反查

check-mc-chest

126/126

存取箱子、全搬

check-mc-auto

246/0

自主层:反射/空闲决策/威胁判据;危险名单与搭路共用一份(含灵魂火);叫醒简报带"脚下一层"摘要

check-call-budget

143/143

单次调用 ≤11 秒、抢占推号

check-token-budget

37/0

mc_* 描述总体积 ≤ 18000 字符

check-core-place

77/0

放置判据、依附面、垫脚

check-core-dig

42/0

挖掘出口、复原包判决

check-core-digtime

27/0

挖矿速度修正(含附魔效率)

check-core-swim

99/99

水里游/上浮/憋气

check-core-map

162/0

地形图字形与配色、地板层图、竖列图(几格实心 / 哪里开始空 / 空腔·水·岩浆分开说)、坐标换算、危险格、导航摘要

check-core-inventory

70/70

背包视图、动作与装备/工具耐久("快坏了"看得见)

check-core-equip

78/0

换工具/穿护甲

check-core-useitem

39/0

用物品

check-core-entities

53/0

实体名单与挑选

check-core-attack

86/0

攻击判据("打死了"要有掉血证据)

check-core-evidence

45/0

"报成功要有证据"

check-session-memory

13/0

每会话独立记忆

check-rules-file

48/0

RULES.md 注入 + 内置默认准则(进服那段 / 工具名)+ 「随版本更新」只动默认那块 + 版本标记(rulesVersion)与旧标记迁移的静态接线

check-mc-observe-host

96/0(--gate-on 110/0)

第一人称感知

check-mc-view-render

112/0

渲染内核

check-mc-skin

78/0

皮肤

check-async-util

59/59

"等一件事"只有一份实现:waitUntil 判据顺序(已中止一次条件都不跑 / 当场成立一次都不等 / 到点不睡穿 / 条件抛错照样往外抛)+ waitForEvent 摘干净监听器 + 六处接线

check-worker-pool

61/0

Worker 池:默认关(一行线程都不开)/ 小活不搬 / 大活真搬且结果逐字节一致 / 搬了主线程真的不堵(心跳实测)/ 超时·中止·未知任务都如实退回原地 / 统计形状

check-msa

55/0

微软正版登录:authTitle 是 clientId 不是应用名(写成 "Minecraft" 会 invalid_client)/ 设备码缓存按账户分 / 两种命名都认 / 登录任务表只给状态不给令牌 / 账户库的 microsoft 类型 / 接线

check-task-epoch

86/0

任务代次:只有重排步骤/取消/收工才换代、两个 await 后各对一次、过期的结果不写账

check-mc-pathfinder

26/0

寻路接入

check-watch-chat

21/21

看门狗聊天唤醒

check-audit-session

46/0

会话审计

check-logging

55/55

日志轮转/级别/去重;端到端自检用临时状态目录跑(不往真日志/真账户库里写)

check-mc-deps

8/0

vendor 依赖可加载

check-action-scheduler

144/0

动作调度器:资源登记/父子复用 actionId/撞车记账/排队 + 抢占/中断释放/ALS 死锁防线

check-perf-monitor

48/0

性能监测:cpuRatio 判据、同步调用记账、短样本不给占比、开关零成本

check-move-progress

104/0

移动进度监控:只认"既没挪动也没靠近"、正常等待豁免有上限、脱困阶梯 → unreachable;路径被反复失效 ⇒ 不等满 2.4 秒直接进阶梯;到达判定分水平/垂直两轴,"站错层"优先于"别人的锅"报

check-bridge

108/0

搭路状态机:铺一块→回读确认→才走一格、危险/落差/血量/材料任一项不过就停、断点续铺;avoidBlockIds 把要命方块交给寻路器绕开

check-terrain

132/0

地形分类(危险/沟/落差/台阶/窄道/平地)与按类调权重:优先级从最要命的往下、一格台阶不把平地判成台阶、用户配置永远赢;导航缓存(没挪窝、世界没变就不重扫,unknown 不缓存)+ 实测判决「一次扫描 0.193ms ⇒ 不需要 Worker」

check-water-navigation

95/95

水下出口搜索:只读扫描 + 6 邻域分层 BFS、三类出口(干燥落脚点 > 可呼吸空间 > 可通行水道)、空气口袋只当中间目标、预算用完/找不到都如实报

check-water-recovery

103/103

水下恢复控制器:氧气/掉血/没进展触发、"搜出口 → 执行 → 复核真的动了吗"循环、没走通就拉黑换下一条、绝不偷偷接管(每次都通知 AI 一声)

check-module-load

30/0

拆出去的模块全都 import 得进来(含 core.mjs ≤ 1000 行,实测 999)

check-tool-def

14

builtinDefineTool 行为:render 返回 [{type:"text", text}](= MCP content block 形状)、toolDefKind() 取值、JSON Schema 转换与校验

check-watchdog-mcp

50

MCP 唤醒链路 + job 结算:ctx.__mcpWake 兜底、伪 agent 的 steer → onWake(自主层的叫醒通道在 MCP 下不再全哑)、唤醒写进会话事件队列、mc_stop 的强清路径认得"本会话的 job"、kill 之后看门狗真停、已结算的 job 自动摘掉

check-accounts-store

36

账户库 + 文件凭据库:mc_accounts {search}、离线账户"刷新"= 不需要认证、mc_connect 指名不存在的账户报错清晰、凭据不落工作区

check-core

✅ 全树正常

全树语法(node --check)+ 动态 import core.mjs 与其余 61 个模块 + 私有字段一致性(不打印 pass/fail,失败即 exit 1)

check-mcp-content

56/0

工具结果 → MCP content blocks:DSH 形状的 {type:'image',attachment} 必须丢掉(照搬 DSH 约定会让整个 tools/call 被客户端判 -32602 扔掉)/ 不合规降级成 text / 落盘图片兜底内联 / blockOk 不变量

check-mcp-cli

67/0

两块:① CLI 短路 —— --version / -v 秒退且 stdout 就是 package.json 的版本号、-h 列全所有开关、帮助里写到的开关源码里真有处理、矛盾/非法参数 exit 2(真踩过:--version 挂在 stdio 里 60 秒被超时杀掉)② accounts … 子命令 —— 帮助里列的都真有实现 / accounts list 秒退 / 用法错退出码 2、运行错 1 / 输出与 credentials.json 里不出现密码明文

check-mcp-package

67

打包 / 改名彻底性 / MCP 接线(工具表与 bin 指向、selfcheck.mjs 之类旧文件确实没了、文档不在旧名残留的扫描范围里)

check-mcp-http

23

HTTP 传输:POST /mcp 握手 + 34 个工具 + inputSchema 都是 object、非法入参在 HTTP 上也回 isError、4 个 resources、没有 session 头时被拒(不是静默成功)、未知路径 404

check-standalone-import

✅ 成功

npm pack → 干净目录 npm install → import('mcp-mc-agent') 必须成功(慢,CI 跑)

mcp/selfcheck-mcp

43 ✅ / 0 ❌

端到端:真起 server 子进程 + 真 MCP 客户端握手(见上)

🔴 静态断言("读源码文本 → 正则匹配"那一类)在拆分模块时最容易悄悄失效 —— 代码搬到别的文件、this. 改成 self.,断言还在原地找,于是一直红着没人看。 搬完模块先跑 node tools\scan-moved-assertions.mjs(顾问型),再核对上表数字。

配置速查

会话级配置用 mc_config 读写;下面的键写在状态目录的 config.json(~/.mcp-mc-agent/config.json), 或者直接用 mc_admin_config / mc_config 工具改 —— 改完立即生效,不用重启 server。

键

默认

说明

memoryDir

<会话工作区>\.mcp-mc-agent

记忆根。MCP 的"工作区"就是 --cwd(默认 process.cwd());设了 memoryDir 就按它走

memoryPerSession

false

true = 每会话一份独立记忆,互相看不见

pathfinder

true

关掉/给路径,等价于 MCP_MC_AGENT_PATHFINDER

walkCanDig / walkDigCost

true / 4

寻路能不能挖穿墙

walkPlaceCost

12

垫方块的代价(上游默认 1,比挖还便宜 ⇒ 会往空气上搭)

walkMaxDropDown

3

最多往下跳几格

walkAllowParkour

false

跑酷

pfThinkTimeoutMs / pfSearchRadius

8000 / 192

A* 思考上限 / 搜索半径

autoEatAt

14

饱食度低于它就吃

moveBudgetMs / collectBudgetMs

很大

会被 10 秒上限钳住

wearArmor

true

自动穿护甲

avoid

true

自主层自保(躲岩浆/火/仙人掌)

idleWakeSec / idleWakeMaxPerHour

5 / 120

空闲多久叫醒 AI / 每小时上限

minIdleGapSec / idleWakeNearPlayerSec

5 / 5

两次叫醒最小间隔 / 旁边有人时的间隔

单独文件(都在 <memoryDir>\):

  • auto.json —— 自主层开关;view.json —— 看图门禁({"gate":true,"maxActions":8,"maxAgeMs":120000},默认不强制)。

  • RULES.md —— 行事准则(2026-10-08 起内置默认与记忆里那份是同一份文本)。

  • 版本提示.md —— 2026-10-08 起只投一行指针。

server 自己的环境变量(决定进程怎么起,见上一节):MCP_MC_AGENT_STATE_DIR · MCP_MC_AGENT_HOME · MCP_MC_AGENT_CWD · MCP_MC_AGENT_HTTP_PORT · MCP_MC_AGENT_VERBOSE=1。 插件行为类环境变量:MCP_MC_AGENT_PATHFINDER(off 或路径)· MCP_MC_AGENT_MC_DEPS · MCP_MC_AGENT_MEMORY_DIR · MCP_MC_AGENT_AUTO · MCP_MC_AGENT_VIEW_GATE / _VIEW_GATED / _VIEW_MAX_ACTIONS / _VIEW_MAX_AGE_MS · MCP_MC_AGENT_SKIN_TIMEOUT_MS · MC_LOG_LEVEL(quiet|info|debug) · MC_LOG_MAX_BYTES · MC_LOG_KEEP · MC_HOST / MC_PORT · MCP_MC_AGENT_SCHEDULER(observe-only(默认) / enforce = 撞车时排队等、够高就叫停低的)· MCP_MC_AGENT_PERF(off 关掉性能监测,默认开)· MCP_MC_AGENT_PERF_RESOLUTION(事件循环延迟直方图分辨率 ms,默认 100,最小 10)· MCP_MC_AGENT_PERF_KEEP(每个动作留多少条样本,默认 20)· MCP_MC_AGENT_WORKERS(Worker 线程池:不给 = 关;数字 = 池子上限(≤4);on = 自动取)。 默认关是量过才这么定的 —— 唯一够格搬走的是 mc_map {format:"image", radius:96, scale:16} 的像素循环(943 万像素、28ms), 其余候选都在 16ms 以内,而且大头是搬不动的 bot.blockAt()。

工具速查(34 个)

三层命名空间:mc_* 游戏内动作、mc_kit_* 宿主能力、mc_admin_* 管理。 mc_connect / mc_disconnect / mc_map / mc_kit_image 不受 11 秒闸门(一次调用要把握手或算图做完)。

连接与账户

工具

干什么

mc_status

在线/坐标/朝向/血量/饱食度/手持/模式/看门狗/最近 3 条聊天;verbose:true 看完整明细

mc_lan

探测局域网服务器(只读):只听多播 224.0.2.60:4445 的公告,不扫端口,恒定 seconds 秒内返回(默认 3、上限 15)

mc_ping

已知地址时发一次 STATUS ping:通不通/版本/协议号/MOTD/人数/延迟;不登录不进服;连不上会翻译成人话(ECONNREFUSED=端口没人听 / ENOTFOUND=域名拼错 / 超时=防火墙)

mc_connect

进服。没有账号密码,用哪个账户由账户库(mc_accounts)维护;地址/端口/子服自己传(host 不传就看环境变量 MC_HOST,两个都没有会直接报错);连上等区块加载。force:true 会先断开再重登(玩家会看到你退游戏几秒)

mc_accounts

列出/搜索/刷新/选定账户。永远拿不到密码或 token;needUserAction 时要明确让用户在终端跑 accounts login / accounts refresh(见 账户)—— 密码只有用户能填

mc_capabilities

边界信息:支持的 MC 版本范围(testedVersions)、mineflayer 版本、登录方式、各种上限。进服前不确定版本能不能连时先看它

mc_sessions

列出所有活跃 MC 会话实例(诊断用)

mc_disconnect

从 MC 下线,不影响其他会话的机器人;看门狗自动关闭

mc_stop

先优雅退游戏、再清本会话全部后台任务(看门狗在内)。不中断当前这一轮,适合"我不想玩了,下线"

感知

工具

干什么

mc_observe

👁 第一人称视角:从眼睛(眼高 1.62)朝正看方向渲染截图并内联,附三段式感知:① 我看到(图里数出来的)② 程序检测到(坐标/射线打到的方块/半径内实体带血量)③ 不确定(客户端预测、未加载区块、读不到的血量)。两套眼睛对不上时点名,以程序射线为准。门禁默认关,写 view.json 的 {"gate":true} 可变回强制

mc_scan

扫描周围方块:给 name 找位置,不给返回统计

mc_map

看周围地形;默认给"脚下方块那一层"(地板层:·能站 #墙 v会掉 ~水 ≈岩浆 ?未加载)。mode:"auto" 地下自动切洞穴层(↓能站 *矿石)/ "surface" 地表 / "cave" / "column" 竖列(只看你脚下一格,往下几格实心、哪里开始是空的 —— 挖之前先问它"会不会掉下去")。format:"image" 额外生成俯视图

mc_inventory

看背包、装备栏和手持物品

mc_events

读/消费看门狗记下的事件(聊天、系统消息、受伤、上线/死亡/重连/断线);peek:true 只看不清。被传送/捡物/其他玩家上下线不在这里(用 mc_watch {action:"log"})

mc_watch

看门狗控制(进服自动挂载、退服自动卸载)。默认叫醒:有人喊我 / 身边有人说话 / 任何玩家说话 / 受击 / 死亡 / 被传送 / 断线。⚠️ MCP 下唤醒走"事件队列 + MCP 日志通知 + mc://wake-log"三条腿,内容以 mc_events 为准(见 MCP 唤醒机制)

mc_config

读写本会话 MC 配置(唤醒条件、近距半径、心跳、观察窗口、叫法)。不传参 = 看当前 + 默认;嵌套项用点号键

mc_diag

诊断:机器人内部状态(物理/控制位/收包/事件队列/合成窗口)+ 提示词注入状态(promptInjection)

动作

工具

干什么

mc_move

walk(自动避障;在水里自动改成直接游,实测 2 格/秒)/ fly(创造默认)/ jump(原地跳,不需坐标)/ swim(不给坐标就只上浮换气)。一次只走 10 秒,没走到回 arrived:false + remaining + hint,再叫一次接着走

mc_act

look 看向坐标/玩家 · toward 看向并走近 · place 放方块(悬空先垫脚,带 used/held)· break 破坏(先自动换对的工具,换不上就拒绝)· use 使用方块或实体(都不给 = 用手上的东西)· useitem 用手上的东西 · attack 打实体(中英文名都认,24 格内会追)· equip 装备(护甲自动穿到对应格)

mc_dig

挖方块:给 name 挖最近的、或给 pos 挖指定坐标;count 连续挖多个;会自己换成对的工具。一次只挖 10 秒的量

mc_build

批量搭方块:(x1,y1,z1)–(x2,y2,z2) 实心长方体,会自己走近/垫脚,结果带 used/held。一次只放 10 秒的量,没放完回 remaining + hint,范围原样重发(放对的自动跳过)

mc_sequence

按顺序执行一串世界交互(替代"写脚本"):wait/move/look/toward/place/break/dig/use/useitem/attack/equip/give/toss/say/jump/swim。整串只跑 10 秒

mc_give

创造模式直接获取物品(不走 /give,是协议级 set_creative_slot,非 OP 也能用);clearAll:true 清空背包

mc_say

在 MC 公屏说话(所有玩家可见)

mc_command

以玩家身份执行服务器指令。白名单可配(默认 tp/give/time/weather/say/gamemode/effect/setblock/fill/clone/summon/title/clear/xp)。最后手段:正经动作优先 mc_act / mc_build / mc_move

长活儿与自主

工具

干什么

mc_task

长活儿进度台账(挖 4 个以上自动开),存 <记忆根>\tasks.json,掉线重连都还在。run 一轮推进一步、resume 复核、plan/step 记账

mc_auto

🤖 自主行为层。① 反射层(不花 token、常开):环顾、饿了吃、捡掉落物、躲岩浆火仙人掌、跟随玩家;被怪打或有怪贴上来立刻叫醒 AI 定策略(自己不跑不还手);在水里憋气也叫醒 AI 上浮;附近有玩家时一直看着他。② 空闲决策层(花 token):AI 停下 idleWakeSec 秒就带"玩家在干什么 + 长期目标 + 没做完的任务"叫醒它,不许反问 Master;叫醒 45s 后没动手再催一条;每小时最多 idleWakeMaxPerHour 次,没做事就翻倍拉长(封顶 30 分钟)

合成 / 熔炼 / 箱子

工具

干什么

mc_craft

🧱 真合成(走服务端合成台)。自动补齐中间产物(要 wooden_pickaxe 自己排"木板→木棍→木镐");需要工作台时附近有用、没有就自己放脚边;原料不够会报缺什么、缺几个、现在有几个并给出下一步调哪个工具;name 可写简称(planks 自动选背包那种原木对应的木板);list:true 看配方、dryRun:true 只出计划

mc_furnace

🔥 真熔炼(走服务端熔炉)。写产物名也行(name:"iron_ingot" 自动找粗铁/铁矿石);附近没熔炉但背包里有就自己放脚边;自己挑燃料(有煤/木炭先烧煤,木棍木板是合成料只在没正经燃料时才烧)

mc_chest

📦 真仓库(服务端箱子/木桶)。list/put/take;附近没箱子但背包里有就自己放到脚边;all:true 是真全搬(工具也会被放进去)

宿主工具

工具

干什么

mc_kit_image

图像能力。SVG 是编辑语言:布局/画框/加中文/拼网格都写 SVG 文本,再光栅化成 PNG。info / embed(图片变 data URI + 现成 <image> 标签,往 SVG 里引图必须这么做)/ render / grid / save。出的图直接以 MCP image content 返回给模型看

mc_kit_express

把发布区 .mcp-mc-agent/.express/ 里的文件换成"给用户的东西"。看 mc_admin_config 的 expressEnabled / expressBase 开没开:关(默认)= 只回一句 + 绝对路径,开 = 回完整 URL。只有 .express/ 下的可分享,默认输出目录 .out/ 不对外。⚠️ MCP 下没有内置 HTTP 文件服务(那条路由原本由 DSH 宿主提供),所以实际用关闭模式、把绝对路径给用户最靠谱;图片本身不用走它 —— mc_map / mc_observe / mc_kit_image 的图直接以 MCP image content 返回

mc_kit_memory

长期记忆(跨会话、重启还在),按服务器建子文件夹(_global/ 放通用)。README.md 索引由 AI 自己维护;MCP 下它的内容 + 自动目录树在 initialize 时拼进 instructions(全文见 mc://prompt)。index / kinds / record(带元数据,同 key 累加成功失败)/ read(读图片会作为附件)/ append / put / search / delete

mc_admin_config

【管理】读写全局配置(状态目录的 config.json)。get/set/unset/reset/list;有用的键:commandWhitelist("tp" 精确、"/^gi.*/" 正则、"*" 全放行)、allowAllCommands、expressEnabled、expressBase、memoryDir、memoryPerSession。DSH 遗留键(mcModePresets / mcPlusPresets / mcMode.allowOtherTools / mcMode.hideAdminTools / ensureMcPreset)在 MCP 下没有效果 —— MCP 没有 preset 体系,这个进程只暴露那 34 个工具

MCP 唤醒机制

MCP 是**"你问它答":server 不能主动往模型上下文里塞消息(DSH 的 agent.steer 能直接起一轮 / 在下一步插话,MCP 没有对应能力 —— mcp/host.mjs 桩上那个同名 steer 只是把消息转进下面这三条通道**, 好让 src/watchdog.mjs 和 extensions/mc_auto.mjs 那些 typeof agent.steer === 'function' 的守卫照常成立)。 所以看门狗的唤醒走三条腿,模型要主动收:

通道

机制

谁看得见

主通道:会话事件队列

唤醒文案以 【看门狗唤醒·<kind>】… 写进本会话的事件队列

模型下次调 mc_events 必然看得见(mc_events 是消费:取走即删;只想看用 peek:true)

提示通道:MCP 日志通知

同时发一条 notifications/message(level: "warning"、logger: "mcp-mc-agent")

支持 logging 的客户端会立刻显示给模型;它只是提示,内容以 mc_events 为准

兜底:mc://wake-log

最近 50 条唤醒存在内存里(服务端进程内环形缓冲)

通知丢了/客户端不支持 logging,读这个 resource 补

  • 看门狗照旧进服自动挂载、退服自动卸载;唤醒条件(有人喊我 / 身边有人说话 / 任何玩家说话 / 受击 / 死亡 / 被传送 / 断线)与 DSH 版一致。

  • 想安静等:mc_events {waitSec} 会被"要唤醒你"打断(返回 interrupted:true),不会把唤醒压到等待结束。

  • 结论:该做的是定期 mc_events;不要指望通知一定送到。

从 dsh-mc-agent 迁移

本仓库的 DSH 插件分支(旧名 dsh-mc-agent)和这个 MCP server 是同一套代码的两个壳: 34 个工具的名字、参数、返回值一模一样,账户库与记忆格式也兼容 —— 记忆里写的工具名不用改。

要搬的东西

旧位置(DSH 插件)

新位置(MCP server)

全局配置

$DSH_HOME/dsh-mc-agent/config.json

~/.mcp-mc-agent/config.json

账户库

$DSH_HOME/dsh-mc-agent/accounts.json

~/.mcp-mc-agent/accounts.json

密码 / token

宿主凭据服务($DSH_HOME/.credentials.yaml)

~/.mcp-mc-agent/credentials.json(mode 0600)

MSA 令牌缓存

$DSH_HOME/dsh-mc-agent/msa/<账户>/

~/.mcp-mc-agent/msa/<账户>/

记忆 / 提示词

<会话工作区>/.mcp-mc-agent/

一样("工作区"= --cwd)

不会自动接管(目录名和位置都变了)。想带着配置和账户过来,手动搬一次:

cp -r "$DSH_HOME/dsh-mc-agent/." ~/.mcp-mc-agent/          # 搬过去
# 或者干脆指回老目录:
MCP_MC_AGENT_STATE_DIR="$DSH_HOME/dsh-mc-agent" node mcp/server.mjs

🔴 密码搬不过来:DSH 版把密码/token 交给宿主凭据服务,MCP server 是独立进程,用的是自己的 credentials.json(key 仍按 mcp-mc-agent/<innerID> 组织)。所以正版 / 皮肤站账户要重新登录一次 (离线账户不用);账户条目本身在 accounts.json 里,会跟着搬过来。 重新登录就是在终端跑 node mcp/server.mjs accounts login <innerID>(正版,打设备码)或 accounts refresh <innerID>(皮肤站,会问你密码)—— 见 账户。

另外两处"没了":MC 模式 / MC+ 那套 preset 与权限隔离在 MCP 下不生效(改成"这个进程只暴露这 34 个工具"), Web GUI / 设置页也没有了(改配置用 mc_admin_config / mc_config,或者直接编辑 config.json)。

架构速览

mcp\ 六个文件 + index.js + 62 个 src\ 模块 + 7 个扩展。依赖方向: mcp\server.mjs → mcp\host.mjs → index.js → extensions\ → src\; src\ 内部单向,新模块不许反向 import 回 core.mjs(靠 setXxx() 注入或收 ctx)。

宿主侧

文件

干什么

mcp\server.mjs

MCP 入口:stdio / Streamable HTTP 两种传输、tools/list + tools/call、4 个 resources、instructions 拼装、工具结果 → MCP content blocks(图片内联)、唤醒 → 事件队列 + notifications/message

mcp\host.mjs

给 index.js 的 apply(ctx, config) 喂宿主桩(假 ctx / 假 exec)。ctx.webServer 是空实现、ctx.tools.guard 故意不生效、agentPresets 假装成 minecraft(让"MC 模式"判据成立)

mcp\content.mjs

工具结果 → MCP content blocks:renderContent(text / image / resource)+ blockOk 校验 + IMAGE_MIME / MAX_INLINE_IMAGE_BYTES(4MB) / mimeOfFile / findImageFiles / safeJson。抽出来才能离线单测

mcp\accounts-cli.mjs

accounts … 子命令(MCP 协议里没有填账号密码的交互,所以账户只能从命令行管):list / servers / add offline|yggdrasil|microsoft / login / refresh / use / remove / help。输出从不打印密码或令牌

mcp\credentials.mjs

本地文件凭据库(<stateDir>/credentials.json,0600,key = mcp-mc-agent/<innerID>),接口对齐 DSH 的 ctx.get('credentials')

mcp\selfcheck-mcp.mjs

端到端自检:真起 server 子进程 + 真 MCP 客户端握手(43 项)

index.js

5251 行。工具注册(29 个)、看门狗接线、stopSession、文件保护、mc_admin_config、日志落盘

src\ 模块

模块

干什么

core.mjs

McBot 内核(999 行)。走路接 pathfinder、单次调用 ≤11 秒、日志基建、actionScheduler 接线

connection.mjs

连接与保活:connect / scheduleReconnect / disconnect;断线时 actionScheduler().releaseAll()

runtime.mjs

运行时管线:reportError(唯一错误上报口)/ withBudget(超时闸门)/ stopPathing / interruptMove / setAbortSignal / holdControl / waitForChunks / requireBot

status.mjs

连接/状态视图(connectionViewOf / statusOf,离线 ghost/reconnecting,在线坐标/朝向/生存/氧气/手持/在线时长)

config.mjs

配置默认值与合并(715 行)

accounts.mjs / auth-session.mjs

账户管理与认证(全插件唯一碰凭据的地方:authOnly / makeSession / refresh / authenticate)

ping.mjs / lan.mjs

STATUS ping / 局域网多播监听(lan.mjs 全程不抛)

plugins.mjs

vendor 依赖加载器(MC_DEPS / loadMcDep / loadPathfinder)+ 装配(setupPathfinder / movements / setupPlugins)

movement.mjs

移动原语:jump / swimUp + 移动主体 walkSwim / walkTo / flyTo / walkPathfinder。唯一的移动实现,不再保留手写按键/盲跳/挖方块兜底

place.mjs / place-items.mjs

放置那一层(placeRef 找依附面 / placeBlock 够不着先走、悬空先垫脚、放完回读)+ 挑方块与垫脚料(NON_FULL_BLOCK / isFullBlockItem / scaffoldingItemIds / pickPlaceItem)

build.mjs

搭建:范围 → 坐标 → 逐个 placeBlock;只算复核过的、跳过已占用、到点带着结果收手

bridge.mjs

搭路(mc_act {mode:"bridge"}):铺一块 → 回读确认 → 才走一格的状态机。开铺前先过危险闸(脚下实地 / 正下方岩浆火 / 落差 ≤8 / 血量 ≥8 / 有整方块料),任何一条不过就一块都不放;中途任何一处对不上立刻停;已铺好的格子跳过(断点续铺);一次最多 16 格

terrain.mjs

地形分类 + 路径评分(导航 P2):量脚边一圈的柱子,归成危险 / 沟 / 落差 / 台阶 / 窄道 / 平地,每类给一套寻路权重(maxDropDown/liquidCost/digCost/placeCost)。只调权重、不换算法;用户显式配置(walkMaxDropDown 之类)永远赢;判不出来就按平地走。结果按 TERRAIN_CACHE_MS 缓存,blockUpdate/区块加载卸载就作废

water-navigation.mjs

水下出口搜索(只读 + 纯计算,一行按键操作都没有):以脚下为中心,水平 8 格 / 上下 8 格做 6 邻域分层 BFS,把格子分成水 / 空气 / 挡路 / 未加载,挑出三类出口 —— 干燥落脚点(最优先)> 可呼吸空间 > 可通行水道。不只看"是不是空气":要查脚位 + 头位 + 脚下支撑 + 一条真的连得通的路;空气口袋只当中间目标;预算用完老实回 budget-out,找不到回 no-exit-found,不假装找到路

water-recovery.mjs

水下恢复控制器:waterStateOf 读氧气/血/在水里,waterTriggerOf(纯函数)判该不该救,recoverFromWater 走"搜出口 → 用现有接口执行 → 复核真的动了吗 → 没走通就把这条拉黑再搜"的循环。移动全靠注入的 ctx.walkTo / ctx.swimUp(不另起一套游泳按键控制);复核不认"离目标够近"(出口就在头顶一格时人没动也够近),认"真到了 / 真近了 / 气回来了 / 出水了"

msa.mjs

微软正版登录(叶子模块,只 import node:*):Authflow 走设备码(flow:'live' + authTitle = 官方 clientId + deviceType:'Nintendo'),令牌缓存按账户分(<stateDir>/msa/<innerID>/);msaLogin 给账户库(mc_accounts)的显式登录用,msaSession 给连接用 —— 后者绝不停下来等用户(缓存没了就报 needUserAction 指路去账户库重新登录);msaJobOf 给 HTTP 层的状态只含 state/设备码/结果,绝不含令牌

mining.mjs

挖掘:breakBlock(够不着/没落地/换不上工具/时间不够都不动手;服务端说"复原"就不报成功)+ dig(一条路走过去挖,服务端不认就如实说)

dig-guard.mjs

采集/落地守卫:collectBusy / cancelCollect(停任务 + 松控制位,等不到就清队列)/ waitGrounded / withHeld

dig-speed.mjs

挖矿速度修正(上游把 108 个方块的 material 写成品级门标签,铁矿石要 4.5 秒)

server-break.mjs

服务端破坏判决(watchServerBreak 三态:真挖掉 / 复原 / 没证据;awaitServerBreak 等服务端的包,可晚到约 1 秒)

pickup.mjs

捡掉落物(grabDrops 按背包差报数;捡不到显式收尾,不让采集任务挂着)

attack.mjs

攻击(who 中英文名/玩家名都认;pvp.attack 只是设目标,自己轮询等它消失)

world-use.mjs

使用/交互:useBlock(容器看窗口、门看属性翻没翻)/ useItem / interact(look·place·break·toward 分流)/ equip

world-scan.mjs

感知最后两块:scanAround(方块计数/点名,按距离升序)/ entitiesNear(附近有谁:分类/血量/远程/上下差)

world-observer.mjs

只读观察:entityHealth / inWaterNow / slowDigWhy / dropsNear / dropHint / blockReachOf / stateNameOf

observer.mjs

世界观察器整层:invSignature / observeTick(每秒产出 teleport·pushed·pickup·playerJoin/Leave)/ startObserver·stopObserver

map-render.mjs

地图:isUnderground / caveCell / heightmapOf / glyphMapText / mapImageOf(自带 2.5 秒软上限)

camera.mjs

朝向/相机:lookAnglesTo / aimAt(直接写 entity.yaw/pitch,绕开会挂的 bot.lookAt)/ lookAt

inventory.mjs

背包那一层(读 + 动):heldText / invSnap / invUsed / canHarvestNow / toolKinds / countOf / freeSlot / inventoryView / giveItem / clearInventory / tossItem / equipFor

sequence.mjs

连串动作调度:runSequence(预算/中断/从第几步续跑;不为没执行的步骤记账)/ runStep

task-runner.mjs

可恢复的任务状态机(落 <记忆根>\tasks.json,只记账不下发动作)+ 结构化目标与步骤表(GOAL_TYPES / STEP_STATUSES / defaultSteps / normSteps / migrateTask / stepBegin / stepDone / stepFail / stepReset)

task-exec.mjs

任务执行器:每轮只推一步(observe() 看现场 → goalDone() 复核 → 取当前步 → call(op,args) 调真工具 → 记账);四道闸门(budgetMs / maxRounds / maxAttempts / 工具自己的 11 秒);"结果未知"不记失败;自己不碰 bot,工具由 core 通过 TOOL_HANDLERS 回调进来

task-report.mjs

任务收工报告(三种形状并存):taskHave / verdictLine(三态判决)/ taskSettle + 任务门面 taskStart/taskList/taskGet/taskResume/taskNote/taskDone/taskCancel/taskForget/taskPlan/taskStep

action-scheduler.mjs

统一动作调度器:RESOURCES / PRIORITY / MODES / TOOL_SPEC / ActionScheduler(run/cancel/父子复用 actionId/冲突记账/releaseAll/排队 waitMs + 抢占 #preempt + AsyncLocalStorage 认父亲)

perf-monitor.mjs

性能监测(perfWrap 包住每次工具调用,记墙钟 / process.cpuUsage() / 期间事件循环延迟)+ perfVerdict() 直接给"哪个动作该搬 Worker"的中文结论。主信号是 cpuRatio(CPU÷墙钟),不是 loop* —— 直方图本身带一个分辨率级底噪。mc_diag 的 perf 段就是它

watchdog.mjs

看门狗(唤醒矩阵 wakeOn.*,714 行)

memory.mjs

记忆四分类 + 元数据(world/lessons/tasks/RULES;来源·更新·成功失败次数·适用版本·条件)

chat.mjs

聊天记录(签名/未签名两条路 + 按渲染形状兜底;1.5 秒去重;chatSay 公屏说话,/ 开头拒)

agentsmd.mjs

AGENTS.md / 提示词注入(326 行)。MCP 下这些条目由 mcp\server.mjs 拼进 instructions(全文 mc://prompt)

version-prompt.mjs / connect-prompt.mjs / user-message.mjs

版本提示 / 进服提示 / 玩家消息转成提示

protected.mjs

受保护文件(RULES.md / AGENTS.md / config.json 不许改)

block-names.mjs

方块名判据(空气/液体/能穿过去/软顶)+ 地形图字形与配色 + 右键交互判据

entity-names.mjs

实体名单与分类(敌对/远程/掉落物)、中文别名、entityNameOf / matchNameOf / pickEntity

budget.mjs

单次调用时间预算(纯常量 + 纯函数):TIMEOUTS / CALL_MS / CALL_BUDGET_MS / callBudget / callDeadline / callSecText / budgetSecText

format.mjs / log.mjs

无损 JSON + 家目录脱敏 / 日志基建(本地时间、级别、去重、轮转)

async-util.mjs / wait.mjs

异步叶子:sleep / withTimeout / raceAbort / abortError + 全仓库唯一一份"等"的实现 waitUntil(判据轮询)/ waitForEvent(真事件);wait.mjs 是 mc_events {waitSec} 的阻塞等待(会被"要唤醒你"打断)

worker-pool.mjs / worker-tasks.mjs / worker-entry.mjs

Worker 线程池(默认关):runInWorker 把纯计算交给线程;任务实现只在 worker-tasks.mjs(主线程与线程同一份);起不来 / 崩了 / 超时 / 活太小 ⇒ 原地跑同一份实现并如实标 via

image.mjs / png.mjs / express.mjs

图像编解码 / PNG 写出 / 文件分享

wsconfig.mjs / serverhistory.mjs

工作区配置 / 服务器历史

tool-def.mjs

工具定义辅助(294 行)

extensions\(改完重启 server 即生效)

文件

干什么

mc_util.mjs

共用件(不是扩展):物品/方块名与 id、countOf、放得下吗…

mc_observe.mjs

第一人称截图 + 三段式感知(注册 mc_observe)

mc_view-render.mjs

渲染内核(不注册工具)

mc_skin.mjs

皮肤解析与渲染(不注册工具)

mc_auto.mjs

自主行动层(注册 mc_auto)

mc_craft.mjs / mc_furnace.mjs / mc_chest.mjs

合成 / 熔炼 / 存取箱子

5 个现成依赖(不自己写这些能力)

MC_DEPS 键

包

接管了什么

接在哪

pathfinder

mineflayer-pathfinder@2.4.5

寻路 / 防卡住

McBot.walkTo()(见 PATHFINDER.md)

collectBlock

mineflayer-collectblock@1.6.0

走过去挖方块 + 捡掉落物

McBot.dig() 优先交给它;mc_auto 的 pickup

tool

mineflayer-tool@1.2.0

按方块挑最好的工具

dig() 退回原地挖时 equipForBlock;包内打过补丁

pvp

mineflayer-pvp@1.3.2

追击 + 近战节奏

McBot.attack({who})

autoEat

mineflayer-auto-eat@5.0.3

饿了自动吃

mc_auto 只按开关 enable/disable

#movements() 造的那份 Movements 同时给 pathfinder / collectBlock / pvp 用 —— 免得"我自己走不乱跳、 插件走就乱跳"。缺哪个只是少那项能力,其它照旧。

目录

mcp\server.mjs                         MCP 入口(stdio / Streamable HTTP)
mcp\host.mjs                           宿主桩(假 ctx / 假 exec)
mcp\credentials.mjs                    本地文件凭据库
mcp\selfcheck-mcp.mjs                  端到端自检(真 server + 真 MCP 客户端,43 项)
index.js                               宿主侧(工具注册 / 看门狗 / 配置)
src\                                   62 个模块(见上表)
extensions\                            7 个扩展 + mc_util.mjs
tools\check-*.mjs                      47 个离线自检套件
tools\check-all.mjs                    一条命令全跑完 + ✓/✗ 汇总表
tools\scan-moved-assertions.mjs        顾问型:搬模块后的断言扫描
README.md / 插件说明.md / 踩坑档案.md / PATHFINDER.md / CHANGELOG.md

东西都落哪去了:

东西

位置

状态目录(全局配置 · 账户库 · 凭据 · 日志 · MSA 令牌缓存)

~/.mcp-mc-agent/(Windows %USERPROFILE%\.mcp-mc-agent\),可用 --state-dir / MCP_MC_AGENT_STATE_DIR 改

记忆 / 提示词 / 出图

<会话工作区>/.mcp-mc-agent/:README.md(AI 维护的总索引)+ RULES.md(行事准则)+ 任意文档/图片

默认输出 / 发布区

<记忆根>/.out/(不对外)· <记忆根>/.express/(可分享,见 mc_kit_express)

任务台账

<记忆根>/tasks.json

.mcp-mc-agent/ 里的东西只读写文件,不执行任何东西。凭据绝不写进工作区(工作区是喂给 AI 的)。

相对上游改了什么

上游 whale-craft 是一个能连服的 mineflayer 壳;这个版本把它做成了"能长期自己玩"的 agent。 按能力分类(每条的具体来龙去脉在 踩坑档案.md):

安全与诚实

  • "报成功必须有证据":break/place/dig 的判据一律回读服务端状态;服务端发回原方块包 (拒绝破坏)绝不算成功(server-break.mjs 三态判决)。

  • 不许骗人:place 没出现方块、use 没开窗口、attack 没打死,都如实说; mc_connect {force:true} 的说明写明"玩家会看到你退游戏几秒"。

  • 受保护文件(RULES.md / AGENTS.md / config.json)不许改。

不卡死

  • 单次调用 ≤11 秒:mc_move/mc_dig/mc_build/mc_sequence 到点带结果收手, 返回 remaining + hint 让模型接着叫 —— 挂住的时候用户看不到任何消息。

  • 超时闸门走 abort signal + 本地 deadline;客户端取消这次调用能立刻真停(interruptMove 推号 + 停采集 + 松控制位)。

  • 挖矿速度修正:上游 material 写错导致铁矿石要 4.5 秒,现在石镐挖铜矿 4550ms → 1302ms。

  • pathfinder 接进来 + 两处补丁 + walkPlaceCost 调高(上游默认 1 会让机器人往空气上搭)。

能自己玩

  • 看门狗:进服自动挂载,聊天/受击/死亡/传送/断线都会叫醒 AI(MCP 下是"事件队列 + 通知",见 MCP 唤醒机制)。

  • 自主层 mc_auto:反射层不花 token 地环顾/吃/捡/躲/跟随,被怪打或憋气时叫醒 AI 定策略; 空闲决策层带"玩家在干什么 + 长期目标 + 没做完的任务"叫醒它,不许反问 Master。

  • 任务台账 + 步骤表 + 执行器:挖 4 个以上自动开工,tasks.json 掉线重连都还在; mc_task {action:"run"} 一次推进一步,四道闸门防跑飞。

  • 记忆:四分类(world/lessons/tasks/RULES)带来源·成功失败次数·适用版本;按服务器建子文件夹,可每会话独立。

  • 统一动作调度器:资源冲突(interaction/movement)记账;enforce 模式下撞车先排队等(waitMs,默认 3 秒), 够高就叫停低的(同级不抢、preemptible:false 不抢、取消边界在 cleanup/unknown 不抢); 父子动作复用 actionId,而且子步骤忘了说自己是谁也不会把自己锁死(AsyncLocalStorage 兜底)。

看得见

  • 第一人称视角 mc_observe:从眼睛渲染截图 + 三段式感知(我看到 / 程序检测到 / 不确定), 两套眼睛对不上时点名,以程序射线为准;门禁默认关,AI 自己决定何时看。

  • 地形图:地表图 + 地下自动切洞穴层;format:"image" 出俯视图。

生活技能

  • 合成/熔炼/存取箱子三个扩展走真服务端界面(不是作弊给物):自动补齐中间产物、 写产物名反查原料、自己挑燃料、附近没工作台/熔炉/箱子就自己放一个。

  • 装备栏、附魔台、铁砧、锻造台。

  • 图像工具链(mc_kit_image / mc_kit_express / mc_kit_memory)与 mc_admin_config。

工程

  • src\core.mjs 从 4896 行拆到 999 行,62 个模块单向依赖(拆的规矩见下一节)。

  • 日志:本地时间、级别、去重、轮转(上游是 UTC + 从不轮转 + 59.7% 启动噪声)。

  • tools\check-all.mjs 49 个套件离线自检 + 顾问型断言扫描器。

拆分模块的规矩

src\core.mjs 是分 43 刀拆出来的(4896 → 999 行)。以后继续拆照这 46 条来 —— 每条都是踩出来的,括号里的"第 N 刀"是它第一次被踩到的位置(对应提交见 CHANGELOG.md)。

  1. core.mjs 里保留同名导出:export { lossless, redactPath } from './format.mjs' —— 外部一行都不用改。

  2. 一次只搬一个模块,搬完立刻 node --check + 真的 import 一遍,确认导出还在、行为没变。

  3. 移动和挖掘最后再拆:#walkPathfinder() / dig() 共享 _moveGen、_ctrlLease、11 秒预算和一堆事件监听。

  4. 静态断言也要跟着搬:套件里有"core.mjs 里有没有 const SOFT_ROOF = "这种按文件找代码的断言,代码一搬它就红。

  5. 先 grep 谁在 import 这个名字:被别的模块 import 就必须 re-export,否则报的是加载期的 does not provide an export named '…'(整个扩展起不来),不是运行期。

  6. grep 要 grep 整个仓库(tools\ 与 *.md 都算),不只看 import —— 有一批断言是"按名字找代码"的。

  7. 搬家脚本别手写"要加 export 的白名单":node --check 只查语法不查导出,漏一个要等真 import 才报。

  8. 新模块不许反向 import 回 core.mjs(循环导入)⇒ 改成 core 注入 setXxx()。

  9. "模块加载时读一次"的环境变量搬进单例模块后必须改成现读:core.mjs 每个连接会重新 import 一份, src\log.mjs 整个进程只加载一次 ⇒ MC_LOG_MAX_BYTES/MC_LOG_KEEP 要写成 logLimits() 现读。 搬之前先问一句"这个模块会被加载几次"。

  10. 搬"私有方法"比搬"纯函数"多两件事:签名要多一个 b(this.#heldText(b) → heldText(b),所有调用点都得改); 公开方法要在 core 留薄包装。"逐字等价"验证法失效(签名变了),改用行为对照。

  11. "grep 整个仓库"要连 tools\check-*.mjs 一起 grep:47 个套件里大量是"读源码文本 → 正则匹配" 的静态断言,代码一搬就在原地红着没人看。改完跑 node tools\check-all.mjs --fast 全绿才算搬完, 别只看你改的那个套件。

  12. 能并进已有模块就别新开(依赖方向要天然是顺的)。

  13. 搬常量/工具函数时先分清"哪部分是纯的、哪部分绑在实例上":纯的进模块,读 this.xxx 的留类里。 判据:"公开面不变"= "以前 export 过",不是"有没有人用"。

  14. 一个名字在两个模块里表示两件事时顺手改名(#reachOf → blockReachOf,与 mc_craft.mjs 那个"材料够不够做"的 reachOf 区分); 改名时用脚本统计命中数,命中 0 处就抛错。

  15. 搬"公开方法"要给类留同名薄包装(不是 re-export):heightmap (opts = {}) { return heightmapOf(this.requireBot(), opts) }。 验证用 A/B 对照(把 git HEAD 的 core.mjs 临时拷回 src\,跑完即删)。

  16. 薄包装的参数形状要跟原来一模一样:scan ({…} = {}) 收对象、entities (radius = 24, {…} = {}) 是位置参数 + 可选对象 —— 调用方是照签名调的,签名一改就是静默改行为。

  17. 实例状态不属于 bot:多给模块一个 ctx 参数(statusOf(b, ctx))。 判据:凡是被搬走的代码里出现 this.xxx,就问这个 xxx 是 bot 的还是插件的。

  18. A/B 的假数据必须复刻真实数据的形状,否则"等价"可能只是"两边都走了同一条死路" (dig-speed.mjs 第一版冒烟 82/0 全绿,但假 registry 把工具表写成了 {900:2, 901:2},真实数据只有木工具)。

  19. 搬"实例状态 + 收尾动作"混在一起的方法:模块只做动作,状态全走 ctx (cancelCollect(b, ctx),ctx = { force, budgetMs, bumpGen, stopPathing, releaseControls, log })。 薄包装里保留字面 this._moveGen++(check-call-budget.mjs 按字面数"抢占时把代号往前推 ≥2 处"); 收尾类动作一律 try { … } catch {} 包住每一步。

  20. 有可变实例状态的层,第一参数直接收 self(不是 ctx)。判据:"要读"进 ctx,"要改"就直接收 self。

  21. 每搬一个模块都要跑 node tools\scan-moved-assertions.mjs(顾问型,不判红)。 它做两遍:① 按导出名找"提到这些名字且正则带实现细节"的断言;② 把断言的正则拿去它断言的文件里试, 在 core 里找不到、却能在别的 src/*.mjs 找到的就报。

  22. 一个 ok() 里塞两条正则时,扫描器可能只看到第一条:要抠掉整行所有 .test(<ident>) 再判, 并把 = 、.方法(、??、|| 也算实现细节。教训:两条正则都要单独看一眼。

  23. lines.splice(n, …) 是 0-based,锚点自检用的是 1-based;split('\n') 后每行带 \r,比对要 .replace(/\r$/, '')。 node --check 不会报这类错(多一行孤立 /** 语法完全合法)。

  24. 搬"要发包的动作"时模块只做动作,实例/库相关的全走 ctx(giveItem/clearInventory/tossItem 要 ctx = { t, makeItem },模块不 import 任何游戏库)。

  25. 搬"服务端不回执的动作":把 await 留在模块里,别把 promise 交回实例 (容器看 currentWindow、门看 getProperties() 翻没翻)。

  26. 混合行尾的文件不能按行号切(既有 CRLF 又有孤立 LF ⇒ 从第一个孤立 LF 起整体错开)。 自检:(raw.match(/\r\n/g)||[]).length 与 raw.split('\n').length - 1 对不上就是混合行尾。 手术脚本要么按内容定位(raw.indexOf(锚点) + 括号配平),要么先统一了再切、搬完换回去。

  27. 要回调一堆实例方法的层,第一参数收 self。判据:"只读几项"用 ctx,"要改"或"要回调"用 self。

  28. 模块够不着私有方法,就把它塞进 ctx:cancelCollect: (bb, o) => this.#cancelCollect(bb, o) (私有名在模块外面不能用字符串取)。

  29. 搬方法时 JSDoc 也要一起搬(脚本删到哪儿,就把那儿的 JSDoc 写进新模块,或在 core 原位留一行指针); 分节注释(/* ─── 世界观察器 ─── */)同样跟着走。丢了注释不影响任何自动检查。

  30. 薄包装和 import 撞名时 import 取别名:import { startObserver as startObserverImpl, … } (类方法名不进词法作用域,能跑但读的人会以为是递归)。

  31. 搬进"已经存在的模块"时,那个模块头注释里的"N 件事"也要改 —— 头注释是这个项目"这个模块负责什么"的索引。

  32. 搬进已有模块时把新成员要用的兄弟依赖补进 import,补之前先确认不会成环。

  33. 新写的头注释里不许出现 */:写 $DSH_HOME/profiles/*/vendor 这种 glob 会提前关掉块注释, node --check 报的行号在注释中间很迷惑。用 <profile> 占位或反斜杠。

  34. A/B 两边抛同一个错也算"通过" ⇒ 每条 A/B 都要配"真的走通了吗"的断言(输出非 null、!ea && !eb)。

  35. 手术脚本的"不许残留 this."自检要先把注释剥掉: const bare = s.replace(/\/\*[\s\S]*?\*\//g, '').replace(/^[ \t]*\/\/.*$/gm, '') —— 注释里的 this. 是踩坑说明,代码里的才是残留。

  36. 改调用点的正则要连"尾参"一起改(await this.connect({...}) → await connect(self, {...}, ctx)); 自检里加一条"新调用点的参数个数对得上"。

  37. A/B 两边不能共用一份"状态会变"的假件(假 bot 的 entity.isInWater 是时变的); 假件里带时间 / 次数 / 状态的字段,A/B 就不能共享 —— 新旧各造一份,B 要在 A 跑完之后再造。

  38. 按签名找函数体时 indexOf('{') 会先撞上参数里的解构 { (async #walkPathfinder (target, { arrive = 1.6, … } = {}) {)⇒ 先取第一个换行、再 lastIndexOf('{', nl)。

  39. 手术脚本要"先全查、后写盘":所有锚点(要删的、要插的、import 的)先验完,最后才写文件。 import 锚点别按"单行 import"去猜(movement 的 import 是多行的,锚点只取 } from './movement.mjs')。

  40. A/B 全绿不等于那条路走到了:写完用例回头问一句"这条分支的前置条件是什么",照着摆世界。

  41. A/B 全绿 + 调用序列一致,也可能是同一份用例把两边带到同一条错路上 (dig 的 40 条断言全绿,四条出口全走的是"够不着"那条)⇒ 每条用例配一条"这条路真的走到了"的断言, 并把输出打出来看一眼。

  42. 带定时器的状态层:状态字段留在实例,读写逻辑搬进模块(holdControl/#releaseControls 只搬读写, 租约表 _ctrlLease 留 core.mjs)。

  43. "抠旧实现"要连它依赖的模块级 import 一起搬:判据是"方法体 + 它引用的所有模块级标识符 (函数、常量、类)",不只是 import 名。

  44. 模块里出现 mineflayer / prismarine 字样(哪怕只是 import)就该停下来看看。

  45. 自写冒烟的 t 兜底写成 ctx.t ?? ((p) => p),不必每次造一个真的闸门。

  46. 拆分收尾用 check-module-load.mjs 体检(30/0:拆出去的模块全都 import 得进来)。

文档索引

文档

给谁看

讲什么

本文件

开发者

接到 MCP 客户端、跑自检、配置速查、工具速查、唤醒机制、迁移、架构速览、拆分模块的规矩

插件说明.md

模型 / 维护者

工具与机制细节:34 个工具的设计取舍、提示词怎么进去、唤醒链路、账户与凭据、安全边界、已知限制、English

踩坑档案.md

开发者

119 个真 bug 的现象 → 根因 → 证据 → 修法 → 回归("为什么现在这么写")

PATHFINDER.md

开发者

寻路/防卡住专题:为什么用现成的、装在哪、打过的补丁、上游复核、已知缺口

CHANGELOG.md

开发者

按日期的改动流水(含拆 core.mjs 的 43 刀 + 分叉成 MCP server 那一版)

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    F
    maintenance
    An MCP server that lets Claude control a Minecraft bot with 40+ actions including movement, combat, crafting, and inventory management. Built on Mineflayer, it supports Microsoft authentication, pathfinding, and auto-reconnect.
    3
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    A production-ready MCP server that gives an LLM agent standalone-equivalent control over a Mineflayer Minecraft bot — movement, mining, crafting, inventory, combat, containers, chat, and much more — exposed as 110 strongly-typed tools across 23 groups, with full bot lifecycle management and dual (poll + push) event streaming.
    97 npm
    4
    MIT