Skip to main content
Glama

bg3-data-mcp

An MCP server (and CLI) for Baldur's Gate 3 modding: layered game-data lookup, static checks that catch what the engine silently ignores, in-game testing through Script Extender, and export to the Larian Toolkit for mod.io publishing. Works natively on Windows or under WSL, finds the game and tools itself, and isn't tied to any one mod.

  • Layered data: the base game read straight from the installed paks (Shared → Gustav → GustavX → hotfix Patch*.pak, via LSLib's Divine.exe), so it always matches the current patch, plus your mod layers (unpacked folders or .paks) in load order. Every resolved field says which layer set it. Indexed: stats, English localization, root templates, progressions, lists, class descriptions, level maps, action resources, feats, MultiEffectInfos and named .lsfx effects.

  • Static checks: bg3_lint_stats (values, functions and fields no shipped content uses - the engine drops them silently - missing references, spells that can't resolve) and bg3_lint_progressions (invalid node UUIDs, dangling lists, stacked choices).

  • Running game (Script Extender): Lua eval, console commands, hot-loading stats without a restart, ground-truth comparison of what the game actually loaded, and a test framework: level up by XP, check a level against its progression, and TOML test cases run as real encounters (docs/TESTING.md).

  • Deploy and Toolkit: pack/deploy/enable any mod (bg3_deploy), restart the game into the newest save, and generate or diff the Toolkit's editor copy (docs/TOOLKIT.md).

  • Timing per machine: bg3_timing keeps each user's waits in ~/.config/bg3-data-mcp/timing.json (or BG3_DATA_TIMING): a scale for every UI / harness wait with min_wait / max_wait clamps, and named settings (console polling, checkpoint interval...) each clamped to its own range. calibrate measures the console and screen-helper round trips on this machine and suggests a scale.

The SQLite index is cached per machine (see Configuration). A layer is rebuilt only when its sources' timestamps or sizes change; rebuilds are atomic, so a failed rebuild keeps serving the previous index.

Not affiliated with Larian Studios. Requires a legal copy of the game; nothing from the game is redistributed - data is read from your own install.

Resolution rules (stats)

  • The highest-ranked new entry NAME among the active layers wins.

  • using "NAME" (the entry's own name) inherits the previous layer's definition of NAME.

  • using "OTHER" inherits OTHER, resolved across all active layers.

  • No using: the entry stands alone. A redefinition without using replaces the earlier one.

  • Every resolved field records the layer and source that set it (e.g. base/GustavDev, dnd55e).

Related MCP server: DesyncedMCP

Tools

Tool

Purpose

bg3_layers

layers, load order, source timestamps, counts

bg3_refresh(force)

re-index changed layers (force: a layer name or all)

bg3_add_mod_layer(name, path, position) / bg3_remove_mod_layer(name)

manage mod layers

bg3_get_entry(name, layers)

resolved stats entry: using chain plus the source of each field

bg3_diff(name, layer)

what a layer changes about an entry compared with the layers below

bg3_search(text, type, field, defined_in)

find entries by name or field value; defined_in="dnd55e" = only what that layer defines

bg3_references(token)

everything mentioning a name or GUID (stats, templates, progressions, lists)

bg3_loca(query)

handle lookup or text search

bg3_template(key)

root template resolved through its ParentTemplateId chain

bg3_progression(key, level)

class/subclass nodes merged by node UUID

bg3_spell_list(key)

spell/passive list by UUID or name

bg3_spell_visuals(name)

a spell's effects, animations, sounds and icon

bg3_similar_spells(damage_type, school, spell_type, level, keyword)

spells like X, with visual kits to borrow

bg3_effect(guid)

a MultiEffectInfo (or effect resource): component VFX names, bones, duration, looping, .lsfx, and who uses it

bg3_search_effects(text, color=None)

find effects by name, every word in any order ("necrotic beam") -> GUIDs ready for *Effect fields; color (red, green, purple, white...) keeps effects whose keyframes are mostly that colour

bg3_fx_index()

fingerprint every vanilla .lsfx (component IDs, colour/value hashes, dominant colour) for the tools below; ~6 min, again after a game patch

bg3_fx_diff(a, b)

compare two effects component by component, float noise ignored: colours, timing, other properties, added/removed parts

bg3_fx_provenance(layer, effect=None, against=None)

trace a mod's effects to vanilla through component IDs (they survive copying): copy, recolour, recolour + edits, combined, new; non-vanilla assets; against flags anything identical to another mod and not to vanilla

bg3_fx_build(layer, apply=False)

recolour vanilla effects per <mod>/Scripts/data/vfx.toml (palette presets or custom): writes .lsfx, a Content bank and MultiEffectInfos with stable UUIDs, plus vfx_generated.json (name -> UUID for stats)

bg3_lint_effects(layer)

stats *Effect GUIDs that resolve nowhere or only in a mod loaded after the layer, and bank entries whose .lsfx is missing

bg3_game_restart(deploy_layer, save)

kill the game, deploy, relaunch via Steam and wait until loaded. Vaults your own campaigns first and arms a watchdog (docs/SAFETY.md); Continues only into a harness save, else loads one from the main menu (save = which, by title)

bg3_safety_check(action)

the safe-test-session state: protected campaigns, vaulted versions, the baseline check, the watchdog, past alerts; protect vaults now, verify checks nothing changed

bg3_level_up(levels) / bg3_level_check

grant exactly the XP for the next level; compare the host with its class/subclass progressions

bg3_test_list / bg3_test_script / bg3_test_run / bg3_test_stage / bg3_test_verify / bg3_test_cleanup

data-driven in-game test cases (TOML in the mod repo) run as real encounters - scripted, real-roll or AI-driven: see docs/TESTING.md

bg3_test_draft(passives / key+level)

draft TOML test cases from a feature's stats (targets, forced saves/hits, reactions, resource pools, non-overlapping controls), with confidence + TODOs

bg3_icon_check(layer)

ask the running game which of a layer's icons exist (5,395 known), cache them, and suggest the closest real icon for each missing one; bg3_lint_stats then flags missing icons offline

bg3_icon_extract(names, out_dir) / bg3_icon_import(paths, layer, names) / bg3_icon_build(layer, src_dir) / bg3_icon_preview(src_dir)

icon pipeline (import keys out the green screen the BG3 icon LoRAs paint on), no Toolkit or texconv: base-game icons out as 380 px PNGs; a mod's <IconName>.png sources (default <mod>/Icons/src) in as the hotbar atlas (DXT5 with mips, GUI/<Atlas>.lsx, TextureBank resource), 380/192 px tooltip and 144/72 px controller DDS and GUI/metadata.lsf, the layout of the Toolkit's Texture Atlas Editor; contact sheets to review a batch

bg3_test_spell_check(spell) / bg3_save_spells(ability)

pre-checks before writing a test: can the AI cast it, is its save the spell's own roll (interrupt-visible) or a status/surface/passive save; AI-castable spells by save ability

bg3_lint_stats(layer) / bg3_lint_progressions(layer)

static checks before deploying: values the engine silently drops (vocabulary learned from the other layers), tooltip-only names used as functors, missing references, unknown resources, feat prerequisites the engine ignores (only FeatRequirementProficiency / FeatRequirementAbilityGreaterEqual are parsed - a level condition is dropped); deliberate exceptions go in <suite>/lint_allow.toml ([[allow]] kind, name pattern, reason - still listed, not counted); progression UUIDs, dangling lists, stacked choices, passive picks with no ProgressionDescription (the screen says "Class Passives"); spell choices missing spell levels the class casts (lists resolved as the game loads them, MergedInto included) and spells learned per level against the suite's [[class_table]]

bg3_lint_container_tests(layer)

spell-container test coverage: every container with children from the layer needs a case per behaviour group of its children (children differing only by damage type or the spell/status they name share one; covers = [...] in a case names the alike children it stands for). bg3_test_draft drafts one case per group

bg3_vortex_status

Vortex, read-only: what it deployed into the Mods folder (deployment manifest), other paks there, staged mods with several versions of one mod flagged, and its recent install/remove/deploy/purge activity

bg3_vortex_sort(apply)

sorts Vortex's enabled BG3 mods into a compatible load order (dependencies from each pak's meta.lsx, then library / standalone / built-on-others / patch tiers, your order kept otherwise); reports missing or disabled dependencies and files two mods both ship; apply rewrites Vortex's load order file while Vortex is closed

bg3_vortex_bridge_install / bg3_vortex(action, mod)

optional, for Vortex users: a small Vortex extension shipped in bg3data/integrations/vortex_bridge (localhost-only, token-checked) so the MCP can deploy / purge / enable / disable / remove mods through Vortex itself

bg3_vortex_profile(action, layer)

Vortex users: a dev/test profile kept apart from your play profile. Set "vortex": {"dev_profile": "My-Testing"} in layers.json (or vortex_profile on a mod layer; optional vortex.dev_keep: extra mods by id/uuid). bg3_deploy and the gate then create/switch to it and fit it: the layer's dependencies + test_mods + dev_keep + non-pak tool mods (Script Extender) enabled, every other pak mod (and a Vortex copy of the layer itself) disabled, sorted, deployed. list / plan / fit / play (switch back to the previous profile). Needs bridge 0.3.0

bg3_saves / bg3_save_fix_mods

which mods each save was made with vs the current load order (renamed/updated/missing mods = the usual "won't load" reason), and rewriting a save's mod list (sync renamed/updated entries by UUID, drop UI-only mods); dry run by default, original backed up to the cache

bg3_screenshot / bg3_click / bg3_press_key / bg3_levelup

see and drive the game UI (Windows): a screenshot of the game window, OS-level clicks (in screenshot pixels) and key presses, and bg3_levelup (auto: a whole level-up hands-free - spells, specific spells, feats/ASI, subclass, multiclass, passive and skill choices - validated). See docs/LEVELUP.md

bg3_new_character / bg3_save_game / bg3_load_save

test characters without playing: a level 1 character of any class from the current host through the game's respec (Withers' Osi.StartRespec, choices pre-filled), named saves, and loading a save by name in the running game (no restart)

bg3_deps_fetch(layer, apply)

newest dependency releases from Nexus through Vortex's own Nexus login (no API key): check, download (Premium direct; free: one click on the page Vortex opens), install, switch versions, deploy, then bg3_deps_update

bg3_lint_rules(layer, lo, hi)

the mod's class/subclass levels against the RULES (suite rules/ tables from the rules texts): missing/extra features, choice counts, slots, unsourced subclasses

bg3_test_build / bg3_test_build_status

run a test build ([[build]] in the suite TOML) hands-free: its start save is loaded, every level is taken with the plan's subclass and the spells its tests need, validated (level_check, and the spell levels the level-up screen offers) and that level's tests run; every 5th level is saved as a checkpoint (<build id> L<n>) that start_level resumes from; background=True runs a batch of builds in a detached process, logging as it goes

bg3_deps_status / bg3_deps_diff / bg3_deps_lock / bg3_deps_update

dependency drift: each meta.lsx dependency that is a layer (its deployed pak), declared vs locked (<mod>/bg3deps.lock.json) vs current; what changed since the lock that the mod overrides or references; and an update that runs the mod's regen commands (layers.json), re-indexes, lints, bumps the dependency Version64/MD5 in meta.lsx and rewrites the lock (dry run unless apply)

bg3_toolkit_status / bg3_toolkit_export / bg3_toolkit_check

Larian Toolkit (mod.io publishing): where the Toolkit expects the mod, generate its editor copy (.stats/.tbl) from the game-ready files, and diff the two copies - formats learned from vanilla + dnd55e editor data

bg3_gate(layer, action) / bg3_gate_status

local release gate (bg3data/gate.py, also python -m bg3data.gate): clean git tree, XML parses (no comments in loca), regen changes nothing, the four lints clean, then deploy + the in-game builds the commit owes - a build is owed when it never passed or the changes since its last passing commit reach its footprint (stats, list/progression nodes and cases reachable from its class/subclass; SE code, meta.lsx and unplaceable files owe every build). plan / run (detached) / status / seed (from a build-batch log) / post (GitHub commit status bg3data/gate). Releases: python -m bg3data.release prepare LAYER VERSION (Version64 stamp + CHANGELOG), package (gate must be green: pak + info.json zip), publish (tag + GitHub Release)

bg3_ingame_check(layer)

every stats entry a layer defines vs what the running game loaded (invalid values the engine dropped)

Install

Works natively on Windows or under WSL; the game, Script Extender and LSLib are Windows programs either way. Needs uv, Python 3.11+, and LSLib v1.20.4+ (releases; Vortex's bundled divine.exe is too old for current LSF files).

Windows (PowerShell):

git clone <this repo> C:\Mods\bg3-data-mcp
cd C:\Mods\bg3-data-mcp
copy layers.example.json layers.json        # then list your mod folders (see below)
uv run python tests\env_check.py            # what it found: game, Divine.exe, mod managers, Script Extender
uv run bg3-data refresh                     # first index build: a few minutes (extracts from the game paks)
claude mcp add bg3-data -- uv run --quiet --directory C:\Mods\bg3-data-mcp bg3-data-mcp

WSL: keep the environment on the Linux filesystem (fast), the project anywhere:

export UV_PROJECT_ENVIRONMENT=$HOME/.cache/bg3-data-mcp/venv
cp layers.example.json layers.json          # then list your mod folders
uv run python tests/env_check.py && uv run bg3-data refresh
claude mcp add bg3-data -e UV_PROJECT_ENVIRONMENT=$HOME/.cache/bg3-data-mcp/venv -- \
  uv run --quiet --directory /mnt/d/path/to/bg3-data-mcp bg3-data-mcp

Configuration (layers.json)

Only your mod layers are required; everything machine-specific is discovered and can be overridden. Paths may be written Windows-style (D:\\Mods\\MyMod) or WSL-style (/mnt/d/Mods/MyMod) - both work on both.

{
  "mods": [
    {"name": "dnd55e", "path": "%LOCALAPPDATA%\\Larian Studios\\Baldur's Gate 3\\Mods\\DnD2024_897914ef-5c96-053c-44af-0be823f895fe.pak"},
    {"name": "mymod", "path": "C:\\BG3Mods\\MyMod", "tests": "tests/bg3", "deploy": "optional custom command",
     "regen": ["bash Scripts/regen_all.sh"]}
  ],
  "base":  {"game_data": "E:\\Games\\Baldurs Gate 3\\Data"},
  "divine": "C:\\Tools\\LSLib\\Packed\\Tools\\Divine.exe",
  "game":  {"launcher": "auto", "profile": "Public", "larian_dir": "...", "steam_exe": "...", "game_exe": "..."}
}

A dependency (a mod you build on) should be the .pak the game loads - the user Mods folder copy (a mod manager's symlink is followed), the same pak you would load into the Toolkit - so the index matches the release your players run. Your own mod is the unpacked folder you edit. bg3_layers lists the pak the game loads per folder layer and flags one older than the indexed sources.

Setting

Discovered from (when omitted)

base.game_data

Steam (registry + every library in libraryfolders.vdf + the app manifest), GOG (registry), common folders

divine

BG3_DIVINE env, PATH, *\\LSLib\\Packed\\Tools\\Divine.exe under your user folder / C: / D:, Vortex's copy (last)

game.larian_dir

%LOCALAPPDATA%\\Larian Studios\\Baldur's Gate 3 (Mods, PlayerProfiles, Script Extender Logs)

game.launcher

steam (steam.exe -applaunch 1086940) for Steam installs, else direct (bin\\bg3_dx11.exe); GOG uses direct

game.profile

Public (the profile whose modsettings.lsx and saves are used)

mod deploy

none: bg3_deploy packs Mods/<folder> + Public/<folder> with Divine, deploys and enables the mod

cache

%LOCALAPPDATA%\\bg3-data-mcp\\cache (Windows), ~/.cache/bg3-data-mcp/cache (WSL); BG3_DATA_CACHE overrides

Mod managers

bg3_environment reports what manages the game's Mods folder. Vortex (detected from its deployment manifest) and BG3 Mod Manager (detected while running) rewrite modsettings.lsx when they deploy or export a load order, which disables mods they don't manage: re-run bg3_deploy afterwards (it re-enables the mod after its dependencies), or add your dev mod to the manager. The in-game mod manager lists local paks under Installed and needs nothing extra.

Script Extender bridge (running game)

These tools talk to the live game through the SE console, using a persistent injector process (bg3data/ps/se_inject_server.ps1, AttachConsole + WriteConsoleInput, so no window focus is needed; ~0.34 s per call, with the one-shot se_inject.ps1 as fallback). Output is read back from the current run's Extender Runtime log. Log file names are UTC; the bridge compares real modification times with the game's start time.

Tool

Purpose

bg3_se_status

game running? PID and start time, this run's log, latest game state

bg3_se_eval(code, context)

run Lua (server/client); returns the return value as JSON plus printed lines. Runs in your live game.

bg3_se_command(line)

one console line (e.g. !apofeature X), plus the log lines it produced

bg3_se_log(filter, lines)

tail this run's log

bg3_se_live_entry(name, layers)

ground truth: the entry as the game loaded it vs the index, field by field

Hot loading (no restart)

Tool

Purpose

bg3_se_hot_load(layer, files, loca)

mirror a mod layer's stats .txt as loose files under <game>/Data/Public/BG3DataHot_<layer>/, Ext.Stats.LoadStatsFile(…, true) each, Ext.Stats.Sync every entry, and push its English loca (Ext.Loca.UpdateTranslatedString)

bg3_se_hot_clean

delete the loose BG3DataHot_* folders from the game install

bg3_se_reset_lua

console reset: reload SE Lua for mods loaded at startup

SE only reads safe relative paths through the game's file system (absolute paths are rejected by IsSafeRelativePath), which is why layers are mirrored as loose files. Entries created after startup must be synced; the tool does that. Hot loading covers stats, loca and Lua. Progressions, lists and templates load at startup and need a restart with a pak. Stats files with // comments load fine (the lines are skipped). Measured 2026-09-30: all 661 Apotheosis entries in 3.0 s and 755 loca strings in 2.6 s. The edit → hot-load one file → re-test loop takes about 3 s. Test-only: don't save while relying on hot-loaded entries.

Each eval is wrapped in unique BEGIN/END markers and pcall, so Lua errors come back as errors rather than timeouts. SE replaces load() (its second argument must be an environment table). Console access is serialised. Functor fields (SpellSuccess...) are exposed by SE as parsed userdata and can't be compared as text. SE reports ComboCategory as empty.

Hardening

  • Hot reload: tool modules (testing, drafting, lint, icons, deploy, format, parse, ...) are re-imported when their source changes, so MCP code edits apply on the next tool call. server.py, query, index, sources and se hold live state and still need a client reconnect.

  • One SQLite connection shared by MCP worker threads, serialised with a lock (WAL mode).

  • Every tool catches errors and returns a readable message; free-text inputs are capped at 500 characters (SQLite's LIKE limit), limit is clamped to 1-500, and output is capped at 24,000 characters.

  • % and _ in user text match literally (LIKE ESCAPE).

  • Resolved entries are cached per layer stack; the cache is cleared on any rebuild.

  • Base extraction goes to a temp directory and is swapped in, so a failed extraction keeps the old cache.

  • The first call after a game patch rebuilds base (about 80 s). Run uv run bg3-data refresh after patches to do it ahead of time.

Tests

  • uv run python tests/gate_selftest.py: offline checks for the gate and release helpers (build report verdicts, stats/LSX change keys, Version64 math and stamping).

  • uv run python tests/stress_test.py drives the real server over stdio with the official MCP client: protocol, correct answers, bad input, concurrency (400 calls, 32 at a time), refresh under load, .pak layer add and remove, and a full resolution sweep. It writes tests/STRESS_REPORT.md.

  • uv run python tests/ingame_check.py [--layers dnd55e] [--sample 400] compares resolved entries with the running game through SE. The deployed paks must match the layers. It writes tests/INGAME_REPORT.md. The first run (2026-09-30) compared 400 entries (200 mod-overridden) and 7,844 fields: 18 mismatches (0.23%). 16 were ComboCategory (SE doesn't expose it) and 2 were one quirk (MULTIATTACKDEFENSE: the game didn't inherit DisplayName through dnd55e's using). A targeted test of the 72 cases where "override re-parents via using" and "using ignored on override" predict different values sided with the resolver's model 78:1.

Known limits

  • SpellAnimation GUIDs are animation slot keys mapped per race and body across about 2,500 content banks, so they aren't resolved to names yet. Copy them between spells as-is.

  • About 0.7% of MultiEffectInfo components point at effect resources outside the indexed banks.

Available Tools

44 tools
bg3_add_mod_layerB

Register a mod layer: an unpacked mod folder (containing Public/ and Mods/) or a .pak file. position: 1-based slot among mod layers (default: on top). The layer is indexed immediately.

Inputs: name (required), path (required), position. Returns: result

bg3_deployA

Pack a mod layer (Mods/ + Public/, via Divine), deploy the pak to the user Mods folder (refused while the game runs; previous pak backed up) and enable it in the active profile's modsettings.lsx after its dependencies. Works for any mod; no per-mod scripts. Re-run after a mod manager rewrites the load order.

Inputs: layer (required), enable. Returns: result

bg3_diffC

What layer changes about stats entry NAME compared with the layers below it.

Inputs: name (required), layer (required), layers. Returns: result

bg3_effectC

Resolve an effect GUID: a MultiEffectInfo (the value of stats *Effect fields) with its component effect resources (names, bones, duration, looping, .lsfx file), or a single effect resource. Also lists the stats entries that use it.

Inputs: guid (required), layers. Returns: result

bg3_environmentB

What this machine looks like to bg3-data: Windows or WSL, the discovered game install (Steam library, GOG or configured), game exe, Divine.exe, the Larian user folder (Mods, modsettings.lsx, Script Extender logs), Script Extender presence, detected mod managers (Vortex, BG3 Mod Manager, in-game) and what they mean for hand-deployed paks, and each mod layer's deploy/enable state.

Inputs: no named parameters. Returns: result

bg3_game_restartA

Kill the game, optionally deploy a mod layer (its layers.json deploy command, else the built-in bg3_deploy), relaunch (Steam -applaunch, or the game exe for GOG/other installs; --skip-launcher -continueGame loads the NEWEST save) and wait until a host character is loaded. Unsaved progress is lost.

Inputs: launch, deploy_layer. Returns: result

bg3_get_entryB

Resolve a stats entry (spell, status, passive, character, object, weapon, ...) through inheritance across the selected layers. Shows the using chain and, per field, the layer/source that set it.

Inputs: name (required), layers, provenance. Returns: result

bg3_ingame_checkB

Compare EVERY stats entry a mod layer defines with what the running game loaded (the engine drops invalid values silently). layers = what's deployed (default: all).

Inputs: layer (required), layers. Returns: result

bg3_layersA

List data layers in load order with source timestamps and entry counts.

Inputs: no named parameters. Returns: result

bg3_level_checkB

Compare the host with its class/subclass progressions up to its current level: passives added and removed, AddSpells lists (with the spell's source), ActionResource boosts, and this level's choices.

Inputs: layers. Returns: result

bg3_level_upA

Grant the host exactly enough XP (from the layers' XPData) to reach levels more levels. The user then levels up in the UI, so every feature and spell is class-sourced; follow with bg3_level_check.

Inputs: layers, levels. Returns: result

bg3_lint_progressionsB

Static progression checks for a mod layer: invalid node UUIDs (the game silently drops those nodes), selectors referencing lists no layer defines, and same table+level nodes from different layers (both load, so their choices/feats stack).

Inputs: layer (required), layers. Returns: result

bg3_lint_statsB

Static stats lint for a mod layer, before the game ever loads it: enum values (Cooldown, StatsFunctorContext, RemoveEvents, SpellFlags, TickType...) and functor/condition/boost names that no other layer uses (the engine silently drops what it doesn't know), plus missing referenced entries (statuses, unlocked spells/interrupts, using, containers) and unknown action resources.

Inputs: layer (required), limit, layers. Returns: result

bg3_locaC

Look up a localization handle (e.g. h1a950eaag...), or search localized text.

Inputs: query (required), limit, layers. Returns: result

bg3_progressionC

Progression nodes for a class/subclass (Name, e.g. 'Soulknife', or TableUUID), merged by node UUID so a higher layer's node replaces a lower one's.

Inputs: key (required), level, layers. Returns: result

bg3_referencesC

Everything that mentions token (entry name, status, passive, GUID...) in stats, templates, progressions and lists.

Inputs: token (required), limit, layers. Returns: result

bg3_refreshA

Re-index layers whose sources changed. force: a layer name, or 'all', to rebuild regardless.

Inputs: force. Returns: result

bg3_remove_mod_layerB

Unregister a mod layer (base cannot be removed).

Inputs: name (required). Returns: result

bg3_search_effectsA

Find effects by what they're called (e.g. 'Necrotic', 'Impact_BodyFX', 'Radiant_Beam'): MultiEffectInfos whose name, or any component effect's name, matches. Returns the GUID to put in a stats *Effect field, what matched, and sample spells/statuses already using it.

Inputs: text (required), limit, layers. Returns: result

bg3_se_commandB

Send one Script Extender console line to the running game (e.g. '!apofeature Barbarian_PersistentRage') and return the log lines it produced within wait seconds (max 60).

Inputs: line (required), wait, context. Returns: result

bg3_se_evalA

Run Lua inside the RUNNING game via Script Extender and return its printed output plus the return value (JSON). context: 'server' (game logic, Osi.*, Ext.Stats) or 'client' (UI/visuals). delay: seconds to wait first (e.g. for a cast or boost to resolve). The test harness, once staged, is available as the global BG3T. This executes code in the user's live game session.

Inputs: code (required), delay, context, timeout. Returns: result

bg3_se_hot_cleanA

Remove the loose hot-load folders (Data/Public/BG3DataHot_*) from the game install. Entries already hot-loaded stay in the running session until restart.

Inputs: no named parameters. Returns: result

bg3_se_hot_loadA

HOT-LOAD a mod layer into the RUNNING game without restarting: mirrors its stats .txt files as loose files under /Data/Public/BG3DataHot_/, loads them with Ext.Stats.LoadStatsFile(…, true) (overwriting existing entries), Syncs every entry to the client, and (loca=True) pushes its English strings. files: only these stats files (e.g. ['Passive.txt']). Progressions/lists/templates can't be hot-loaded (restart with a pak). Test-only: don't save the game while relying on hot-loaded entries. Clean up with bg3_se_hot_clean.

Inputs: layer (required), loca, files. Returns: result

bg3_se_live_entryB

Ground truth: compare a stats entry as the running game actually loaded it with the index's resolution for layers. Pick layers matching what's deployed. Shows each field as MATCH or DIFF.

Inputs: name (required), fields, layers. Returns: result

bg3_se_logB

Tail the current game run's Script Extender runtime log, optionally only lines containing filter (e.g. '[Apotheosis]', 'error', 'FAILED').

Inputs: lines, filter. Returns: result

bg3_se_reset_luaA

Reload Script Extender Lua (console reset): re-runs every loaded mod's Bootstrap scripts from its pak/loose files. Only affects mods that were loaded at game start.

Inputs: no named parameters. Returns: result

bg3_se_statusA

Is the game running, which Script Extender log belongs to this run, and the latest game state.

Inputs: no named parameters. Returns: result

bg3_similar_spellsB

Spells matching damage type / school / spell type (Target, Projectile, Shout, Zone...) / level / name keyword, with their visual kits - to borrow effects and animations for new spells.

Inputs: level, limit, layers, school, +3 more. Returns: result

bg3_spell_listC

A spell/passive/skill list by UUID or Name (highest layer wins).

Inputs: key (required), layers. Returns: result

bg3_spell_visualsC

The visual/audio kit of a spell: effects, animations, sounds, icon - for reusing vanilla VFX.

Inputs: name (required), layers. Returns: result

bg3_staticB

Static game data by UUID or Name (highest layer wins): kind = ClassDescription (classes/subclasses, with ProgressionTableUUID), LevelMapSeries (e.g. SuperiorityDie, per-level values), ActionResourceDefinition (resources: MaxValue, ReplenishType), Feat. A partial key lists matches.

Inputs: key (required), kind (required), layers. Returns: result

bg3_templateC

Resolve a root template (MapKey GUID or Name) through its ParentTemplateId chain.

Inputs: key (required), layers. Returns: result

bg3_test_cleanupA

Remove every test spawn, boost (tag BG3Test) and applied status, and stop recording.

Inputs: no named parameters. Returns: result

bg3_test_listC

Test cases a mod layer defines (TOML under /tests/bg3, or layers.json tests), validated against the index (spells, statuses, templates, aliases).

Inputs: layer (required), level, layers, class_name. Returns: result

bg3_test_planB

The step-by-step plan for a test build ([[build]] in the suite TOML): for every level the exact level-up choices (subclass, which spells, feat), chosen so every spell a test needs is learned by its test level, plus the tests that run there. Written to /docs/test-scripts/plan-.md.

Inputs: build (required), layer (required), layers. Returns: result

bg3_test_runA

Run one auto/script case end to end: stage (real combat, host acts first), scripted cast, verify, cleanup. Effects come from recorded events; costs are checked against the spell's loaded UseCosts (a scripted cast never charges them). player-mode cases need bg3_test_stage + a hotbar cast instead.

Inputs: layer (required), case_id (required), wait, layers. Returns: result

bg3_test_run_levelB

Run every automated case for a level, one after another, and summarise: the cases assigned to build (see bg3_test_plan), or all of class_name's cases at that level. Lists the player-mode cases that still need a hotbar cast.

Inputs: layer (required), level (required), wait, build, +2 more. Returns: result

bg3_test_scriptC

Write the human test script (step-by-step reading script per level and case) to /docs/test-scripts/ and return it.

Inputs: layer (required), level, layers, class_name. Returns: result

bg3_test_stageB

Set up one test case in the running game: preconditions (class level, spell learned through the class, not in combat), spawns, HP/status setup, host-first initiative, safety watch, event recorder. mode="script" cases are cast immediately. Then the user acts; finish with bg3_test_verify.

Inputs: layer (required), case_id (required), layers. Returns: result

bg3_test_verifyB

Evaluate the staged case's expectations (before/after snapshots + recorded events), report PASS/FAIL per check with its fidelity, then remove every spawn, test boost and applied status.

Inputs: wait, layers, cleanup. Returns: result

bg3_toolkit_checkB

Compare the mod's Toolkit editor copy (dest='toolkit': /Data/Editor/Mods/; 'repo': /Editor/Mods/) with an export of its game-ready files: missing/extra objects and differing fields per file. Run before publishing; 0 problems = both copies say the same.

Inputs: layer (required), dest, limit. Returns: result

bg3_toolkit_exportA

Generate the Toolkit editor copy (.stats for stats, .tbl for lists/progressions/resources/classes/ level maps/feats) from the mod's game-ready files. Existing editor UUIDs are kept. write=False is a dry run listing files, object counts and warnings; write=True writes them (dest 'toolkit' or 'repo'). Close the Toolkit first: it overwrites files it has open.

Inputs: layer (required), dest, write. Returns: result

bg3_toolkit_statusB

Where the Larian Toolkit expects this mod (Data/Projects, Mods, Public, Editor/Mods): present or missing, linked to the repo or a separate copy, file counts, and the meta.lsx version (mod.io readiness).

Inputs: layer (required). Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3/5.0

    Scored across 44 tools

    Disambiguation3/5

    Many tools are clearly distinct by context (layer management vs. runtime Script Extender vs. toolkit vs. tests), but several pairs overlap: bg3_ingame_check/bg3_se_live_entry both compare indexed vs. loaded data, bg3_search/bg3_search_effects/bg3_references/bg3_static all search different facets, and bg3_get_entry/bg3_diff both resolve entries. Descriptions help, but with 44 tools an agent faces real misselection risk.

    Naming Consistency4/5

    All tools use the bg3_ prefix and snake_case, making them predictable and visually cohesive. However, the verb_noun pattern is not strict: many tools are noun-only (bg3_layers, bg3_environment, bg3_static, bg3_progression) while others use verbs (bg3_get_entry, bg3_search_effects), a minor deviation.

    Tool Count2/5

    At 44 tools, the surface is far above the typical well-scoped range of 3-15 and even beyond the 25+ threshold that the rubric treats as too many. Although BG3 modding is a complex domain, the sheer count—driven by many specialized test, runtime, and toolkit operations—creates a heavy cognitive load and higher likelihood of tool selection errors.

    Completeness4/5

    The surface covers a broad lifecycle: layer add/remove/list/refresh, stats and effect search/resolution, runtime Script Extender interaction, hot-loading, automated and scripted testing, linting, toolkit export/check, deployment, and environment discovery. Gaps are minor—for example, there is no direct stats-entry editing or file mutation tool, but the inspection/test/deploy focus appears intentional and workable.

    Maintenance

    ActivityActive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI assistants to search, browse, and manage mods across Nexus Mods, mod.io, Thunderstore, and Modrinth, as well as perform local diagnostics like detecting games and parsing crash logs.
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      Enables AI assistants to develop Desynced mods by providing direct access to the game's Lua API reference, base-game source, live logs, and installed mods, plus live control via a debugger link for evaluating and reloading Lua in the running game.
      9
      2
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      Enables AI coding agents to inspect a Mount & Blade II: Bannerlord installation and safely modify it — searching game data, writing and patching module XML with XSLT, handling localization, editing scenes, prefabs and assets, and driving the live Modding Kit editor. Every change is validated against the game's own schemas and journaled with backups.
      43
      1
      MIT