Mutagen
FreeNot checkedMCP server for working with Bethesda plugins like esm esp esl using mutagen works library
About
MCP server for working with Bethesda plugins like esm esp esl using mutagen works library
README
MCP-сервер (stdio) на .NET 9 + Mutagen для чтения Skyrim SE (и родственных релизов Mutagen с ToSkyrimRelease()): статус путей, список из plugins.txt (пагинация offset / maxPlugins, фильтр только отсутствующие на диске), заголовки, мастера, компактный режим mutagen_verify_masters без массива рёбер, а также ограниченный анализ записей (цепочка оверрайдов по FormKey, сравнение двух плагинов, кандидаты ITM / удалённые записи). Тяжёлые сценарии ограничены параметрами и настройками (maxPlugins, maxNodes, MaxConflictScanRecords, …).
Требования
- .NET SDK 9 (на машине с игрой обычно Windows; сборка кроссплатформенная; в Docker образ собирается сам).
- Путь к
plugins.txt(список и порядок плагинов для игры или профиля MO2). DataFolder— первый корень поиска (обычноSkyrim Special Edition\Dataс ванильными мастерами).PluginSearchRoots— дополнительные корни (плоские каталоги с.esp/.esm/.eslили кореньmodsMod Organizer 2). Порядок важен: последний корень в списке выигрывает, если одно и то же имя файла встречается в нескольких корнях.- Для MO2 (плагины в
mods/<имя мода>/…, а не в корнеmods/): задайтеModlistTxtPath— путь кmodlist.txtпрофиля. Резолвер ищет файл какmodsRoot/<папка из modlist>/(Data/)?/<имя плагина>; приоритет как в MO2: ниже в modlist — выше приоритет при совпадении имён.
Если PluginSearchRoots пуст, используется только DataFolder. Без ModlistTxtPath дополнительные корни обрабатываются только плоско (корень/файл и при ProbeDataSubfolder — корень/Data/файл).
Заметка: это не дублирует Mod Organizer MCP: тот про управление профилем MO2; mutagen-mcp использует те же файлы, чтобы открыть бинарные плагины через Mutagen.
Сборка
dotnet build MutagenMcp.sln -c Release
Бинарь: src/MutagenMcp/bin/Release/net9.0/mutagen-mcp.dll.
Запуск в контейнере
MCP по stdio нуждается в интерактивном stdin у контейнера (-i). Смонтируйте Data, при MO2 — mods и modlist.txt, plugins.txt профиля; передайте внутренние пути в env.
Сборка образа (из корня репозитория):
docker build -t mutagen-mcp:local .
Минимальный пример (только игровой Data + plugins.txt):
docker run -i --rm \
-v "/path/to/Skyrim Special Edition/Data:/data:ro" \
-v "/path/to/profiles/MyProfile/plugins.txt:/config/plugins.txt:ro" \
-e MutagenMcp__DataFolder=/data \
-e MutagenMcp__PluginsTxtPath=/config/plugins.txt \
mutagen-mcp:local
Mod Organizer 2 (игра + каталог mods + modlist.txt того же профиля, что и plugins.txt):
docker run -i --rm \
-v "/path/to/Skyrim Special Edition/Data:/data:ro" \
-v "/path/to/MO2/mods:/mods:ro" \
-v "/path/to/MO2/profiles/MyProfile/plugins.txt:/config/plugins.txt:ro" \
-v "/path/to/MO2/profiles/MyProfile/modlist.txt:/config/modlist.txt:ro" \
-e MutagenMcp__DataFolder=/data \
-e MutagenMcp__PluginsTxtPath=/config/plugins.txt \
-e MutagenMcp__ModlistTxtPath=/config/modlist.txt \
-e MutagenMcp__PluginSearchRoots__0=/mods \
mutagen-mcp:local
Пример с плоским вторым корнем (без MO2-структуры папок):
docker run -i --rm \
-v "/path/to/Skyrim Special Edition/Data:/data:ro" \
-v "/path/to/mods/staging:/mods:ro" \
-v "/path/to/profiles/MyProfile/plugins.txt:/config/plugins.txt:ro" \
-e MutagenMcp__DataFolder=/data \
-e MutagenMcp__PluginSearchRoots__0=/mods \
-e MutagenMcp__PluginsTxtPath=/config/plugins.txt \
mutagen-mcp:local
Фрагмент для Cursor (Linux / WSL; пути замените на свои; образ локально: mutagen-mcp:local):
{
"mcpServers": {
"mutagen-mcp": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-v", "/mnt/g/Games/steamapps/common/Skyrim Special Edition/Data:/data:ro",
"-v", "/mnt/s/MO2_data/mods:/mods:ro",
"-v", "/mnt/s/MO2_data/profiles/Default/plugins.txt:/config/plugins.txt:ro",
"-v", "/mnt/s/MO2_data/profiles/Default/modlist.txt:/config/modlist.txt:ro",
"-e", "MutagenMcp__DataFolder=/data",
"-e", "MutagenMcp__PluginsTxtPath=/config/plugins.txt",
"-e", "MutagenMcp__ModlistTxtPath=/config/modlist.txt",
"-e", "MutagenMcp__PluginSearchRoots__0=/mods",
"-e", "MutagenMcp__CacheGameEnvironment=true",
"mutagen-mcp:local"
]
}
}
}
На Windows с Docker Desktop пути в -v часто задают как D:/Games/.../Data:/data:ro. Флаг :ro необязателен, но удобен для чтения.
При MutagenMcp__CacheGameEnvironment=true в Docker имеет смысл задать лимит памяти контейнера (например --memory=8g в args перед образом), если лоад большой.
Ограничения: контейнер должен видеть те же файлы, что и ваша установка. В Linux-контейнере на смонтированном NTFS возможны проблемы с регистром имён файлов относительно plugins.txt.
Процесс MCP, Docker и кэши
- Нет отдельного «вечного» контейнера под MCP: при типичном конфиге
docker run -i --rm …Cursor запускает контейнер на время одной сессии MCP (stdio к процессуdotnet …/mutagen-mcp.dllвнутри контейнера). Выключили MCP или перезагрузили окно — процесс завершился, с--rmконтейнер удаляется. Следующий старт — снова новый контейнер и пустая память процесса. - Кэш резолва путей (класс
PluginPathResolver, RAM процесса, на диск не пишется): накопительный словарь «имя файла плагина → абсолютный путь» (или пометка, что файла нет). Общий для пакетногоResolvePluginPathsи одиночногоTryResolve. Сбрасывается при смене списка корней (DataFolder+PluginSearchRoots), пути или mtimemodlist.txt, флагаProbeDataSubfolder. Порядокplugins.txtна физическое расположение файлов на диске не влияет — в ключ инвалидации он не входит. Для MO2 резолв по папкам модов делается пакетно (перечисление.esp/.esm/.eslв каталоге мода), без комбинаторного числа отдельных проверок «мод × плагин». mutagen_plugin_master_closureперед обходом мастеров один раз вызывает пакетный резолв для всех включённых плагинов изplugins.txt, чтобы заполнить этот словарь; дальнейшие обращения к тем же именам идут из кэша.CacheGameEnvironment— отдельный кэш: полныйGameEnvironmentMutagen в памяти до смены лоада/сигнатуры; см. таблицу ниже. С кэшем путей к файлам он не смешивается.
Конфигурация
src/MutagenMcp/appsettings.json, секция MutagenMcp:
| Ключ | Описание |
|---|---|
DataFolder |
Первый корень поиска плагинов (часто игровой Data) |
PluginSearchRoots |
Дополнительные корни; более поздние важнее при совпадении имён файлов в разных корнях |
ModlistTxtPath |
Путь к MO2 modlist.txt; включает разрешение путей внутри PluginSearchRoots по папкам модов |
ProbeDataSubfolder |
Если true, для каждого корня (и для mods/<мод>/) проверяются и …/file.esp, и …/Data/file.esp (по умолчанию true) |
PluginsTxtPath |
Абсолютный путь к plugins.txt |
GameRelease |
Имя enum Mutagen, по умолчанию SkyrimSE |
PrependImplicitStandardMasters |
Если true (по умолчанию), к резолвящемуся лоаду (полный GameEnvironment, mutagen_formid_override_chain и т.д.) в начало добавляются стандартные мастера SSE (Skyrim.esm, Update.esm, DLC), если они есть на диске, но нет среди включённых строк plugins.txt (типичный MO2). Только Skyrim-релизы; false — старое поведение «только plugins.txt» |
MaxPluginsPerRequest |
Верхняя граница для параметра maxPlugins у mutagen_list_plugins и mutagen_verify_masters (1–2000) |
MaxConflictScanRecords |
Потолок записей при скане одного плагина в mutagen_plugin_record_flags_report (1–100000) |
MaxCompareResults |
Потолок пар FormKey в mutagen_compare_two_plugins (1–10000) |
MaxOverrideChainLength |
Верхняя граница параметра maxChainLength у mutagen_formid_override_chain (1–256). Это только обрезка массива chain в JSON, не лимит «сколько плагинов проверить» при полном обходе |
OverrideChainUseBinaryOverlay |
Если true (по умолчанию), mutagen_formid_override_chain не строит полный GameEnvironment, а открывает каждый плагин через binary overlay и ищет FormKey по top-level группам Mutagen (ContainsKey на группе), без полного перебора всех записей мода. false — прежний путь с полным лоадом и кэшем окружения |
OverrideChainOverlayMaxDegreeOfParallelism |
Для overlay-ветки: сколько плагинов открывать параллельно (1 — по очереди). 0 — авто: от 1 до min(число_ядер, 8). Больше — быстрее на SSD при длинном plugins.txt; меньше — если упираетесь в диск или RAM |
CacheGameEnvironment |
Если true, один полный GameEnvironment переиспользуется между вызовами тяжёлых инструментов, пока не изменятся plugins.txt, пути плагинов или связанные опции (выше потребление RAM) |
EnvironmentCacheMaxAgeSeconds |
При CacheGameEnvironment и значении > 0 кэш сбрасывается по таймеру (секунды); 0 — только инвалидация по сигнатуре файлов |
ExposeToolTimingsInJson |
Если true (по умолчанию), тяжёлые conflict-tools добавляют в JSON поле timings (мс), чтобы видеть длительность без чтения stderr |
ToolDiagnosticsLogPath |
Если задан абсолютный путь к файлу, сервер дописывает в него строки NDJSON (один JSON на строку): события кэша окружения, сборки GameEnvironment, успехи и ошибки conflict-tools с теми же таймингами, что в логах и при ExposeToolTimingsInJson. Пустая строка — выключено. Каталог создаётся при необходимости |
ToolDiagnosticsProgressPluginInterval |
В mutagen_formid_override_chain каждые N позиций лоадордера пишется строка OverrideChainScanProgress в NDJSON-лог (0 — отключить). По умолчанию 64. Перед overlay-сканом пишется событие OverrideChainResolvedListings: prepMs (от старта инструмента до готовности листингов), readListingsMs (чтение лоада через Mutagen из plugins.txt), resolvePathsMs (поиск файлов на диске / MO2), плюс счётчики вроде pluginCount |
Переменные окружения (перекрывают JSON): MutagenMcp__DataFolder, MutagenMcp__PluginsTxtPath, MutagenMcp__ModlistTxtPath, MutagenMcp__GameRelease, MutagenMcp__PrependImplicitStandardMasters, MutagenMcp__MaxPluginsPerRequest, MutagenMcp__MaxConflictScanRecords, MutagenMcp__MaxCompareResults, MutagenMcp__MaxOverrideChainLength, MutagenMcp__OverrideChainUseBinaryOverlay, MutagenMcp__OverrideChainOverlayMaxDegreeOfParallelism, MutagenMcp__CacheGameEnvironment, MutagenMcp__EnvironmentCacheMaxAgeSeconds, MutagenMcp__ExposeToolTimingsInJson, MutagenMcp__ToolDiagnosticsLogPath, MutagenMcp__ToolDiagnosticsProgressPluginInterval, MutagenMcp__ProbeDataSubfolder, MutagenMcp__PluginSearchRoots__0, MutagenMcp__PluginSearchRoots__1, …
Cursor / MCP (без Docker)
Пример (путь к проекту и пути к Data / MO2 подставьте свои):
{
"mcpServers": {
"mutagen-mcp": {
"command": "dotnet",
"args": [
"run",
"--project",
"C:/path/to/mutagen-mcp/src/MutagenMcp/MutagenMcp.csproj",
"-c",
"Release",
"--no-build"
],
"env": {
"MutagenMcp__DataFolder": "D:/Steam/steamapps/common/Skyrim Special Edition/Data",
"MutagenMcp__PluginsTxtPath": "C:/ModOrganizer/profiles/Default/plugins.txt",
"MutagenMcp__ModlistTxtPath": "C:/ModOrganizer/profiles/Default/modlist.txt",
"MutagenMcp__PluginSearchRoots__0": "C:/ModOrganizer/mods",
"MutagenMcp__CacheGameEnvironment": "true"
}
}
}
}
После первой сборки можно вызывать dotnet exec .../mutagen-mcp.dll вместо dotnet run, если удобнее.
Готовый фрагмент для вставки в конфиг MCP Cursor (с кэшем окружения): docs/cursor-mcp.example.json — скопируйте содержимое в свой mcp.json / настройки MCP и поправьте пути. Новые аргументы tools (offset, onlyFileMissing, includeDirectEdges, …) не требуют правок mcp.json: схема приходит от сервера при подключении; достаточно пересобрать образ или обновить бинарь и перезапустить MCP.
Длинные ответы: пагинация и «только проблемы»
| Инструмент | Параметры | Зачем |
|---|---|---|
mutagen_list_plugins |
offset (по умолчанию 0), maxPlugins, onlyEnabled, onlyFileMissing |
Листать длинный лоадордер страницами; при onlyFileMissing: true в выборку попадают только плагины из plugins.txt, для которых нет файла на диске по текущим корням (после фильтра onlyEnabled). |
mutagen_list_plugins |
ответ | totalMatching, returnedCount, count (= returnedCount), limit, offset, hasMore / truncated, onlyFileMissing. Поля header и headerReadError в строках плагина опускаются, если null (меньше JSON). |
mutagen_list_plugins |
includeHeaders |
Ошибка чтения заголовка одного файла → headerReadError на этой строке, вызов целиком не падает. |
mutagen_verify_masters |
includeDirectEdges (по умолчанию true) |
При false массив directEdges в JSON не включается (остаются missingMasters, orderViolations, cycles, …); число рёбер по-прежнему в counts.directEdges. Удобно при большом лоадордере, когда нужны только отчёты об ошибках. |
Полный проход mutagen_verify_masters по всем включённым плагинам по-прежнему ограничен maxPlugins и серверным MaxPluginsPerRequest.
Инструменты
| Имя | Назначение |
|---|---|
mutagen_status |
serverVersion — версия сборки (в Docker: 0.x.y+docker. + UTC-метка в publish, чтобы проверить, что запущен свежий образ), плюс пути к plugins.txt / modlist.txt, GameRelease, корни поиска |
mutagen_list_plugins |
Список из plugins.txt; файлы ищутся по DataFolder + PluginSearchRoots (+ разбор MO2 при ModlistTxtPath). Пагинация: offset + maxPlugins; в ответе totalMatching, hasMore / truncated, returnedCount (поле count — то же число). onlyFileMissing — только плагины без файла на диске. При includeHeaders ошибка чтения заголовка даёт headerReadError на строке, а не сбой всего вызова |
mutagen_plugin_header |
Заголовок одного плагина; путь через те же правила резолва |
mutagen_plugin_major_records_sample |
Первые N major-записей из одного плагина через binary overlay (без полного GameEnvironment); в ответе есть formKey — удобно подобрать ключ для mutagen_formid_override_chain |
mutagen_plugin_lookup_form_key |
Один плагин + одна строка FormKey: быстрый поиск по top-level группам; при enumerateFallback: true (по умолчанию) при промахе — полный перебор major-записей только этого файла (на больших мастерах вроде Skyrim.esm может занять заметное время). Ответ: found, lookupStrategy, formKey, recordType, editorId |
mutagen_plugin_master_closure |
Предки по цепочке Master в заголовке TES4 (не обратные зависимости); аргументы: pluginFileNames[], maxNodes (по умолчанию 512, до 4096). Перед BFS выполняется пакетный резолв путей для всех включённых плагинов из plugins.txt (общий кэш с остальными инструментами). В ответе: nodes, unresolvedMasters, флаг truncated |
mutagen_verify_masters |
По первым maxPlugins плагинам из plugins.txt (по умолчанию только включённые): чтение списков Master без полных заголовков; ответ — directEdges (опционально), missingMasters (notInLoadOrder / disabled / fileNotFound), orderViolations (мастер ниже зависимого), cycles (SCC по рёбрам «мастер раньше зависимого»), dependentFileMissing, readErrors, truncated если в списке остались необработанные включённые. includeDirectEdges: false убирает массив directEdges из JSON (счётчик остаётся в counts.directEdges) |
mutagen_formid_override_chain |
FormKey (hexId:PluginFile.esp, как в Mutagen) → плагины из резолвящегося лоада (включённые строки plugins.txt с файлом на диске плюс при PrependImplicitStandardMasters стандартные мастера SSE в начале, если их не было в txt), в которых есть major-запись с точно таким ключом; победитель — макс. индекс. Строка должна совпадать с тем, что возвращает Mutagen (см. mutagen_plugin_lookup_form_key / mutagen_plugin_major_records_sample): для оверрайдов суффикс после : часто совпадает с файлом, где лежит строка, а не только с колонкой мастера в xEdit. Параметр accurateRecordLookup: при true на каждом плагине используется полный перебор major-записей вместо top-level ContainsKey — очень медленно на длинном лоаде, если ключа нет; только для отладки. При totalContributors: 0 в ответе (если удалось собрать статистику) есть emptyResultDiagnostics: счётчики включённых строк plugins.txt vs резолвнутых файлов, флаги стандартных мастеров (Skyrim.esm, Update.esm, …), текстовый hint. Остальное: winnerOnly, maxChainLength (обрезка JSON), overlay vs OverrideChainUseBinaryOverlay, pluginsScannedFromEnd. Пример: 000D62:AI Overhaul.esp |
mutagen_compare_two_plugins |
Пересечение FormKey по major-записям в двух ESP/ESM; sameBinaryShape через Equals Mutagen. recordType — подстрока имени типа записи |
mutagen_plugin_record_flags_report |
По одному плагину: удалённые записи (флаг Deleted через рефлексию) и кандидаты ITM (равенство записи победителю при лоаде без этого мода). Счётчик majorRecordsExamined ограничен maxRecords. includeItmCandidates: false сильно ускоряет (нет второго полного лоада и разрешения победителя по каждой записи) |
Ответы — JSON-текст. Для conflict-tools при ExposeToolTimingsInJson: true в том же JSON есть timings (мс) — удобно для агента и UI без stderr. У mutagen_formid_override_chain в overlay-режиме в timings есть resolvedListingsPrepMs (суммарно: чтение листингов + резолв путей до скана). Разбивка readListingsMs / resolvePathsMs выводится только в NDJSON-событии OverrideChainResolvedListings (см. ToolDiagnosticsLogPath), а не в поле timings JSON-ответа. Дополнительно логи в stderr: сборка GameEnvironment, RentFullEnvironment: CACHE HIT|MISS|…, те же цифры дублируются текстом. Если задан ToolDiagnosticsLogPath, события и тайминги пишутся в файл построчно (NDJSON) — удобно, когда stderr клиента MCP не сохраняется или нужен последующий разбор (jq, Grafana Loki и т.п.). В Docker смонтируйте каталог для файла, например -v "/path/to/mcp-logs:/logs" и -e MutagenMcp__ToolDiagnosticsLogPath=/logs/mutagen-mcp-diagnostics.ndjson.
Производительность тяжёлых инструментов
mutagen_formid_override_chain:winnerOnly: true— один победитель (макс. индекс лоадордера); overlay параллелит открытия как полный скан.winnerOnly: false— полная цепочка.accurateRecordLookup: true— перебор всех major-записей в каждом плагине лоада вместо быстрого top-level поиска: на сотнях модов и «левом» FormKey время может исчисляться минутами; по умолчаниюfalse. ВtimingsJSON (и NDJSONConflictToolOk) для overlay естьresolvedListingsPrepMs. Детальная разбивка чтения лоада vs резолва путей — в NDJSONOverrideChainResolvedListings(readListingsMs,resolvePathsMs).maxChainLengthтолько обрезка JSON.mutagen_verify_mastersсincludeDirectEdges: falseсильно уменьшает размер ответа при том же объёме работы на диске (чтение заголовков плагинов для графа).mutagen_list_plugins: для обзора лоада без гигантского JSON используйтеoffsetи умеренныйmaxPlugins; для поиска «дыр» в установке —onlyFileMissing.- Кэш резолва путей (см. выше) переиспользуется между вызовами в одном процессе: повторный
mutagen_list_plugins,mutagen_verify_masters,mutagen_formid_override_chainи т.д. после первого полного прохода по именам дают гораздо меньше обращений к диску, пока не сменились корни илиmodlist.txt. - Включите
CacheGameEnvironment(и при необходимостиEnvironmentCacheMaxAgeSeconds), если клиент MCP часто вызывает тяжёлые инструменты с полным лоадом подряд: повторно не поднимается полныйGameEnvironment(кроме сценария ITM — там по-прежнему строится второй лоад без целевого мода). - Для Docker задайте достаточный лимит памяти контейнера: кэш окружения держит один полный граф модов в RAM; словарь путей к плагинам обычно скромнее, но тоже живёт в процессе.
- Не запускайте несколько долгих вызовов параллельно на одном stdio-процессе без необходимости.
Ограничения v1
mutagen_formid_override_chainне видит плагины, включённые вplugins.txt, но без найденного файла подDataFolder/PluginSearchRoots/ MO2 — они выпадают из скана. Стандартные мастера (Skyrim.esm, …) приPrependImplicitStandardMasters: trueдобавляются к лоаду даже без строк вplugins.txt(см. конфиг). Пустая цепочка при корректном лоаде часто означает неверную строку FormKey относительно Mutagen, а не только «нет оверрайдов».- Инструменты по FormKey и сравнению плагинов используют резолв путей к файлам плагинов (внутрипроцессный кэш имён до смены корней /
modlist.txt/ProbeDataSubfolder). ПолныйGameEnvironmentподнимается в зависимости от режима (overlay vs полный лоад) иCacheGameEnvironment; держите лимиты (maxRecords,maxResults, настройкиMax*) разумными. - Семантика ITM / сравнение записей опирается на Loqui/Mutagen
Equals, а не на побайтовое совпадение с xEdit; удалённые записи определяются черезMajorRecordFlagsрефлексией — возможны расхождения с UI. includeHeadersвmutagen_list_pluginsоткрывает каждый плагин через Mutagen — держитеmaxPluginsумеренным; для длинного лоада используйтеoffsetи при необходимостиonlyFileMissing; большие JSON-ответы клиент MCP может обрывать по таймауту.mutagen_plugin_master_closureстроит только мастер → мастер мастера из заголовков; патчи без записи в мастерах и «кто зависит от плагина X» сюда не входят.mutagen_verify_mastersпо умолчанию обрабатывает только первыеmaxPluginsвключённых записей в порядке файла — не весь список целиком; увеличьтеmaxPluginsилиMaxPluginsPerRequestдля полного прохода.- Разрешение по
modlist.txtопирается на строки+ИмяПапкиМода; при несовпадении имени папки на диске плагин может не находиться.
Лицензия
Укажите лицензию по желанию владельца репозитория (файл не добавлялся автоматически).
Installing Mutagen
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/Charrdge/mutagen-mcpFAQ
Is Mutagen MCP free?
Yes, Mutagen MCP is free — one-click install via Unyly at no cost.
Does Mutagen need an API key?
No, Mutagen runs without API keys or environment variables.
Is Mutagen hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install Mutagen in Claude Desktop, Claude Code or Cursor?
Open Mutagen on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
Fetch
Web content fetching and conversion for efficient LLM usage.
AWS KB Retrieval
Retrieval from AWS Knowledge Base using Bedrock Agent Runtime.
by modelcontextprotocolSpring AI MCP Server
Provides auto-configuration for setting up an MCP server in Spring Boot applications.
llm-analysis-assistant
A very streamlined mcp client that supports calling and monitoring stdio/sse/streamableHttp, and can also view request responses through the /logs page. It also
by xuzexin-hzCompare Mutagen with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All ai MCPs
