balatro-codex-mcp
balatro-codex-mcp
balatro-codex-mcp 让 Codex 桌面端通过本地 STDIO MCP 工具控制 Steam 版
《Balatro / 小丑牌》。Python Server 只暴露经过约束的正常游戏动作,不向 Codex
提供任意 JSON-RPC、调试、作弊、存档读写、截图或返回菜单能力。
项目基于:
BalatroBot v1.5.2, commit
9052d76f14723293f6c6b2cecaa791a5c4ae68f3核对过的 BalatroBot main commit
e7c6db8a9ad88318f6e4128eefd6e61aafc94885
2026-09-06 升级验证
MCP SDK 和
mcp-types已升级并锁定为 2.1.1,保留官方MCPServer与 STDIO 入口。macOS 已验证 Steamodded 26.829.0 与 BalatroBot v1.5.2、Lovely 0.9.0 的启动及只读连接兼容性。
验证涵盖
health、rpc.discover、gamestate和 MCPtools/list;升级验收未执行 游戏动作,完整对局及无尽模式仍需实际游玩验证。启动脚本将
localhost、127.0.0.1、::1加入启动进程的NO_PROXY/no_proxy, 保留已有绕过规则,避免 macOS 系统代理向健康检查返回 HTML 而触发JSONDecodeError。 此设置不修改系统代理或 BalatroBot 上游源码。升级 Steamodded 前应将旧目录备份到
Mods以外的位置,避免同时加载两份 Mod。 本机升级备份位于~/Library/Application Support/balatro-codex-mcp/backups/。BalatroBot 保留稳定版。无尽模式弹窗修复 PR #200 目前只在
dev;该分支还要求专用配置档并自动全解锁,本项目不默认切换。
架构
Codex 桌面端
↓ STDIO MCP(唯一游戏控制入口)
本项目的 Python MCP Server
↓ HTTP JSON-RPC 2.0(默认 http://127.0.0.1:12346)
BalatroBot
↓ 游戏内 Mod API
Steam 版 BalatroMCP Server 和 BalatroBot 使用独立生命周期。Codex 启动 MCP Server 时,Server
会尽力读取 rpc.discover,但绝不会启动、关闭、重置游戏或加载存档;即使
BalatroBot 离线,MCP 初始化和 tools/list 仍会成功,balatro_health 会返回
可执行的修复命令。
为什么 Codex 桌面端使用 MCP
MCP 把参数类型、工具说明、只读/写入提示和 server-level instructions 一起交给
Codex。模型调用 balatro_play_cards(cards=[...], expected_state_token="..."),
而不是通过 Shell 拼接 BalatroBot JSON。这样可以集中执行:
0-based 索引和完整排列校验
state_token乐观并发保护写锁内二次状态校验
写操作禁止自动重试
工具白名单和危险接口隔离
stdout 仅承载 MCP 协议,诊断日志只写 stderr
Balatro、Lovely、Steamodded 与 BalatroBot
Balatro 是 Steam 游戏本体。
Lovely Injector 把 Mod 加载能力注入 Balatro 的 LÖVE 运行时。
Steamodded 是 Balatro 的 Mod loader/API。
BalatroBot 是运行在游戏内的 Mod,并在本机提供 HTTP JSON-RPC 2.0。
本项目 不重写或修改 BalatroBot,只把它的正常游戏 API 收窄为 Codex MCP 工具。
安装 Mod 时遵循 BalatroBot 安装文档、 Lovely 文档和 Steamodded 文档。本项目脚本不会复制、 删除或修改游戏、Mod 和存档文件。
前置条件
macOS 与 Steam 版 Balatro 1.0.1+
Lovely 0.8.0+
Steamodded 1.0.0-beta-1221a+
BalatroBot Mod v1.5.2
Python 3.13+(项目
.python-version固定 3.13 系列)Codex 桌面端;建议同时有 Codex CLI
BalatroBot 当前常见路径:
游戏:
~/Library/Application Support/Steam/steamapps/common/Balatro/Balatro.app
Lovely:
~/Library/Application Support/Steam/steamapps/common/Balatro/liblovely.dylib
Mods:
~/Library/Application Support/Balatro/Mods
BalatroBot:
~/Library/Application Support/Balatro/Mods/balatrobotQuick Start
在项目根目录:
uv sync
./scripts/doctor_macos.sh
./scripts/start_balatro_macos.sh --fast启动脚本在前台运行 BalatroBot v1.5.2,默认显示游戏窗口。不要关闭这个终端;
Ctrl+C 会直接转发给 BalatroBot。--fast 可省略,也可以追加任何
balatrobot serve 参数,例如 --no-shaders。脚本不会后台运行、不会杀死占用端口
的进程,也不会重置游戏或存档。
打开另一个终端:
./scripts/verify_connection.sh
./scripts/configure_codex_mcp.sh然后:
重启 Codex 桌面端。
打开本项目。
在对话框输入
/mcp。确认
balatro已连接。输入下文“推荐游戏提示词”。
如果 doctor_macos.sh 报告 Mod 或游戏缺失,先按上游文档补齐;该脚本是只读检查,
不会自动安装或修改系统。
安装 Python 项目
uv sync该命令创建项目私有环境并生成/使用 uv.lock。完成后,真正交给 Codex 的入口是:
<仓库绝对路径>/.venv/bin/balatro-codex-mcp可以直接验证入口存在:
test -x .venv/bin/balatro-codex-mcp不用 uv run 启动 Codex MCP 的原因是:macOS GUI 进程未必继承 Homebrew 或用户
Shell 的 PATH。.venv/bin 中的绝对控制台入口已经包含正确的 Python 环境,
Codex 无需找到 uv、python3 或 Homebrew。
macOS 检查与启动 BalatroBot
只读检查:
./scripts/doctor_macos.sh它检查 macOS、uv、Python 3.13+、Steam Balatro、Lovely、Steamodded、BalatroBot
Mod、12346 端口、BalatroBot health、Codex CLI、项目 .venv 和绝对 MCP 入口。
前台启动:
./scripts/start_balatro_macos.sh快速模式:
./scripts/start_balatro_macos.sh --fast额外参数原样传给上游:
./scripts/start_balatro_macos.sh --fast --no-shaders --fps-cap 60脚本使用上游推荐的 uvx balatrobot serve 路径并明确固定 v1.5.2。在 macOS,
BalatroBot CLI 直接执行:
~/Library/Application Support/Steam/steamapps/common/Balatro/
Balatro.app/Contents/MacOS/love同时通过 DYLD_INSERT_LIBRARIES 加载 liblovely.dylib。这是 BalatroBot 当前
macOS launcher 的实现;不是鼠标、OCR、截图或 UI 自动化。
只读验证 health
BalatroBot 启动后,在第二个终端运行:
./scripts/verify_connection.sh该脚本严格只调用:
healthrpc.discovergamestate
它不会调用 start、play、discard、buy、sell、reroll、pack、
next_round 或任何其他写操作。
配置 Codex 桌面端 MCP
自动配置:
./scripts/configure_codex_mcp.sh脚本先确认 .venv 和绝对入口存在,然后执行等价于:
codex mcp add balatro \
--env BALATROBOT_URL=http://127.0.0.1:12346 \
-- /ABSOLUTE/PATH/TO/balatro-codex-mcp/.venv/bin/balatro-codex-mcp如果 balatro 已存在,脚本会停止并要求先检查/移除旧配置,不会静默覆盖。成功后
会运行:
codex mcp list也可以在 Codex 桌面端手动添加:
Settings → MCP servers → Add server → STDIO
Name: balatro
Command: <仓库绝对路径>/.venv/bin/balatro-codex-mcp
Environment: BALATROBOT_URL=http://127.0.0.1:12346保存后必须重启 Codex 桌面端。重启后在对话框输入:
/mcp确认 balatro 状态为 connected。可参考
.codex/config.toml.example;示例只含占位路径,
不包含开发机用户名。
环境变量
变量 | 默认值 | 说明 |
|
| BalatroBot JSON-RPC 根地址 |
|
| 单次 HTTP 超时秒数 |
|
| 仅显式设为 |
|
| 只读连接级重试次数,范围 0–3 |
默认只接受 localhost、127.0.0.1 和 ::1。URL 不允许凭据、额外路径、query
或 fragment。非回环模式会扩大信任边界,不建议用于普通本地游戏。
MCP 工具
所有工具都有 balatro_ 前缀。
只读工具
工具 | 用途 |
| MCP/BalatroBot 状态、health、版本、当前阶段、discover 结果、缺失能力和 macOS 修复命令 |
| 安全上游方法的精简 OpenRPC 参数与 required states;可强制刷新缓存 |
| 读取紧凑 decision 或保留全部未知字段的 full state |
| 当前允许/不允许动作、原因、参数、有效索引范围与 token |
写工具
每个写工具都强制要求 expected_state_token,不提供可选默认值。
工具 | 上游方法 | 主要约束 |
|
| 仅 |
|
| 仅 |
|
| Boss Blind 禁止跳过 |
|
| 1–5 张、唯一、有效手牌索引 |
|
| 检查剩余弃牌和高亮上限 |
|
| 仅 |
|
|
|
|
| 仅 |
|
|
|
|
| 不猜测目标数量 |
|
| 转为 |
|
| 转为 |
|
|
|
|
| 仅 |
所有写操作统一执行:
读取最新
gamestate。计算并比较 token。
校验阶段、参数、资源和索引。
获取唯一写锁。
在锁内再次读取状态并校验 token。
向 BalatroBot 发送恰好一次写 RPC。
再次读取状态;如果该只读请求失败,使用写响应中的 gamestate 作为明确降级。
返回上游结果、旧/新 token、完整新 decision state、warnings,并写动作日志。
decision state、state_token 与 0-based 索引
balatro_get_state(view="decision") 保留当前阶段、seed、deck、stake、ante、round、
Blind 目标分数/奖励(上游提供时)/特殊效果、分数、钱、bankrupt_at(上游提供时)、
剩余手数/弃牌数、胜负、手牌、Joker、消耗牌、voucher、商店、补充包、reroll
费用、槽位上限、可用动作、warnings 和 state_token。
每张卡或物品都保留原始 BalatroBot 字段,并额外提供:
当前
indexbalatrobot_id、key、label/namedescriptionrank、suitenhancement、edition、seal、debuffbuy_cost、sell_value
所有索引均为 0-based。任何状态改变后,手牌、商店、Joker、消耗牌和 Pack 的 旧索引立即失效。
state_token 使用 action-relevant 状态的规范化 JSON、稳定 key 排序和 SHA-256
生成。同一状态重复读取会得到相同 token;阶段、主要资源、手牌顺序、商店、
Joker、消耗牌或 Pack 改变都会换 token。
如果传入旧 token,工具返回 STALE_STATE、实际 token 和最新 decision state,
且不会执行上游写操作。正确处理方式是重新决策,不是盲目重试原动作。
开始或继续一局
推荐把下面整段交给 Codex:
请用 balatro MCP 玩小丑牌。先调用 balatro_health,再读取
balatro_get_state(view="decision")。如果已有一局,继续当前局,不要返回菜单,
不要开始新局覆盖它;如果状态是 MENU,则用 RED deck、WHITE stake 开始一局。
每次动作前都重新读取最新 decision state,只使用 available_actions 中允许的动作,
所有索引按 0-based,并把最新 state_token 作为 expected_state_token。每个写操作后
检查 new_state。不要盲目重试失败工具,不要跳过 Boss Blind;商店操作前检查钱和
槽位。进入 GAME_OVER 后停止操作,汇总 ante、round、胜负和关键 Joker。如果只想继续、不允许开始新局,把其中 MENU 分支改为“若为 MENU,只报告当前没有
进行中的局,不要调用 balatro_start_run”。
常见错误
错误/现象 | 含义与处理 |
| 在独立终端运行 |
启动时 | 若本机 HTTP 请求被系统代理返回 HTML,使用已加入本机地址 |
| 确认游戏未卡死;动作结果未知时先读状态,绝不能重复写操作 |
| 使用错误附带的 |
| 当前界面不允许该动作;读 |
| 状态已变化或索引错误;重新读取 decision state |
| 卡牌索引或排列有重复 |
|
|
| 先卖出、选择其他物品或离开商店 |
| 必须选择并打 Boss Blind |
12346 被占用 | 启动脚本会报出监听进程但不会杀进程;确认是否已有 BalatroBot |
|
|
| 完整重启 Codex 桌面端,并确认配置使用 |
更新 BalatroBot 后验证接口
阅读新 release 的
pyproject.toml、src/lua/utils/openrpc.json、src/lua/utils/gamestate.lua和src/lua/endpoints/*.lua。更新
scripts/start_balatro_macos.sh中的固定版本。启动新版本后运行只读验证:
./scripts/verify_connection.sh在 Codex 中调用:
balatro_capabilities(refresh=true)运行完整项目验收:
uv sync uv run ruff check . uv run ruff format --check . uv run ty check uv run pytest
如果上游方法参数或状态发生变化,更新兼容表、action guard、工具说明、README 和
tools/list 测试后再允许游戏写操作。
当前上游差异与已知限制
实施时核对了 v1.5.2 和 main,而不是依赖旧教程。发现:
BalatroBot package/release 为 v1.5.2,但 v1.5.2 内嵌 OpenRPC
info.version仍为1.5.1;main 的相同文件写为1.5.2。health 中的版本因此 明确标注来源为rpc.discover.info.version。当前
rpc.discover没有发布 endpoint 的requires_state。项目以 v1.5.2src/lua/endpoints/*.lua的实际约束补齐,并在 capabilities 中标注来源。OpenRPC 的
Stateenum 没列出SMODS_BOOSTER_OPENED,但实际pack.lua明确要求该状态;项目按运行中的实际 endpoint 名称处理。 同时,v1.5.1/v1.5.2 的rpc.discover漏列了实际已注册的packendpoint; 项目仅对这两个已核实版本使用 endpoint 兼容表,并在 capabilities 的compatible_openrpc_omissions中明确报告,不把它误报为缺失能力。v1.5.2 gamestate 没有显式暴露
bankrupt_at或 Blind 奖励。decision state 在上游缺失时返回null,不伪造数据;购买的最终合法性由 BalatroBot 决定。消耗牌目标数量不是当前 OpenRPC 参数 schema 的一部分。项目只校验类型、唯一性 和索引范围,具体数量遵从描述和 BalatroBot 错误。
view="full"保留上游未知字段;decision 是面向决策的非破坏性字段重组,但不 承诺包含与动作无关的未来顶层字段。这是控制接口,不负责安装 Mod,不使用 OCR、截图识别或鼠标坐标,也不在开发 验收中自动开始真实游戏。
日志
动作日志:
logs/actions.jsonl轮转:单文件约 2 MB,保留 3 个备份
BalatroBot 自身日志:上游默认
logs/<timestamp>/<port>.logMCP 普通日志:stderr
MCP stdout:只含协议数据
动作日志只记录动作名、上游方法、参数形状/索引摘要、结果与 token;不会写环境变量、 完整状态或 seed 值。
移除 Codex MCP 配置
./scripts/remove_codex_mcp.sh脚本会先显示即将执行的 codex mcp remove balatro,并且只移除名为 balatro 的
Codex MCP 配置。它不会删除项目、虚拟环境、BalatroBot Mod 或游戏存档。
安全边界
本项目绝不注册或转发以下 BalatroBot 能力:
addsetloadsavescreenshotmenu
同样不存在 execute_raw、call_method、任意 JSON-RPC、debug、eval、改钱、
改分、生成 Joker、跳 Ante、强制胜利或加载 checkpoint 工具。这些名称不会出现在
tools/list。
其他边界:
MCP 是 Codex 唯一的游戏控制入口。
写 RPC 自动重试次数永远为 0。
所有写操作共用一个
asyncio.Lock。只读连接失败可以有限重试。
HTTP 客户端是单个可复用的
httpx.AsyncClient。进程退出时关闭 HTTP client 和动作日志,不遗留阻止退出的后台任务。
不修改 BalatroBot 上游源码。
不修改 Balatro 存档。
返回主菜单只能由用户直接在游戏界面操作。
GAME_OVER后 instructions 要求停止动作并总结。
开发与测试
默认测试全部使用 httpx.MockTransport,不需要 Balatro 或 BalatroBot:
uv sync
uv run ruff check .
uv run ruff format --check .
uv run ty check
uv run pytest测试覆盖连接拒绝、超时、HTTP/JSON/JSON-RPC 错误、ID 不匹配、discover 缓存、
unknown fields、decision view、稳定 token、stale 拒绝、索引与排列、参数转换、
写操作零重试、并发写串行化、危险工具缺失,以及 BalatroBot 离线时的真实 STDIO
MCP 初始化和 tools/list。