utmt-mcp
v0.3.2
Published
MCP server for reading GameMaker data files via UndertaleModCli
Maintainers
Readme
utmt-mcp
通过 UndertaleModCli 读取和导出 GameMaker 数据文件(.win、.ios、.droid、.unx)的 MCP 服务器。
专为 LLM 工具调用而设计 —— 所有输出均为干净的 JSON,实体按名称查找。
跨平台 —— 支持 Windows、macOS、Linux。UndertaleModCli 为这三个平台都提供了官方构建。
快速开始
npx utmt-mcp或全局安装:
npm install -g utmt-mcp
utmt-mcp环境要求
- Node.js 18+
- .NET 运行时(UndertaleModCli 需要)
- UndertaleModCli —— 加入 PATH,或通过环境变量 / 工具参数指定
安装 UndertaleModCli
从 GitHub Releases 下载对应平台的构建包,解压后任选一种方式:
- 把可执行文件加入系统 PATH,或
- 将
UTMT_CLI_PATH环境变量设置为可执行文件的完整路径
在 Windows 上 CLI 二进制文件带 Windows 可执行文件后缀;macOS 和 Linux 上没有后缀。其余用法完全一致。
配置
环境变量
| 变量 | 说明 |
|----------|-------------|
| UTMT_CLI_PATH | UndertaleModCli 可执行文件的完整路径 |
MCP 客户端配置
在 MCP 客户端中(如 Claude Desktop、Cursor、Claude Code 等):
{
"mcpServers": {
"utmt": {
"command": "npx",
"args": ["-y", "utmt-mcp"],
"env": {
"UTMT_CLI_PATH": "/path/to/UndertaleModCli"
}
}
}
}或者在每次工具调用时显式传入 CLI 路径(无需环境变量):
{
"mcpServers": {
"utmt": {
"command": "npx",
"args": ["-y", "utmt-mcp"]
}
}
}工具
所有示例均为对真实数据文件的实际调用结果(长输出按需截断)。
utmt-info
获取 GameMaker 数据文件的概览信息。返回 JSON,包含项目名称、GM 版本、isYYC 标志和各类资源数量。
参数:
dataFilePath(必填)— 数据文件路径cliPath(可选)— UndertaleModCli 路径
输入:
{"dataFilePath": "data.win"}返回:
{"name":"VIVIDSTASIS","displayName":"vivid/stasis","gmVersion":"2024.14.1.0","isGMS2":true,"isYYC":false,"bytecodeVersion":17,"windowWidth":320,"windowHeight":180,"fps":60,"counts":{"sprites":2141,"sounds":664,"scripts":19375,"code":22033,"rooms":232,"gameObjects":814,"fonts":30,"backgrounds":5,"paths":0,"shaders":44,"strings":51932,"variables":16316,"functions":17835,"timelines":1,"embeddedTextures":100,"embeddedAudio":627,"texturePageItems":6782,"extensions":7,"sequences":0,"particleSystems":0}}utmt-list-entities
列出指定类型的实体。返回 JSON 数组,包含索引、名称和简要属性。
参数:
dataFilePath(必填)entityType(必填)— 可选值:sprites、sounds、scripts、code、rooms、gameobjects、fonts、backgrounds、paths、shaders、strings、timelines、embeddedtextures、embeddedaudio、extensions、sequences、particlesystemsfilter(可选)— 不区分大小写的名称过滤offset(可选)— 分页偏移(默认:0)limit(可选)— 最大返回数,1-500(默认:50)cliPath(可选)
输入:
{"dataFilePath": "data.win", "entityType": "sprites", "limit": 3}返回:
[{"index":4,"name":"_filter_underwater_noise_sprite","width":256,"height":256,"frames":1,"origin":[0,0]},{"index":7,"name":"_filter_heathaze_noise_sprite","width":64,"height":64,"frames":1,"origin":[0,0]},{"index":987,"name":"sp_default_actor_sprite","width":16,"height":16,"frames":1,"origin":[0,0]}]utmt-get-entity
获取单个实体的详细信息。返回 JSON,包含所有属性。
参数:
dataFilePath(必填)entityType(必填)— 可选值:sprite、sound、script、code、room、gameobject、font、string、generalinfoname(必填)— 实体名称(string类型用数字索引;generalinfo忽略此参数)cliPath(可选)
输入:
{"dataFilePath": "data.win", "entityType": "sprite", "name": "_filter_large_blur_noise"}返回:
{"name":"_filter_large_blur_noise","width":32,"height":32,"originX":0,"originY":0,"frames":1,"transparent":false,"smooth":false,"preload":false,"bboxMode":0,"sepMasks":0,"marginLeft":0,"marginRight":31,"marginTop":0,"marginBottom":31,"collisionMasks":0,"spriteType":0,"playbackSpeed":30}utmt-decompile-code
将代码条目反编译回 GML 源码。YYC 编译的游戏不可用。
参数:
dataFilePath(必填)codeName(必填)— 代码条目名称(如gml_Script_myFunction)cliPath(可选)
输入:
{"dataFilePath": "data.win", "codeName": "gml_Script_io_gamemaker_gm_effect_glow_1_0_0__effect_glow_script"}返回(GML 源码,节选):
function gml_Script_io_gamemaker_gm_effect_glow_1_0_0__effect_glow() constructor
{
static shader = _effect_glow_shader;
static u_GlowRadius = shader_get_uniform(shader, "g_GlowRadius");
...
}utmt-export-entity
将单个实体导出到磁盘。
参数:
dataFilePath(必填)entityType(必填)— 可选值:sprite、sound、code、embeddedtexturename(必填)— 实体名称(embeddedtexture用数字索引)outputPath(必填)— 输出文件或目录路径cliPath(可选)
导出格式:
sprite→ 输出目录中的 PNG 帧sound→ .ogg 或 .wav 文件code→ 反编译后的 .gml 文件embeddedtexture→ PNG 文件
输入:
{"dataFilePath": "data.win", "entityType": "sprite", "name": "sp_default_actor_sprite", "outputPath": "C:/out"}返回:
{"success":true,"exportedFrames":1,"outputDir":"C:/out"}utmt-search
跨所有类型按名称模式搜索实体。不区分大小写的子串匹配。
参数:
dataFilePath(必填)query(必填)— 搜索关键字entityTypes(可选)— 限定搜索类型(空 = 全部)cliPath(可选)
输入:
{"dataFilePath": "data.win", "query": "player", "entityTypes": ["gameobjects", "sprites"]}返回(节选):
[{"type":"gameobject","index":6,"name":"o_00_movieplayer"},{"type":"gameobject","index":734,"name":"obj_player_actor"},{"type":"sprite","index":661,"name":"sp_2023res_playerwindow"},"..."]utmt-get-room-assets
获取一个房间内使用的所有资源:精灵(含尺寸及使用它们的对象)、对象(含位置和精灵)、代码条目(来自事件)以及图层汇总。
参数:
dataFilePath(必填)roomName(必填)— 房间名称(如scene_gameplay)cliPath(可选)
输入:
{"dataFilePath": "data.win", "roomName": "betweenspace"}返回:
{"room":"betweenspace","width":320,"height":180,"sprites":[],"objects":[{"name":"o_chromatest","sprite":"","depth":0,"visible":true,"x":0,"y":0,"instanceId":100000}],"codeEntries":[{"obj":"o_chromatest","evt":"create","subtype":0,"code":"gml_Object_o_chromatest_Create_0"},{"obj":"o_chromatest","evt":"draw","subtype":0,"code":"gml_Object_o_chromatest_Draw_0"}],"layers":[{"name":"Instances","type":2,"depth":0,"visible":true,"objectCount":1},{"name":"Background","type":1,"depth":100,"visible":true,"objectCount":0}]}utmt-search-strings
按内容搜索字符串表。字符串没有名字,常规 utmt-search(按名字)搜不到它们。返回匹配的字符串索引和内容(每条截断到 500 字符)。
参数:
dataFilePath(必填)query(必填)— 不区分大小写的内容搜索关键字limit(可选)— 最大返回数(默认:50)cliPath(可选)
输入:
{"dataFilePath": "data.win", "query": "CoroutineThen", "limit": 3}返回:
[{"index":287,"content":"THEN"},{"index":288,"content":"gml_Script___CoroutineThen"},{"index":289,"content":"__CoroutineThen"}]utmt-export-all
批量导出某类型的全部资源到目录。支持:sprites、sounds、embeddedtextures。精灵导出为每个精灵一个 PNG 帧子目录,声音为 ogg/wav 文件,内嵌纹理为编号 PNG。返回导出/失败数量。
参数:
dataFilePath(必填)entityType(必填)— 可选值:sprites、sounds、embeddedtexturesoutputDir(必填)— 导出目标目录cliPath(可选)
输入:
{"dataFilePath": "data.win", "entityType": "sounds", "outputDir": "C:/audio"}返回:
{"type":"sounds","exported":664,"failed":[]}utmt-get-object-code
反编译对象上某个事件的 GML 代码。返回 JSON,包含对象名、事件类型、子类型和反编译代码。子类型是零基索引:alarm 是闹钟编号;step 0=begin/1=normal/2=end;collision 是另一对象索引;其余映射到事件专属按键。单条目事件用 0(如 create)。YYC 编译的游戏不可用。
参数:
dataFilePath(必填)objectName(必填)— 对象名(如o_player)eventType(必填)— 可选值:create、destroy、alarm、step、collision、keyboard、mouse、other、draw、keypress、keyrelease、trigger、cleanup、gesture、precreatesubtype(可选)— 零基事件子类型索引(默认:0)cliPath(可选)
输入:
{"dataFilePath": "data.win", "objectName": "cc", "eventType": "create", "subtype": 0}返回(code 字段为反编译的 GML,节选):
{"objectName":"cc","eventType":"create","subtype":0,"codeName":"gml_Object_cc_Create_0","code":"if (global.op_hide_cursor)\n{\n window_set_cursor(cr_none);\n}\nglobal.gamefps = @@array_get@@([30, 60, 75, 90, 120, 144, 165, 240, 500, 1000], global.op_fpscap);\n...\ninstance_create_depth(0, 0, -1000, obj_judgement_display);\n"}utmt-find-references
查找某个资产的引用。sprite:哪些对象使用它作为精灵、哪些房间包含这些对象的实例;object:哪些房间实例化它、哪些对象继承它;variable/function:哪些代码条目引用了它(扫描字节码指令);string:传入字符串索引,查找推入该字符串的代码条目。返回 JSON。
参数:
dataFilePath(必填)type(必填)—sprite、object、variable、function、stringname(必填)— 资产名(string类型传索引)cliPath(可选)
输入:
{"dataFilePath": "data.win", "type": "variable", "name": "x"}返回(节选):
{"codeReferences":[{"code":"gml_GlobalScript_create_chapter2_event_nodes","references":52},{"code":"gml_GlobalScript_create_chapter2end_event_nodes","references":16},{"code":"gml_GlobalScript_create_chapter3_event_nodes","references":57},...]}utmt-find-unknown-functions
列出未被任何脚本、代码条目、内置函数或扩展函数解析的函数条目(缺失脚本、外部 DLL 函数、YYC 隐藏函数)。反混淆和完整性审计用。
参数:
dataFilePath(必填)cliPath(可选)
输入:
{"dataFilePath": "data.win"}返回:
[]utmt-extract-embedded-data
从 YYC 编译的可执行文件或内存 dump 中提取内嵌的 GameMaker 数据文件。扫描输入二进制中的 FORM+GEN8 头,把恢复的数据文件(data.win)写入输出路径。纯字节扫描,无需 UndertaleModCli。
参数:
dataFilePath(必填)— YYC 可执行文件或 dumpoutputPath(必填)— 输出数据文件路径
输入:
{"dataFilePath": "game.exe", "outputPath": "extracted.win"}返回:
{"success":true,"file":"extracted.win","offset":125952,"size":25165824,"candidates":1}utmt-dead-resource-analysis
分析哪些字符串、变量、函数未被使用(没有被任何资产名或代码指令引用)。返回 JSON,含各分类总数和样本。内置变量/函数已排除。字符串检查还能发现未本地化的残留文本。
参数:
dataFilePath(必填)limit(可选)— 每类样本上限(默认:50)cliPath(可选)
输入:
{"dataFilePath": "data.win", "limit": 3}返回(节选):
{"unusedStrings":{"total":1552,"sample":[{"index":41466,"content":"@@SleepMargin"},{"index":41467,"content":"@@DrawColour"},{"index":41468,"content":"4294967295"}]},"unusedVariables":{"total":4844,"sample":[{"index":0,"name":"prototype"},{"index":1,"name":"@@array@@"},{"index":2,"name":"arguments"}]},"unusedFunctions":{"total":0,"sample":[]}}utmt-find-replace
在全部代码条目中查找并替换文本(或正则),然后重新编译。写操作:结果保存到新的输出文件,原始文件绝不被修改。仅适用于 VM 编译的游戏(非 YYC)。
参数:
dataFilePath(必填)— 输入数据文件outputFilePath(必填)— 输出文件(必须与输入不同)find(必填)— 查找文本(或正则)replace(必填)— 替换文本caseSensitive(可选)— 区分大小写(默认:false)isRegex(可选)— 按正则处理(默认:false)cliPath(可选)
输入:
{"dataFilePath": "data.win", "outputFilePath": "data-mod.win", "find": "mod_scrollspeed", "replace": "mod_speed"}返回:
{"success":true,"searched":4842,"message":"Find/replace applied; changes are saved to the -o output file"}utmt-export-strings-json
将字符串表全部导出为 JSON 文件。用于本地化、词频分析、文本资产审计。
参数:
dataFilePath(必填)outputPath(必填)— 输出 JSON 路径cliPath(可选)
输入:
{"dataFilePath": "data.win", "outputPath": "strings.json"}返回:
{"success":true,"file":"strings.json","count":51933}支持的数据文件格式
data.win— Windowsgame.ios— iOSgame.droid— Androidgame.unx— Linux/macOS
开发
git clone <repo>
cd utmt-mcp
pnpm install
pnpm run build测试
npm test单元测试零依赖(Node 内置 node --test),校验所有脚本生成器的 null 安全、转义和结构。集成测试会真实调用 UndertaleModCli:对一个生成的空白数据文件跑全部工具脚本(覆盖 GM 1.x 空集合路径);如需对真实数据文件再跑一遍,设置 UTMT_TEST_DATA_WIN 环境变量指向 data.win(未设置则跳过真实文件用例)。集成测试需要 UTMT_CLI_PATH(或 PATH 中能找到 UndertaleModCli);CLI 缺失时对应用例自动跳过。
许可证
MIT
