Skip to main content
Glama
Txpple

fvtt-mcp-molten5e

by Txpple

fvtt-mcp-molten5e

A D&D 5e–only, Molten Hosting–optimized Model Context Protocol server for Foundry VTT, driven by Claude Code. It lets an AI GM assistant read and edit a live Foundry world (actors, items, journals, scenes, compendia, roll tables, cards…) and manage a Molten-hosted server's static files.

Paired with its bundled skills, it goes well beyond CRUD: it can author a complete, table-ready adventure end to end — scene, monsters, NPCs, a pregen PC or party, treasure, linked journals, and roll tables — scaled to however much the DM provides. Hand it only a map image and Claude reads the map and builds the whole module; hand it your own finished module and it faithfully recreates every stat block, item, and handout in the VTT.

Two importers worth calling out:

  • D&D Beyond → a real PC. Point it at a player's publicly shared D&D Beyond sheet and it builds a fully leveled Foundry character — class/subclass/species/background, ability scores, the level-1 choices, spells, and starting equipment — canonicalized to the premium 2024 books.

  • Adventure map packs → your world. Import a battlemap module (a distributed Foundry scene-pack with its own compendiums) faithfully: every scene with its walls, lights, day/night mood, and navigation thumbnails, plus the journal of map keys — with all assets re-pointed into your world, and cross-version (older and newer Foundry formats) handled. Tom Cartos packs are the first supported format; more to come.


📐 Design north star — design.md. The mission, scope, the skills decide, tools do contract, and the NPC authoring doctrine all live there; it's the document every skill, tool, and refactor traces back to. 🚧 Still under construction — actively evolving alongside the project, so expect it (and the tool surface) to change.

Why this shape

Managed Foundry hosts (like Molten) don't expose a general control API and you can't run a process next to the game server. The only supported way in is Foundry's own authenticated client.

So the MCP server drives a headless Chromium client (via Playwright): it wakes the (sleeping) Molten box with the Magic URL, joins the world as a dedicated Foundry user, waits for game.ready, and injects a page-side library that exposes the world's own client APIs. Claude Code talks to the MCP server over stdio; the server turns each tool call into a call inside that live page.

Claude Code  ──stdio──>  MCP server  (dist/index.js, on your PC)
                              │  Playwright → headless Chromium (src/foundry.ts)
                              ▼
                    Headless Foundry client
                    (wakes the box, joins the live world as a dedicated GM user)
                              │  the world's own client APIs (window.__fvtt)
                              ▼
                    Foundry VTT world (Molten-hosted)

The headless client connects lazily: tools/list answers without touching Foundry, and the first actual tool call is what wakes the box and joins the world. The whole tool tree depends on one seam — foundry.call(name, args) — and only src/foundry.ts ever imports Playwright.

Two-plane model

  • Plane A — the live bridge. World documents (actors, items, journals, scenes, compendia, roll tables, cards, ownership). Goes through the headless Foundry client while the server is awake — the only safe way to read/write live world data.

  • Plane B — Molten files. Talks to Molten's own file endpoints directly (no bridge): upload/serve static assets over WebDAV and map Data/-relative paths to public URLs.

Safety rule baked in: a running world's database (LevelDB stores under Data/worlds/<world>/data/) must never be written over the file channel — that corrupts it. Plane-B file ops are restricted to static assets and refuse world-DB paths; bulk DB edits are an offline-only flow (stop → Create Backup → fvtt unpack → edit → fvtt pack → start, via foundryvtt-cli). The Molten management panel is never scripted (their ToU forbids it); only the Magic-URL wake, WebDAV, and the Foundry server are automated.

Related MCP server: Foundry VTT MCP Bridge

Scope

In scope: actors — both NPCs and full leveled PCs (including import from a publicly shared D&D Beyond character) — items, journals, scenes (the scene document and its placeables — walls, lights, tokens, regions/teleporters, ambient sounds, tiles, drawings, map notes), playlists, roll tables, cards, macros, combat-tracker config, compendium manipulation — especially pulling content out ("make an actor from the MM owlbear") — and asset upload. With the bundled skills these compose into end-to-end adventures — from reading a provided map image to drive a scene and everything in it, to importing a distributed battlemap module (e.g. Tom Cartos scene-packs) faithfully into your world. Authoring prefers the 2024 dnd5e data model, sourced from PHB / DMG / MM; if the requested content isn't in those packs the tool says so rather than inventing it.

