Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Mutagen

БесплатноНе проверен

MCP server for working with Bethesda plugins like esm esp esl using mutagen works library

GitHubEmbed

Описание

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 или корень mods Mod 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), пути или mtime modlist.txt, флага ProbeDataSubfolder. Порядок plugins.txt на физическое расположение файлов на диске не влияет — в ключ инвалидации он не входит. Для MO2 резолв по папкам модов делается пакетно (перечисление .esp / .esm / .esl в каталоге мода), без комбинаторного числа отдельных проверок «мод × плагин».
  • mutagen_plugin_master_closure перед обходом мастеров один раз вызывает пакетный резолв для всех включённых плагинов из plugins.txt, чтобы заполнить этот словарь; дальнейшие обращения к тем же именам идут из кэша.
  • CacheGameEnvironmentотдельный кэш: полный GameEnvironment Mutagen в памяти до смены лоада/сигнатуры; см. таблицу ниже. С кэшем путей к файлам он не смешивается.

Конфигурация

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. В timings JSON (и NDJSON ConflictToolOk) для overlay есть resolvedListingsPrepMs. Детальная разбивка чтения лоада vs резолва путей — в NDJSON OverrideChainResolvedListings (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 опирается на строки +ИмяПапкиМода; при несовпадении имени папки на диске плагин может не находиться.

Лицензия

Укажите лицензию по желанию владельца репозитория (файл не добавлялся автоматически).

from github.com/Charrdge/mutagen-mcp

Установка Mutagen

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/Charrdge/mutagen-mcp

FAQ

Mutagen MCP бесплатный?

Да, Mutagen MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Mutagen?

Нет, Mutagen работает без API-ключей и переменных окружения.

Mutagen — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить Mutagen в Claude Desktop, Claude Code или Cursor?

Открой Mutagen на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Mutagen with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории ai