Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
PYTHONPATHYesPath to the GameModMCP 'src' directory, needed so the server script can import the gamemod_mcp package (e.g. "C:/path/to/GameModMCP/src").
PYTHONUTF8NoSet to "1" to force UTF-8 mode for the Python process (the project's sources and outputs contain non-ASCII text).1
GAMEMOD_DEBUG_LOGNoTurn the tool-call log stream on/off for this server process: "1" to enable, "0" to disable. Can also be set per client. Default: off.
GAMEMOD_DEBUG_LOG_PATHNoPath of the tool-call log stream file. Default: ~/.gamemod/debug/calls.jsonl
GAMEMOD_DEBUG_LOG_MAX_BYTESNoMaximum size of a single log file before it is rotated. Default: 8 MB
GAMEMOD_DEBUG_LOG_MAX_FILESNoNumber of log files to keep, including the current one. Default: 4

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
find_game_processesA

列出正在运行的游戏进程,并识别其运行时(il2cpp / mono / 其它)。

Args: name_filter: 进程名子串过滤(大小写不敏感)。 only_games: 只要看起来像游戏的进程(默认 True)。

Returns: {ok, count, processes: [{pid, name, exe_path, window_title, runtime, is_64}]} is_64 为 None 表示没测出来(拿不到进程句柄),不是「32 位」。

inspect_gameA

探测一个游戏目录:识别引擎、找出主程序与关键文件。

本项目的使用方式是「一次只针对一个游戏项目目录」, 所以不必去扫全机有哪些游戏 —— 直接把目录传进来即可。

Args: game_dir: 游戏安装目录。

Returns: {ok, engine, main_exe, il2cpp/mono/ue 的关键路径, ...}

inspect_binaryA

分析 PE 文件(exe/dll):架构、节区、导出表、导入表、运行时类型。

用于判断游戏是否加壳、是否托管、以及找出可 Hook 的导出函数。

关键结论(architecture / runtime / export_count / section_count) 会同时提到顶层与 pe 子对象里:Agent 通常只关心「这是不是 x64」 「这是不是 .NET」「有没有导出函数」,不该为了拿这几个值多下钻一层。 完整的节区表、导入 DLL 列表留在 pe 里。

Args: path: 可执行文件或 DLL 路径。

open_gameA

选定游戏,并构建(或复用)它的元数据索引(跨引擎自动识别)。

之后所有查询工具都以这个游戏为上下文。切换游戏会重置会话 (已注入的运行时会被安全卸载)。

支持三种引擎,会自动识别: · IL2CPP — 元数据是 global-metadata.dat,逻辑在 GameAssembly.dll · Mono — 元数据是托管 dll(通常是 Assembly-CSharp.dll) · UE — 无集中元数据(能力以 get_diagnostics 报告为准)

重复调用同一个游戏不会重建(返回 reused=true,通常 10ms 内返回)。

Args: game_dir: 游戏安装目录(推荐;会自动识别引擎并定位元数据文件)。 metadata_path: 直接指定元数据文件(IL2CPP 用 global-metadata.dat, Mono 用某个 .dll;目录结构不标准时用)。 engine: 强制指定引擎(il2cpp / mono / ue),一般不用填。 cache_dir: 索引缓存目录,默认 ~/.gamemod/cache。 force_rebuild: 强制重建索引(怀疑缓存损坏时用)。

Returns: {ok, engine, engine_label, metadata_path, index: {type_count, method_count, field_count, capabilities...}, elapsed, reused}

get_sessionA

查看当前会话状态:选中的游戏、索引统计、运行时注入情况。

排查「为什么查不到类 / Hook 不上」时,先看这个。

close_sessionA

关闭当前会话:卸载所有 Hook、断开注入、关闭索引。只有真的恢复原状才 ok=true。

任一步失败会 ok=false + error,并保留会话以便重试 —— 这是有意的: 本工具的语义是「游戏已恢复原状」,一个假成功会让你带着仍被篡改的游戏收工。

做完了就调用它,把游戏恢复原状(卸载 Hook 后游戏行为回到未修改状态)。

只有真的恢复成功才返回 ok: True。 卸载 Hook / 卸载脚本 / detach 任何一步失败都会 ok: False + error,并保留会话以便重试 —— 这是有意为之:本工具的语义是「游戏已恢复原状」, 一个假成功会让 Agent 带着仍被篡改的游戏进程收工。

search_classesA

搜索游戏里的类、结构体、枚举 —— Mod 开发最主要的入口。支持按基类与程序集过滤。

返回结果里带 handle,结果集较大时可用 read_query_result 翻页, 并可用它的 pattern 参数在结果内再过滤。

下游工具该用哪个名字:用 nested_name,不要用 full_name。 嵌套类型(状态机 State、协程体 <Xxx>d__0、私有内部类)的 full_name 只是短名(IL2CPP 的 namespace 只属于最外层类型), 实测 20,072 个类型里有 131 个叫 State,只有 1 个是顶层。 拿短名去查,会命中一堆同名类型,或直接被判为歧义而拒绝。 nested_name(如 Outer.Inner)才是能唯一定位的那个。

Args: pattern: 名称子串(大小写不敏感)。空表示不过滤。 subtype_of: 只要继承自该类型的(如 "UnityEngine.MonoBehaviour")。 assembly: 按程序集名过滤(子串,如 "TeamTapas")。 only_enums: 只要枚举(找游戏常量表很有用)。 limit: 本页最多返回多少条(上限 2000)。

Returns: {ok, handle, total, returned, has_more, items: [{full_name, nested_name, namespace, method_count, field_count, is_enum, image, token}]}

class_detailA

查看一个类的完整信息:字段(含类型)、方法(含 token)、继承链;给 method_name 可只看某方法的重载。

这是定位 Hook 落点的核心工具:方法的 token 由这里给出, 再配合 runtime_class_info / find_hook_target 就能拿到真实的运行时地址。

重要:静态元数据里没有字段偏移、也没有方法地址(只有 token)。 字段偏移与方法真实地址必须靠注入后的运行时工具取。

同类名跨程序集时会拒绝并列出候选(ok:false + candidates), 请改用完整名或补 assembly= —— 因为返回的是「这个类的全部字段表」, 选错类不是少几条数据,而是整份结果都是别的类的。

查嵌套类型时,full_name 传 search_classes 给的 nested_name (如 Outer.Inner):嵌套类型的 full_name 只是短名,多个类会撞名, 本工具会因歧义而拒绝(这不是故障,是防止你拿到另一个类的字段表)。

Args: full_name: 类的完整名(如 "PresetLoaderV2"、 "TeamTapas.ProjectR.UserDatas.Service.UserData_Service", 或嵌套类型的 "Outer.Inner")。 include_inherited: 是否包含继承来的字段/方法。 method_name: 只关心某个方法时填这里。填了就只返回该方法的 全部重载(含 token / 参数表 / 是否静态), 等价于旧的 get_method_source,省一轮往返。 assembly: 同名类消歧用(子串匹配,如 "TeamTapas")。留空则 名字必须唯一,否则报错并给出候选。

Returns: {ok, full_name, nested_name, namespace, image, token, ..., fields, methods, parent, base_chain, derived_count, derived, derived_returned, derived_has_more} derived_count 是派生类总数(真值);derived 只是前 50 个名字, 被截断时 derived_has_more 为 True —— 需要完整列表请用 find_derived_classes。

search_fieldsA

按名字搜索字段,返回所属类与字段类型。按字段名搜(定位「金币」「血量」这类目标的首选入口)。

total 是真实匹配总数(不受 limit 影响),配合 offset/limit 翻页。 owner 限定单个类;同名类跨程序集时会拒绝并给候选,可用 assembly= 消歧。

Args: pattern: 字段名子串(大小写不敏感)。 offset: 跳过前多少条(配合 total / has_more 翻页)。 limit: 本页最多返回多少条(上限 2000)。 owner: 限定在某一个类里搜。接受完整类名或短名,但短名必须唯一: 若命中多个同名类(常见于不同程序集),本工具会拒绝并返回 candidates,此时请改用完整类名或用 assembly 消歧。 为什么不猜:把两个无关类的字段混在一起返回,ok 仍是 true, 但算出来的偏移必然错,且要到运行时才暴露。 assembly: 程序集名子串,仅用于在 owner 命中多个类时消歧。

Returns: {ok, handle, total, offset, limit, returned, has_more, items} total 是真实匹配总数;若超过句柄上限会带 handle_truncated 与 handle_note(翻页请用本工具的 offset,不要依赖句柄拿全集)。

search_methodsA

按名字搜索方法,返回所属类、返回类型与 token。owner 语义与 search_fields 一致。

Args: pattern: 方法名子串(大小写不敏感)。 offset: 跳过前多少条(配合 total / has_more 翻页)。 limit: 本页最多返回多少条(上限 2000)。 owner: 限定在某一个类里搜,语义与 search_fields 一致 (短名必须唯一,否则拒绝并给 candidates)。 assembly: 程序集名子串,仅用于 owner 消歧。

Returns: {ok, handle, total, offset, limit, returned, has_more, items} total 是真实匹配总数。

class_hierarchyA

查看类的继承体系(向上找基类、向下找派生类)。direction="down" 是逐层的:depth 是所在层、parent 是真实直接父类;单层超 500 条时 down_has_more=true。

同类名跨程序集时会拒绝并给出候选(与 class_detail 同一规则)。 (早期版本把传递式后代当成直接子类,depth/parent 是错的 —— 现在两者用途已分开, 想一次捞全所有脚本请用 find_derived_classes。)

Args: full_name: 类完整名。 direction: up / down / both。 depth: 向下递归深度。 assembly: 同名类消歧用(子串匹配,如 "TeamTapas")。

find_derived_classesA

找出所有继承自某个基类的类型(含间接继承);配 assembly= 可筛出游戏自己的 MonoBehaviour 脚本(找脚本的必备工具)。

total 是匹配总数,被 limit 截断时 has_more=true。

两个最常见的用法:

· 找游戏自己写的 MonoBehaviour 脚本(Mod 最常 Hook 的目标): find_derived_classes(base_full_name="UnityEngine.MonoBehaviour", assembly="Assembly-CSharp") 传 assembly 比按程序集名前缀猜「哪些是引擎自带的」可靠。 · 找所有 UI 面板 / 数据服务:把 base_full_name 换成对应基类即可。

基类不必在当前索引的程序集里(如 UnityEngine.MonoBehaviour 属于框架程序集),这种情况结果依然有效,并在 note 里如实说明。

total 是匹配总数,不受 limit 影响;returned 才是本次返回条数。 若 has_more 为 True,说明被 limit 截断了,还有结果没返回。

Args: base_full_name: 基类完整名,如 "UnityEngine.MonoBehaviour"。 assembly: 只看该程序集(子串匹配,如 "TeamTapas")。留空不限。 limit: 最多返回多少条(total 仍是匹配总数)。

Returns: {ok, found, base, total, returned, has_more, items: [{index, full_name, image, field_count, method_count}]}

analyze_dependencyA

分析一个类型引用了哪些其它类型(字段/父类/方法返回类型)。近似,非精确调用图。

referenced_count 是真实总数(不受 limit 影响); referenced_types 最多 100 条并带 referenced_has_more。

用来判断「改这个类会不会牵连很多地方」。

同类名跨程序集时会拒绝并给出候选(与 class_detail 同一规则)。

Args: full_name: 类完整名。 assembly: 同名类消歧用(子串匹配,如 "TeamTapas")。

list_imagesA

列出游戏里的程序集(dll)及各自的类型数量。支持 offset/limit 翻页并如实给 has_more。

用来判断「游戏逻辑主要在哪个 dll」,从而缩小搜索范围。

Args: pattern: 名称子串过滤(如 "TeamTapas")。 offset: 从第几条开始(翻页用)。 limit: 本页最多几条。被截断时 has_more 为 true。

decompileA

反编译 Mono/.NET 程序集,拿到真正的 C# 源码(薄封装 ilspycmd)。

给 type_name 反编译单个类型(小结果内联在 source);给 output_dir 反编译整个程序集(只落盘 + 返回文件清单,不内联源码)。 嵌套类型会退让到最外层并写 nested_note,decompiled_type 如实报 实际产出的类型名。只支持 Mono,IL2CPP 会明确报错。 依赖是可选的,见 get_diagnostics 的 decompiler.available。

为什么需要它:静态元数据只能给出「有哪些类/字段/方法、签名如何」, 但给不出方法体逻辑。想知道「这个方法到底怎么算伤害的」, 就要反编译看源码 —— 这是元数据索引与运行时 Hook 都替代不了的一环。

只支持 Mono/.NET。IL2CPP 的 GameAssembly.dll 里没有托管元数据, 传进来会明确报错(IL2CPP 的 C# 反编译需要先造 DummyDll,本项目暂未实现)。

两种粒度,用参数区分(故意只做一个工具,避免让你猜该用哪个):

· 反编译**一个类型**:给 type_name(可选 output_dir)
  源码小于 20 万字符时直接内联在 `source` 里返回。
· 反编译**整个程序集**:只给 output_dir
  产物可能上万文件,因此只落盘 + 返回文件清单统计,**不内联源码**。

Args: assembly: 程序集路径(.dll / .exe)。Mono 游戏通常是 <游戏目录>/<游戏名>_Data/Managed/Assembly-CSharp.dll。 留空则用当前会话识别出的那个(Mono 游戏可直接用)。 type_name: 类型名,支持 Namespace.Type。传空表示反编译整个程序集。 嵌套类型可传 Outer.Inner 或 Outer+Inner: 由于 ilspycmd 无法单独定位嵌套类型(产物始终是最外层那一份), 会自动退让到最外层类型,并在 nested_note 里说明。 decompiled_type 报的是实际产出的类型名(即最外层), 不是请求名 —— 这样调用方不会把外层源码误当成内层源码。 declared_types 已核对,内层类型的声明确实在这份产物里。 output_dir: 输出目录。给了就落盘(整程序集模式必填)。 split_files: 整程序集模式下是否一个类型一个文件(默认 True)。 True 出 800+ 个小文件(实测 3.5 秒),便于按需读取; False 出单个大文件(实测 11.5 秒),便于整体搜索。

read_query_resultA

按句柄翻页读取之前某次查询的完整结果;给了 pattern 就在结果内再过滤。

列表类工具单次只返回一页,但完整结果留在句柄里。 (原先独立的 search_query_result 已按契约第 3 条合并进来 —— 它就是本工具在 pattern="" 时的特例。) 两个用途共用本工具:

  • 只看第 500~550 条 → 只给 offset/limit;

  • 在这一批结果里再找含某子串的 → 再加 pattern(大小写不敏感)。

两者的成本不一样:纯翻页只解析取到的那一页(结果集是惰性物化的, 见 engine/index.py 的 _RowSource);而 pattern 要看每条的值才能 过滤,必须把整个结果集解析一遍。所以在几万条的句柄上, 纯翻页是毫秒级,带 pattern 会明显更慢 —— 缩小范围优先用 search_* 的参数(pattern / subtype_of / owner / assembly)。

原先另有一个 search_query_result,实测它在 pattern="" 时与本工具 输出完全相同 —— 它就是本工具的特例。按契约第 3 条(单一职责: 一个是另一个的特例就合并)已删除,并入 pattern 参数。

句柄有两个来源,本工具都能读:search_* 这类静态查询登记的索引句柄, 以及响应超限被截断时登记的 cap- 句柄(那种句柄可能在没有索引时也存在)。 句柄只在它所属的那一个会话内有效;过期时返回 available_handles 列出当前还能用的,绝不静默换成另一个结果集。

Args: handle: search_classes 等返回的 handle。 offset: 起始位置。 limit: 本页条数(上限 2000)。 pattern: 在结果集内二次过滤的子串;留空表示不过滤(纯翻页)。

inject_processA

把运行时探针注入正在运行的游戏进程。目标换了游戏时会把会话身份与索引一起换过去,并在返回里给 context_switched。

注入后才能使用 runtime_* / hook_* / drain_events 等在线工具。 同一个进程只会注入一次(重复调用会复用,不会重复 Hook)。

会话上下文会自动跟随目标:注入的进程若属于另一个游戏,会话身份 (game_dir / engine / metadata_path)与静态索引会一起切过去,返回里给 context_switched 说明换了哪个游戏。为什么必须切:否则 search_*(旧游戏) 与 runtime_*(新游戏)属于两个游戏,token 对账这把「尺子」本身就是错的。 反推不出目标属于哪个游戏时不猜,改用 context_warning 如实提示。

权限:不需要管理员。实测(2026-10)普通用户权限注入 IL2CPP 游戏 成功(本进程与游戏进程的完整性级别都是 Medium,attach 后取运行时、 枚举实例都正常)。唯一要匹配的是权限级:游戏若以管理员身份运行 (完整性级别 High),普通权限的 Agent 才注入不进去。 游戏带反调试 / 反注入时也会失败 —— 那是保护机制,不是权限问题。

Args: pid: 目标进程号。留空则自动找当前游戏的主程序。 auto_find: 没给 pid 时是否自动查找。 preferred_module: IL2CPP 模块名,默认自动识别 (GameAssembly.dll / libil2cpp.so)。

Returns: {ok, pid, init: {module, base, api_available, api_missing, fallbacks}, context_switched?, context_warning?}

launch_gameA

启动游戏并在游戏初始化之前注入探针(能抢在初始化前挂钩)。

同名进程已在跑时直接拒绝(避免白等 60 秒再报「IL2CPP 未就绪」); exe 指向另一个游戏时会自动切换会话上下文(建新索引), 返回里给 context_switched 说明换了哪个。

比先手动开游戏再 inject_process 更强:能在对象被创建之前就装好 Hook, 适合拦截只执行一次的初始化逻辑。

会话上下文会自动跟随 exe:若它属于另一个游戏,会话身份与静态索引 会一起切过去(并建索引),返回里给 context_switched 说明。 否则 search_* 与 runtime_* 会分属两个游戏(实测过:index 140 个 image 对上运行时 143 个,image_count_matches=false,而工具全程 ok:true)。 反推不出 exe 所属游戏时不猜,用 context_warning 如实提示。

Args: exe: 游戏可执行文件路径。留空则用当前会话识别出的主程序。 args: 附加命令行参数。按 Windows 习惯拆:空白分隔,双引号内保留空白 (所以含空格的路径要写成 -path "C:\Games\My Game\a.txt")。 反斜杠不是转义符,Windows 路径可以直接写。

runtime_infoA

查看已注入游戏的探针状态:IL2CPP 模块、映像数量、可用 API。含静态/运行时一致性自检(image_count_matches)。

一致性自检:image_count 应与静态索引里的程序集数量吻合; 不吻合说明探针挂到的可能不是同一个游戏实例,需要排查。

runtime_class_infoA

查类的运行时实况:真实字段偏移与方法真实地址(RVA)。

嵌套类型请传完整嵌套名(search_classes 的 nested_name); 短名命中多个时拒绝并给 candidates,不替你猜。

与静态 class_detail 的区别(很重要): · 静态元数据里的字段没有偏移(偏移是运行时泛型实例化后才定的) · 静态元数据里的方法只有 token,没有地址 · 这里给出的是运行时真实值 —— 这才是能直接用来 Hook 的落点

嵌套类型请传完整嵌套名:IL2CPP 的 namespace 只属于最外层类型, 嵌套类型(状态机 State、协程体 d__0、私有内部类)的命名空间是空的、 只有短名。实测 IL2CPP 样本 20,072 个类型里 7,056 个(35%)是嵌套类型, 名为 State 的类型有 131 个而只有 1 个是顶层。所以查嵌套类型时 name 要写成完整嵌套名(如 "TeamTapas.ProjectR.UI2.RUI_FloatingUI.State", 从 search_classes 的 nested_name 直接抄)。只传短名而命中多个时, 本工具返回 ok: false + ambiguous: true + candidates,不会替你猜。

Args: name: 类名(短名;嵌套类型请传完整嵌套名)。 namespace: 命名空间(可选)。不给也能用:会按短名在顶层类型 里解析,唯一才采用;命中多个则返回 ambiguous: true + candidates,让你自己指定。嵌套类型必须用完整嵌套名 (candidates[].full_name)—— 只用短名会被拒绝, 不会随便挑一个(短名会撞上同名的嵌套枚举/状态机)。 image: 程序集名(可选,如 "TeamTapas.ProjectR.dll")。 method_filter: 只看名字含该子串的方法(类方法很多时用)。

Returns: {ok, full_name, fields: [{name, type, offset, is_static}], methods: [{name, token, pointer, rva_hex, in_module, param_count}]}

find_instancesA

枚举类的活对象实例(拿 instance 指针的正路 —— 此前只能靠 Hook 碰运气)。走 GC liveness;IL2CPP 可用(实测毫秒级)、Mono 受官方限制会如实报 unsupported(不用空列表冒充「没有实例」),值类型无堆实例则明确报错。

为什么需要它:read_field_value / set_field_value / hook_method 都要一个对象指针,而在此之前唯一的办法是「Hook 一个恰好会收到 该对象的函数,从 args[0] 里捡」—— 那是碰运气。本工具直接问运行时 「这个类现在有哪些实例」。

实现走 GC liveness(Unity 自家内存分析器用的那套导出)。 注意:il2cpp_class_get_instances 这种「按类直接列实例」的导出 并不存在(5 个真实样本实测),别照着那个名字找。

引擎差异(都是如实声明,不是缺陷):

引擎

情况

IL2CPP

✅ 可用,实测毫秒级(GameObject/Transform/String 各约 10ms)

Mono

⚠️ 官方头文件写明堆遍历「只在 pre-stop-world 回调内有效」,从外部线程调用不保证可用。拿不到时本工具返回 ok:false + unsupported:true,不会用空列表冒充「没有实例」

两个明确的限制:

  • 值类型(struct)没有堆实例,会直接报错说明,而不是返回空列表。

  • 实例数超过 max_count 时截断,overflow:true 表示「还有更多」。

⚠️ 返回的实例包含派生类(这是最容易踩的坑): find_instances(name="Transform", namespace="UnityEngine") 拿回来的 实测全是 RectTransform —— 因为 Unity 里 UI 节点都是 RectTransform, 而它们是 Transform 的派生类。这不是缺陷(liveness 语义如此), 但用基类的字段偏移去读派生类实例可能出错。

所以返回里带 instances: [{pointer, class_name}] 与 exact_class_count / derived_count / class_name_counts, 请按 class_name 甄别你要的是哪一种。

⚠️ 拿到的指针只在游戏继续运行时有意义:对象可能随时被 GC 回收, 或随场景切换失效。应当立即使用(读字段 / 写字段 / 下 Hook), 不要跨多个步骤长期保存。

Args: name: 类名(嵌套类型传完整嵌套名,同 runtime_class_info)。 namespace: 命名空间。 image: 程序集名。 max_count: 最多返回多少个实例(默认 100,上限 100000)。

Returns: {ok, class, engine, count, pointers: [...], instances: [{pointer, class_name}], exact_class_count, derived_count, class_name_counts, overflow} 也可能带 unsupported(引擎不支持这条路径,如 Mono 的时机限制) 或 warning(恢复 GC world 失败,游戏可能卡住)。

find_hook_targetA

定位某个方法可 Hook 的地址(含 RVA 与 token)。同样支持完整嵌套名,歧义时拒绝。

这是「从静态元数据到 Hook 落点」的关键一步: 先用 search_classes/class_detail 从元数据确认目标方法(拿到 token), 再用本工具在运行时把类实体化,拿到真实地址; 最后用返回的 token 与静态 token 比对,确认挂的是同一个方法。

返回的每个 candidate 里 address 可直接喂给 hook_address; rva_hex 可与静态元数据的 RVA 对照,token 用于确认是同一个重载。

Args: name: 类名(短名即可;嵌套类型传完整嵌套名)。 method: 方法名(重载时看 candidates 的 param_count 区分)。 namespace: 命名空间(可选)。不给时按短名在顶层类型里解析, 唯一才采用,多个则返回 ambiguous: true + candidates。 image: 程序集名。

hook_methodA

在目标方法上安装 Hook,捕获它的调用(参数、调用来源、返回值)。用 token 指定重载;多候选未指定则拒绝。

风险:Hook 会改变游戏运行行为,且错误的参数类型可能崩溃游戏。 建议先用 find_hook_target 确认落点与 token。

重载:同名方法可能有多个重载,token 各不相同。若目标方法有多个 重载而你又没给 token / param_count,本工具会拒绝猜测并返回 候选清单(ok: false)。这是故意的:盲挂到错误的重载上, Hook 照样会成功,错误要到很久以后才以「数据不对」的形式暴露。 最可靠的做法是从静态元数据取目标重载的 token 传进来。

Args: name: 类名。 method: 方法名。 namespace: 命名空间。 image: 程序集名。 args_type: 参数类型列表,逗号分隔,用于解析参数值。 可选:int/uint/long/ulong/bool/float/double/ptr/string/cstring 例:"ptr,int,float"。留空则不解析参数值。 写错规格名会直接报错(不再静默按指针读)。 实例方法的第一个整型位置是 this:想拿对象指针就写 "ptr"(0 参数方法也一样,第 1 个 ptr 即 this)。 label: 自定义标签,便于在事件里识别(默认 "类名.方法名")。 max_calls: 最多记录多少次调用(防止高频方法刷爆事件队列)。 backtrace: 是否记录调用栈(较慢,排障时用)。 token: 目标重载的 token(如 "0x06000A3D")。选重载首选这个, 静态元数据的 class_detail / get_method_source 里就有。 param_count: 目标重载的参数个数(-1 表示不指定)。仅在拿不到 token 且各重载参数个数不同时可用。 retval_type: 返回值读取类型(int/uint/long/ulong/bool/float/double/ ptr/string/cstring)。留空 = 按静态返回类型自动推断, 这是推荐用法:float/double 的返回值在 XMM0 而不在 RAX, 按 ptr 读会给出毫无意义的整数。 override_retval: 受控改写返回值(默认空串 = 只观察不改,与原行为 100% 兼容)。支持 int/uint/long/ulong/bool/ptr/float/double; 其余类型会被当场拒绝(不假成功)。 比 eval_script 手写 Interceptor 强在三点:它进探针的 hooks 表,所以 hook_status 看得见、unhook 卸得掉、 write_memory 的重叠护栏也保护得到。 浮点返回值同样可覆写(实测走「整体换 XMM0 的 ArrayBuffer」, 见 agent.js 的 encodeRetvalOverride)。请配合 hook_status 的 override_retval 字段核对,并用 drain_events 里的 original_retval 对比改前/改后。

Returns: {ok, hook_id, class, method, chosen: {pointer, rva_hex, token}, retval_type, return_type, candidate_count, usable_count} 失败时 {ok: false, error, candidates: [...]}。

hook_addressA

在任意地址安装 Hook(不限于托管方法)。适用于原生函数、系统 API、以及「模块基址 + RVA」算出的地址。

适用于:原生游戏函数、系统 API、以及从静态分析里算出的地址 (模块基址 + RVA)。

Args: address: 目标地址,十六进制(如 "0x7ffa05f7f050")。 args_type: 参数类型列表,逗号分隔(同 hook_method)。 写错规格名会直接报错。 retval_type: 返回值类型(int/uint/float/double/ptr/string/bool)。 max_calls: 最多记录多少次。 backtrace: 是否记录调用栈。 override_retval: 受控改写返回值(默认空串 = 只观察不改)。语义与 取值规则同 hook_method(override_retval=...):支持 int/uint/long/ulong/bool/ptr/float/double,其余当场拒绝。 它进探针的 hooks 表 → hook_status 可见、unhook 可卸。

unhookA

卸载 Hook。hook_id 留空则卸载全部。

卸载后游戏行为恢复原状。收尾时务必调用(或用 close_session)。

hook_statusA

列出当前所有 Hook 及其触发次数。

触发次数可以用来判断「Hook 装对了没」: 如果挂的是每帧调用的方法却始终 0 次,多半是落点不对。

drain_eventsA

取走探针缓冲的 Hook 事件(调用记录)。kind 过滤作用于整条队列(不是只看队首)。

不匹配的事件会原序留在队列里,不会被吞;超出 limit 的匹配事件同样留下。

语义与 AiMod 的 /v1/events?after= 一致:取走即消费。 想持续观察就每隔一会儿调用一次,累加统计即可。

kind 过滤的是整条队列,不是只看队首那一条 —— 注入后队首恒有一条 notice(probe_ready),若只在队首判断,kind="hook" 会一条都取不到 (返回 ok:true + 空列表,调用方会误判成「Hook 没触发」而去排查落点)。 不匹配的事件按原序留在队列里,不会被吞;超出 limit 的匹配事件同样留下。

Args: limit: 本次最多取多少条。 kind: 只取某类事件(如 "hook" / "notice" / "log")。

Returns: {ok, returned, pending_approx, dropped, events: [...]} 事件形如 {type, id, label, phase: enter/leave, seq, thread, from, args, retval}

read_memoryA

读取游戏进程内存(十六进制返回)。

用于:确认字段偏移对不对、读对象原始字节、观察内存变化。

Args: address: 地址(十六进制,如 "0x1a2b3c4d")。 size: 读取字节数(上限 16 MB)。

write_memoryA

写游戏进程内存。

⚠️ 危险操作:写错地址会立刻崩溃游戏。 写之前务必先用 read_memory 确认地址内容符合预期。 建议只改浮点数/整数这类简单值,并先备份原值。

不要写已装 Hook 的函数入口:Frida 的 Interceptor 拥有那几字节 (它在那里写了跳转指令),绕过它直接 Memory.protect + 写同一块内存, 哪怕写回刚读到的同样字节,也会在数秒内让游戏崩溃 —— 实测两次:Windows 事件日志里是 GameAssembly.dll 在该方法 RVA 处的 0xc0000005;同样操作写在模块头(base)上则完全无害且能原样读回。 本工具默认拒绝这种写入;确实要写请传 force=True, 或者先 unhook 再写。

Args: address: 地址(十六进制)。 hex_data: 要写入的字节,十六进制串(如 "0000803f" 表示 float 1.0)。 force: 明知目标被 Hook 覆盖仍要写(默认 False = 拒绝)。

read_field_valueA

读取某个实例或静态字段的原始值(静态字段传 instance="static")。读几个字节由字段类型决定(bool 1 / int 4 / long·double·引用 8)。

自动完成三步:运行时查字段偏移 → 算出字段地址 → 读内存。

读几个字节由字段类型决定(_field_read_width):bool 读 1、int 读 4、 long/double/引用类型读 8。原来固定读 8 字节,4 字节字段会连带读到相邻 字段的 4 个字节 —— 既给出无意义的 as_int64(两个字段拼出来的数), 也会在字段贴近页尾时把一次本可成功的读取变成假失败。

返回里 read_width 是实际读取的字节数,解释字段也按宽度给: 1 → as_int8/as_uint8/as_bool;2 → as_int16/as_uint16; 4 → as_int32/as_uint32/as_float;8 → 再给 as_int64/as_double/as_pointer。

局限:只适合基础类型字段(int/float/bool/enum 等)。 引用类型字段读到的是指针,要读内容需再解引用(用 read_memory)。

静态字段:把 instance 传成 "static"(或空串)即可读静态字段, 不需要对象指针。这是 Mono 上拿单例指针的正路 —— Mono 的 find_instances 受官方限制常常返回 unsupported,而 GameManager.Instance 这类静态字段读出来的 as_pointer 就是实例指针 (宽度 8 的引用类型),可以直接喂给本工具的 instance 去读实例字段。 静态字段会连同基类一起查(declared_by 告诉你它声明在哪个类上)。

Args: instance: 对象指针(十六进制)。可从 Hook 事件的 args[0] 里拿到。 传 "static" 表示读静态字段(不需要对象指针)。 name: 类名。 field: 字段名。 namespace: 命名空间。 image: 程序集名。

set_field_valueA

写对象字段值(read_field_value 的对称入口):自动查偏移、按类型定宽度与编码、写完回读核对。溢出直接报错不截断;引用类型字段拒绝(改引用属改逻辑,请用 eval_script/Hook)。

⚠️ 危险操作:写错地址或写错值会立刻让游戏崩溃或行为异常。 写之前务必先 read_field_value 读一次,确认偏移与当前值符合预期。

与 write_memory 的分工(不是重复工具): write_memory 要「地址 + 裸十六进制字节」,是原始内存入口; 本工具要「哪个对象的哪个字段 + 一个语义化的值」,是字段入口。 它替你做了三件容易做错的事:查真实偏移、按字段类型定宽度、 按字段类型选整数/浮点/布尔的编码。

写几个字节由字段类型决定(与读取同一张表):bool 1、int16 2、 int32/float 4、long/double/引用类型 8。溢出直接报错,不静默截断 —— 把 300 写进 1 字节字段如果截成 44,你会看到 ok:true 却拿到没要的值。

引用类型字段(class/string/数组)会被拒绝:field_type 认不出基础类型 且宽度为 8 时,本工具报错而不是去写一个指针 —— 换对象引用属于改逻辑, 应该用 eval_script 或 Hook,而不是改一个字段的字节。

静态字段:把 instance 传成 "static"(或空串)即可写静态字段 (与 read_field_value 同一套约定)。静态字段走探针的 *_field_static_set_value,没有「实例 + 偏移」这回事;返回里 is_static 为 true、并给出 declared_by(它可能声明在基类上)。 ⚠️ 静态字段常被全局逻辑读取,改了会影响整个游戏——返回里带 value_before,出问题可以用它写回。

Args: instance: 对象指针(十六进制)。可用 find_instances 拿, 或从 Hook 事件的 args[0] 拿。 传 "static" 表示写静态字段(不需要对象指针)。 name: 类名。 field: 字段名。 value: 要写入的值。整数按字段宽度与符号解释;浮点字段给数字; bool 字段给 true/false/1/0;认不出类型时给 0x... 字节串。 namespace: 命名空间。 image: 程序集名。 verify: 写完后立刻回读一次并放进返回(默认 True)。 这是为了不留「假成功」:写完把实测值给你,而不是只报 ok。

Returns: {ok, class, field, instance, offset, field_address, wrote_hex, write_width, write_kind, value_before?, read_back?...}

scan_patternA

在游戏模块里做 AOB(特征码)扫描。这是兜底手段 —— 优先用元数据 + RVA 定位。

⚠️ 这是兜底手段。优先用元数据 + RVA 定位: 本工具集的核心优势就是「不用扫内存也能找到 Hook 落点」。 只有当符号被剥离、或目标是原生代码时,才用扫描。

Args: pattern: 十六进制特征码,支持通配符 "??"。 例:"48 8B ?? ?? E8"。 module: 限定模块名(默认当前 IL2CPP 模块)。 max_results: 最多返回多少个命中。

eval_scriptA

在游戏进程内执行任意 Frida JS 代码 —— 应急逃生舱。

与 AiMod 的 eval_script 同定位:当预置工具不够用时, 直接下发 JS 探测。

可用全局对象:mod / api / funcs / hooks(IL2CPP 侧), 以及 monoFuncs 与 BE(Mono 侧的已绑定导出、以及当前选中的后端对象)。 后者是排查 Mono 问题的必需品:例如 "typeof monoFuncs.mono_field_get_offset" 能直接看出某个导出有没有绑上, 不必去复刻绑定逻辑。

返回值必须是可 JSON 序列化的(字符串/数字/布尔/数组/普通对象)。

示例: "Process.arch" "mod.base.add(0x1000).toString()" "(function(){ return Process.enumerateModules().length; })()" "typeof monoFuncs.mono_get_root_domain"

Args: code: JS 表达式或语句块。

gc_controlA

暂停/恢复游戏的 GC(垃圾回收)。

Hook 期间建议暂停 GC,避免正在观察的对象被回收掉。 观察结束后务必恢复(disable=False),否则游戏内存会持续增长。

标注上 idempotentHint=False:它是状态开关,disable=True 与 disable=False 是两个不同状态,且「开了没关」会让内存持续增长。 同参数重复调确实无额外影响,但把这个调用标成「幂等」会被客户端读成 「可以随便重试」—— 在一个已经暂停了 GC 的会话里那样做是错的。 探针卸载时会自动恢复 GC(见 agent.js 的 dispose),但那是兜底, 不该替代调用方自己恢复。

Args: disable: True 暂停 GC,False 恢复。

capture_screenA

截屏 / 截窗口存盘并返回路径(不回 base64)。截图是证据、不是判据;窗口找不到会如实失败,不静默改截整屏。

⚠️ 截图不是判据,只是证据。要判断「现在在哪个界面」请用 ocr。 这条规矩来自同类项目的教训(podium-mcp 源码原文: "Screenshots are NEVER an oracle.")—— 让模型看像素去下结论, 既贵(单张截图约 5 万 token)又不可靠。

不返回 base64:本项目任何返回都不超过 256 KB,而一张 2560x1440 的 PNG base64 会直接把它撑爆。图落在磁盘上,返回路径。

典型用途:验收失败时留证据(配合 ocr 的 save_on_failure)。

Args: region: "x,y,w,h" 屏幕绝对坐标;不传则截整个主屏。 window: 窗口标题子串;给了就用该窗口的矩形(优先于 region)。 找不到会如实失败,不会静默退化成截整屏。 save_path: 保存路径;不传则落临时目录(返回里给路径)。 max_side: 证据图最长边上限(等比缩小,默认 1920);0 = 不缩放。 只影响存盘的证据图,不影响坐标语义。

Returns: {ok, path, width, height, backend, region, coordinate_space, ...}

ocrA

OCR 识别文本并给屏幕绝对坐标(可直接用于点击)。只对给定 ROI 做(实测整屏 4.1s vs ROI 数十 ms)。

matched_count==0 是「本次没找到」而非「不存在」; save_on_failure 可在没匹配到时留下截图证据。

这是「弱视觉」的主力:验收时绝大多数判断只需要 「屏幕上有没有这几个字、它在哪」。

只对给定的区域做 OCR(实测整屏 2560x1440 要 4.1 秒,而 ROI 通常几十毫秒)—— 这也是同类项目的标配做法。

坐标一律是屏幕绝对坐标,可直接喂给点击;coordinate_space 字段会明说是 screen_absolute,避免「拿裁剪图坐标去点」这个经典坑。

匹配 expected 时的语义(重要): · matched_count == 0 的意思是「本次没找到」, 不等于「游戏里没有」—— 可能界面还没加载、或文字被 OCR 拆成了多个框。返回里的 note 会写明这一点。 · best_match 取的是置信度最高的那条,不是第一条。

Args: region: "x,y,w,h" 屏幕绝对坐标;与 window 二选一。 window: 窗口标题子串;用它定 ROI。 expected: 期望出现的文本;给了就把命中的单独列出来。 match: contains(默认)/ exact / regex。 threshold: 置信度下限,低于它的丢弃(默认 0.3)。 case_sensitive: 文本匹配是否区分大小写。 max_items: 最多返回多少个文本框。 save_on_failure: 给了路径时,若没匹配到就把当时的截图存到这里 (等价 MaaFramework 的 save_on_error)—— 失败要留证据。

Returns: {ok, engine, count, results: [{text, score, box, center, click_point}], texts, matched, matched_count, best_match, click_point, coordinate_space: "screen_absolute", image, ...}

probe_logA

读游戏日志尾部并按正则过滤(编码自动 utf-8/gbk)。只读、不消费,可反复轮询。

同类 Bot 项目普遍没有这条「用日志确认结果」的通道。

为什么单独做这件事:调研的 7 个游戏自动化 Bot 项目里, 没有一个用游戏日志判断结果 —— 它们靠视觉、注册表或 CDP。 而「通过日志确认结果」恰恰是最可靠的验收通道之一 (Mod 自己的输出、游戏自己的报错都在日志里),所以这里补上。

做法刻意保守: · 只读尾部 tail_bytes(默认 256 KB)—— 日志动辄几百 MB, 绝不整个读进来; · 给了 pattern 就只返回匹配行,并额外给 matched_count, 便于断言「没有出现 ERROR」这类否定式判据; · 编码自动探测(utf-8 → utf-8-sig → gbk)—— 中文游戏常见 GBK。

⚠️ 它是只读的,不消费日志(不像 drain_events 那样取走即没)。 同一次读取可以反复做,所以适合轮询等待某个日志行出现。

Args: path: 日志文件路径。 pattern: 正则;只返回匹配行。留空返回尾部所有行。 max_lines: 最多返回多少行(默认 200,取最后 N 行)。 tail_bytes: 从文件末尾读多少字节(默认 262144)。

Returns: {ok, path, size, encoding, total_lines_in_tail, returned, lines: [{line_no, text}], matched_count?, has_more?, truncated_head}

wait_forA

轮询等待条件成立并给出证据包(验收闭环里「等到可以判了」那一步)。五种条件:field / instances / ocr / log / window。返回三态 verdict(pass / fail / unverifiable —— 把「没法判」与「没通过」严格区分)+ polls / evidence;超时如实报 fail,不静默当通过。注意 kind="log" 等的是新增(先记基线再等增量),不是「当前存在」。

为什么必须有它:游戏是异步的 —— 点完按钮要等界面出来、场景加载要 等几秒。不等待就直接断言,会得到大量假失败(其实只是还没加载完)。 同类工具(MaaFramework)把它写成一句伪代码: while(!hit && !timeout) { 检查; sleep(rate_limit); }。

支持的条件(kind)覆盖三类信号,优先用引擎信号,少用像素:

kind

判据

需要哪些参数

field

某字段的值等于 equals

name/field/equals(instance 不需要:会先用 find_instances 找)

instances

某类的活实例数 ≥ min_count

name(可选 namespace/image)

ocr

屏幕上 OCR 到 target 文本

target(可选 region/window)

log

日志里新增匹配 pattern 的行

target=日志路径,pattern

window

出现标题含 target 的窗口

target

kind='log' 等的是「新增」,不是「存在」(这条是真机实战踩出来的): 第一次轮询会记下基线匹配数,之后超过基线才算成立。 否则旧日志里早就有的匹配行会让它在 1ms 内返回 pass, 而本次操作根本没发生(实测:日志里已有 5 条「已修改 精核」时确实如此)。 要判「当前是否已有」请直接用 probe_log,不要用 wait_for。

返回值就是证据包(不是光秃秃的布尔): · ok —— 条件成立了 · verdict —— pass / fail / unverifiable 三态。 unverifiable 表示「没法判」(例如依赖不可用、探针不上线), 与 fail(判了、没成立)严格区分 —— 把「没法判」说成「没通过」 会让 Agent 去修一个根本不存在的缺陷。超时如实报 fail。 · polls / elapsed_ms / interval_ms —— 轮询了几次、花了多久 · evidence —— 每次轮询的实测值(最后若干次),可核查 · hint —— 超时时给出下一步建议

Args: kind: 上面五种之一。 target: ocr / window 的目标文本。 region: ocr 用:"x,y,w,h" 屏幕绝对坐标。 window: ocr 用:窗口标题子串。 name/namespace/image: field / instances 的类定位参数。 field: field 用:字段名。 equals: field 用:期望值(按字段类型比较)。 pattern: log 用:日志正则。必须同时给 target(日志路径)。 timeout_ms: 总超时(默认 20000)。 interval_ms: 轮询间隔(默认 500)。别设太小 —— 徒增开销。 min_count: instances 用的实例数下限(默认 1)。

Returns: {ok, verdict, kind, polls, elapsed_ms, evidence, last, hint?}

verify_stateA

出一份可证伪的验收结论:每条断言独立求值,并要求足够多的独立证据才判成立。

为什么必须有它(这是从 reverse-skill 的证据链契约学来的一条硬规则)

单条证据不得静默提升为「已验证」。它那条契约的原文是:

validated 需要 >=2 independent Evidence(最好 1 静态 + 1 动态); 单条证据不得静默提升为 validated。

我们自己的实战正好印证了这条:在 CatapultHero 验收 Mod 时, 我同时看了日志(已修改 精核 = 108)与屏幕 OCR(精核:108)—— 两个互相独立的来源给出同一个结论,才敢说「Mod 真的生效了」。 只看日志的话,日志本身是 Mod 打的,Mod 写错了照样打日志。

所以本工具把「要几条独立证据」变成显式参数,而不是留给 Agent 的直觉。

参数 checks 怎么传

逗号分隔的条目,每条形如 kind=参数,与 wait_for 共用同一套条件语义 (_eval_condition,所以两处结论必然一致):

写法

含义

log=<路径>#<正则>

日志里有匹配行(只读不消费)

ocr=<期望文本>

屏幕上 OCR 到该文本

window=<标题子串>

存在该窗口

field=<类>.<字段>=<期望值>

该字段当前等于期望值

instances=<类>>=<最小个数>

该类活实例数达到下限

示例(双证据验收 Mod 生效):

verify_state(checks="log=D:\\Game\\UE4SS.log#已修改 精核,"
                    "ocr=精核")

返回

· verdict —— pass / fail / unverifiable(三态,与 wait_for 同源)

  • pass 要求:成立条数 >= require_independent,且这些成立的条目 来自 >= require_independent 条不同的信号通道

  • 全部条件都成立但只有 1 条 → 判 fail 并说明「证据不足」, 不是 pass —— 这正是「单条证据不得静默提升」

  • 多条成立但全是同一条通道(如两条 ocr=)→ 同样判 fail, 并说明「同源,缺独立第 2 通道」—— 同一张截图上的两条断言会一起 对、也会一起错,不是「互相独立」的证据 · evidence —— 每条断言各自的 {check, hit, detail} · independent_count —— 成立的不同通道数(按 kind 去重), 与 passed(成立的条数)不同;independent_kinds 列出是哪几种 · require_independent / verdict_reason —— 门槛值与判定理由

Args: checks: 逗号分隔的断言列表,见上表。 require_independent: 判 pass 所需的成立条数下限(默认 2)。 设 1 会退化成「单条证据即通过」—— 只在确实只有一个可靠来源时用。

Returns: {ok, verdict, verdict_reason, evidence, independent_count, require_independent, passed, unverifiable_count}

detect_ueA

判断一个 exe 是否为 Unreal 引擎游戏,并给出可核查的证据与置信度。

只判定这一个程序,不做全机扫描。

UE 版本号拿不到是正常的:实测两个样本中 exe 内 UE 版本模式 0 命中, Engine/Build/Build.version 也不存在。此时返回空串 + 原因,不猜测。

Args: exe: 游戏可执行文件路径。留空则用当前会话的主程序。

ue_profileA

UE 游戏概况:容器、清单、脚本模块。汇总一个 Unreal 游戏的可提取信息(资源容器、清单、脚本对象)。

包含每个 .pak/.utoc 的文件数、目录数、以及条目样例。 全量条目可能非常大(实测某样本 18,956 条 → JSON 15.8 MB), 所以默认只给样例与计数;要完整清单用 ue_list_entries。

Args: exe: 游戏可执行文件路径。留空则用当前会话的主程序。 output_path: 可选,把完整 profile 写成 JSON 文件的路径。

ue_list_entriesA

列出 .pak / .utoc 里打包了哪些资产(可按名字过滤、翻页)。返回 returned/has_more;翻页空页会说明是哪一种「空」。

这是 UE 游戏上最实用的一步:想知道「这游戏有哪些资源、 本地化文本在哪个文件里」,就靠它。

Args: container: .pak 或 .utoc 文件路径。 kind: auto / pak / utoc。默认按扩展名自动判断。 pattern: 名字子串过滤(大小写不敏感)。 limit: 最多返回多少条。 offset: 从第几条开始(配合 total 翻页)。

ue_script_objectsA

从 UE 游戏 exe 里提取 /Script/ 模块名与对象路径。失败时按契约给 ok=false + error。

这是字符串证据,不是反射元数据:能告诉你游戏大致有哪些系统 (如 /Script/Engine、/Script/GameplayAbilities), 但拿不到父类链和属性偏移——那需要运行时。

Args: exe: 游戏可执行文件路径。留空则用当前会话的主程序。 limit: 每类最多返回多少条。

ue_manifestA

读取 UE 的打包清单 Manifest_*Files*.txt。支持 offset/limit 并有 has_more。

清单是打包时生成的「应该有哪些文件」的真值, 常用来校验从 pak/utoc 解析出的文件清单是否正确。

Args: path: 清单文件路径。留空则自动在 game_dir 里找。 game_dir: 游戏安装目录(path 留空时用)。 pattern: 路径子串过滤。 offset: 从第几条开始(翻页用)。 limit: 本页最多多少条。被截断时 has_more 为 true。

get_diagnosticsA

检查运行环境:Python、Frida、各引擎解析器是否可用。默认不扫本机进程(见 scan_processes)。

开始工作前建议先调一次,明确「这台机器上能做什么」。 如果某项显示不可用,对应的工具会失败——这是环境问题,不是工具 bug。

Args: scan_processes: 是否顺带列出本机正在运行的游戏。默认 False。 该扫描实测耗时约 2.7 秒,且与 find_game_processes 职责重复, 所以默认不扫 —— 要列运行中的游戏请直接调 find_game_processes。

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription
gamemod_guide本 MCP 的完整工作流说明(与 skills/gamemod-mod/SKILL.md 同源)。

TDQS

A4/5.0

Scored across 45 tools

Disambiguation4/5

Most tools target clearly distinct stages (static metadata, runtime inspection, hooking, memory/field access, verification), and descriptions explicitly distinguish close pairs like read_memory vs read_field_value and write_memory vs set_field_value. A few boundaries remain fuzzy, especially inspect_game vs detect_ue vs ue_profile, and class_hierarchy(down) vs find_derived_classes, but the descriptions provide enough guidance to choose correctly.

Naming Consistency4/5

The set is consistently snake_case and mostly follows verb_noun or noun_action patterns (search_classes, hook_method, read_field_value, verify_state). A handful of bare nouns and imperatives (class_detail, decompile, unhook, ocr, wait_for) break the pattern slightly but remain readable and predictable.

Tool Count2/5

At 45 tools, the surface is heavy for a single MCP server even given the broad game-modding and multi-engine scope. Many tools are individually justified, but the overall count raises selection cost and makes overlap more likely; a more tightly grouped interface would be easier to use.

Completeness4/5

Coverage is broad across static indexing, runtime inspection, hooking, memory/field mutation, UE asset inspection, OCR/log verification, and session lifecycle. The main gaps are input automation (OCR returns click points but no click/keypress tool) and full IL2CPP decompilation, which agents may need to work around.

Maintenance

ActivityMaintained
ResponsivenessSyncing