fvtt-mcp-molten5e logo

fvtt-mcp-molten5e

Txpple/fvtt-mcp-molten5e
0 starsMITUpdated 2026-06-25Community

Is this your server?

Add your score badge to your README and get your server in front of 45k+ builders a month.

Works with

Claude CodeClaude DesktopCursorVS CodeClineCodex CLIOpenClaw+ any MCP client

Install to Claude Code

This server doesn't publish a one-line install command. Follow the setup in the source repository.

Summary

Enables AI assistants to read and edit live D&D 5e Foundry VTT worlds and manage Molten-hosted server lifecycle via the Model Context Protocol.

README.md

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.

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).

  1. 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).

  1. 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.

  1. 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.

See related servers & alternatives →

Related MCP servers

Browse all →

Related guides

Hand-picked reading to help you choose and use AI & ML servers.