Out of scope (for now): non-5e game systems; live session assistance — monitoring a running game and interjecting during play (live chat, running the monsters' combat turns) is the next phase (see design.md §8), not built yet; AI map-image generation (Claude reads a provided map, it does not draw one); scripting the Molten management panel. (Scene placeables — walls, lights, tokens, regions — are authored and edited as scene contents; what's out of scope is driving them live on the canvas during a running session.)


Repository layout

src/
  index.ts          MCP server entry (stdio) — serves the registry's tools over JSON-RPC
  registry.ts       single source of truth: tool name → handler (advertised list derived from it)
  foundry.ts        THE Playwright seam: launch headless Chromium → wake → join → inject → call()
  config.ts         env/config loader (reads .env from the repo root)
  tools/            MCP tool classes — Plane A world tools + molten/ (Plane B WebDAV file tools)
  page/             page-side domain library, bundled into dist/page.bundle.js and injected
scripts/            dev/maintenance scripts (verify-*.mjs live acceptance, spike-headless)
tests/              gated live integration suites (offline unit tests live beside the code in src/**)

Requirements

  • Node.js 22+ (developed/tested on Node 24; see .nvmrc; CI runs 22 + 24). On Windows, if Node isn't on PATH, use the full path to node.exe (see wiring below).

  • A Chromium for Playwrightnpx playwright install chromium (Playwright is a devDependency; the headless bridge drives this browser).

  • Foundry VTT 14.x with the D&D 5e system, hosted on Molten, plus a dedicated passwordless Foundry user for the MCP to join as.

Build

npm install
npx playwright install chromium   # one-time: the headless browser the bridge drives
npm run build                     # tsc → dist/, then esbuild bundles the in-page library

npm run build runs tsc && node esbuild.page.mjs: TypeScript compiles src/** to dist/, then esbuild bundles the page-side library (src/page/**) into dist/page.bundle.js for injection. Tests: npm test (offline unit suite on vitest). Live integration suites are gated — see vitest.integration.config.ts and npm run test:integration.

Dev watch: npm run dev rebuilds the page bundle once, then runs tsc --watch for src/**. Because the page library is a separate esbuild artifact, editing anything under src/page/** while developing needs npm run dev:page (esbuild --watch) alongside it — otherwise the running server keeps injecting the stale dist/page.bundle.js.

Wire into Claude Code

Register the built MCP server in your Claude Code config. Copy .mcp.json.example to a .mcp.json Claude Code reads (project-scoped, or your ~/.claude.json mcpServers) and set absolute paths:

{
  "mcpServers": {
    "foundry-molten5e": {
      "command": "C:/Program Files/nodejs/node.exe",
      "args": ["C:/path/to/fvtt-mcp-molten5e/dist/index.js"]
    }
  }
}
  • Use an absolute path to the root dist/index.js (Claude Code may launch the server from any directory).

  • On Windows, point command at the full node.exe path if Node isn't on PATH.

  • The server loads its .env from the repo root regardless of working directory.

  • The headless client connects lazily — the first tool call wakes the Molten box and joins the world, so the initial call after a cold box can take a while.

Configuration

Copy .env.example to .env (gitignored) and fill in your instance:

  • Non-secret, per-instance: MOLTEN_SERVER_URL, MOLTEN_WORLD_ID, MOLTEN_WEBDAV_URL, MOLTEN_FILEBROWSER_URL, FOUNDRY_USER (the dedicated passwordless user to join as; defaults to MCP-Claude). The committed defaults are neutral your-server/your-world placeholders.

  • Wake (optional but recommended): MOLTEN_MAGIC_URL — Molten's "Server Startup / Magic URL" (…?s=token), GET to wake a sleeping box before joining.

  • Secrets (never commit — env only): MOLTEN_WEBDAV_PASSWORD (upload-asset / asset file ops), MOLTEN_ADMIN_KEY. Read them from your Molten panel → Server Details. Each tool reports which variable to set if its secret is missing.

Tools

138 tools total: 128 over the headless bridge (Plane A) + 10 Molten WebDAV file tools (Plane B).

Plane A (bridge) covers world introspection and editing — actors, items, compendium search, journals & quests, scenes and their placeables (walls, lights, tokens, regions/teleporters, ambient sounds, tiles, drawings, notes), roll tables, cards, playlists, ownership, folders/organization, macros, combat-tracker config, and 5e-specific helpers (NPC creation, PC building & leveling, D&D Beyond import, feature/spell granting, structured inventory/loot authoring), plus the asset-composition + reference-integrity tools. Plane B (Molten WebDAV) is the asset file library.

Plane B — Molten file tools (WebDAV):

Tool

What it does

list-assets

List a directory under Data/ (folders + files, with size/type/public URL)

asset-info

Existence + size/type/mtime/public URL for one path under Data/

download-asset

Download a file from under Data/ to a local path

upload-asset

Upload a local file under Data/ (auto-creates parents; refuses world-DB paths)

upload-asset-tree

Recursively upload a local directory tree under Data/ (preserves layout)

create-asset-folder

Create a folder (and missing parents) under Data/ (idempotent)

delete-asset

Delete a file (reference-aware; refuses if still used unless force)

move-asset

Move/rename a file (refuses or relinks references; relink/force)

copy-asset

Copy a file under Data/

asset-url

Map a Data/-relative path to its public HTTPS URL (pure, no network)

Plane A — asset composition + reference integrity (bridge):

Tool

What it does

find-asset-references

Find every scene/actor/journal/playlist/… that references an asset path

relink-asset

Rewrite all references from one asset path to another (dryRun supported)

create-playlist

Create a Playlist from sound paths (the flagship "upload → playlist" wiring)

create-scene

Create a Scene from a background image path

update-scene

Update a scene's fields, including swapping its background image

set-actor-art

Set an actor's portrait (+ prototype token) from an image path

add-journal-image

Append an image page to a journal entry

The remaining Plane A tools cover world CRUD (create-actor-from-compendium/author-npc, add-feature (features / compendium features / spells), import-item (copy a real PHB/DMG item — art + stats — onto an actor or the sidebar), add-item (author structured weapons/armor/consumables/loot/containers), create-item, create-journal/create-quest-journal, create-rolltable, create-cards, …), listing/search (list-actors, search-compendium, list-journals, …), and organization (create-folder, move-documents, bulk-delete). See the handlers map in src/registry.ts for the full dispatch table.

Plane B file ops run over WebDAV (need MOLTEN_WEBDAV_PASSWORD, work whenever the VM is awake). Plane A tools run over the headless bridge (need the world joined). Write tools refuse live world-DB paths; destructive file ops consult find-asset-references first.

Security

  • Outbound-only, nothing public. The server and the headless browser run on your machine and make only outbound connections (to Foundry on Molten, and to Anthropic); nothing listens for inbound traffic, and the headless client authenticates to Foundry exactly as a normal user would.

  • Secrets stay in .env (gitignored), with tight file perms — never commit MOLTEN_WEBDAV_PASSWORD, MOLTEN_ADMIN_KEY, or your Claude token. Errors name the missing variable, never its value.

  • Treat all agent inputs as untrusted (chat, transcripts, web) — prompt-injection can ride in. Plane-A writes are inherently safe because they go through Foundry's own client APIs; Plane-B destructive file ops are reference-aware, refuse live world-DB paths (canonicalized, ..-rejecting), and deletes resolve strictly (exact id/name, no fuzzy match).

  • Anything under Data/ is served publicly over HTTPS with no auth — don't upload anything sensitive.

Contributing

The project is one package: a Node-side MCP server (src/) that drives a headless Foundry page through the foundry.call(name, args) seam, plus a page-side library (src/page/**, bundled into dist/page.bundle.js and injected as window.__fvtt). Adding a tool touches both halves:

  1. MCP tool class (src/tools/<category>.ts) — declare the input contract once as a hoisted zod schema; getToolDefinitions() returns { name, description, inputSchema: toInputSchema(schema) } (the advertised JSON Schema is generated from that zod via src/utils/schema.ts — never hand-written), plus a handleX(args) that schema.parsees and calls foundry.call('<op>', data).

  2. Register it (src/registry.ts) — instantiate the class, add its getToolDefinitions() to the collected definitions, and add a '<tool-name>': args => tool.handleX(args) entry to the handlers map. The advertised tool list is derived from handlers, so a handler with no matching definition fails loudly at startup (src/tools/registry.test.ts guards the surface).

  3. Page-side op (src/page/<domain>.ts) — implement <op>(args) and register it in src/page/index.ts. This runs inside the live Foundry page (the actual Document.create / update / delete): import only browser + Foundry globals here, never Node/Playwright.

  4. Build + verifynpm run build, then npm test, npm run typecheck, npm run knip, and biome (npm run check). For live changes, npm run test:integration against a real world.


Support

Issues: GitHub Issues

Acknowledgments

Used as a reference: adambdooley/foundry-vtt-mcp by Adam Dooley.

License

MIT License — see LICENSE for details.

Install Server
A
license - permissive license
A
quality
A
maintenance

Maintenance

Maintainers
Response time
1dRelease cycle
8Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Txpple/fvtt-mcp-molten5e'

If you have feedback or need assistance with the MCP directory API, please join our Discord server