Skip to main content
Glama

utmt-mcp

中文 · English

通过 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(必填)— 可选值:spritessoundsscriptscoderoomsgameobjectsfontsbackgroundspathsshadersstringstimelinesembeddedtexturesembeddedaudioextensionssequencesparticlesystems

  • filter(可选)— 不区分大小写的名称过滤

  • 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(必填)— 可选值:spritesoundscriptcoderoomgameobjectfontstringgeneralinfo

  • name(必填)— 实体名称(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(必填)— 可选值:spritesoundcodeembeddedtexture

  • name(必填)— 实体名称(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"}

跨所有类型按名称模式搜索实体。不区分大小写的子串匹配。

参数:

  • 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

批量导出某类型的全部资源到目录。支持:spritessoundsembeddedtextures。精灵导出为每个精灵一个 PNG 帧子目录,声音为 ogg/wav 文件,内嵌纹理为编号 PNG。返回导出/失败数量。

参数:

  • dataFilePath(必填)

  • entityType(必填)— 可选值:spritessoundsembeddedtextures

  • outputDir(必填)— 导出目标目录

  • 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(必填)— 可选值:createdestroyalarmstepcollisionkeyboardmouseotherdrawkeypresskeyreleasetriggercleanupgestureprecreate

  • subtype(可选)— 零基事件子类型索引(默认: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(必填)— spriteobjectvariablefunctionstring

  • name(必填)— 资产名(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 可执行文件或 dump

  • outputPath(必填)— 输出数据文件路径

输入:

{"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 — Windows

  • game.ios — iOS

  • game.droid — Android

  • game.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