Getting Started
This page is for you if you already have an AI agent — Claude Code, Kimi, or one you built yourself — and want it to operate your local Foundry VTT directly.
arcane-fvtt is the bridge: it connects your agent to Foundry so the agent can read combat state, execute turns, and maintain the world. One command, one action, and every action returns an explicit receipt — the agent never has to guess. It is not a tactical AI: your agent decides how to play; the CLI just lands each action in Foundry reliably.
Five-minute quickstart
- Launch a debuggable Chrome (see “Launch Chrome” below).
- Log in to Foundry (see “Logging in to Foundry” below).
- Run your first command:
npm run fvtt:cdp -- --port 9230 --target-url <your Foundry URL> doctor. JSON state output means you’re connected.
How it works
The CLI talks to an already-open GM Foundry page over the Chrome DevTools Protocol (CDP), reaching the game / canvas / dnd5e / midi-qol APIs:
Agent / Skill
-> arcane-fvtt CLI
-> Chrome DevTools Protocol
-> Foundry browser page
-> game / canvas / dnd5e / midi-qol APIs
Reads return JSON that agents can consume directly; write receipts include the chat card, HP/effect diffs, warnings, and timing. Combat strategy, QA judgment, and fallback logic live on the agent side, not in the CLI.
Launch Chrome
We recommend launching a dedicated debug Chrome with a helper script. It uses an isolated profile, opens CDP port 9230, places the window on a normal screen area, and disables background timer throttling:
.\scripts\start-fvtt-chrome-fast.ps1 -KillExisting
The script only kills Chrome processes using the same debug port or the dedicated Arcane Foundry profile, so it will not close your normal working browser. The default window placement keeps Foundry visible for login and observation. Pass -Offscreen only when you truly need it off-screen.
If you prefer to launch Chrome yourself, you need at least:
--remote-debugging-address=127.0.0.1
--remote-debugging-port=9230
For combat automation also add:
--disable-background-timer-throttling
--disable-renderer-backgrounding
--disable-backgrounding-occluded-windows
--disable-features=CalculateNativeWinOcclusion
Basic usage
From the repo root:
npm run fvtt:cdp -- --port 9230 --target-url autofvtt.criticalrole.top doctor
npm run fvtt:cdp -- --port 9230 --target-url autofvtt.criticalrole.top world-info
npm run fvtt:cdp -- --port 9230 --target-url autofvtt.criticalrole.top scene-snapshot
npm run fvtt:cdp -- --port 9230 --target-url autofvtt.criticalrole.top combat-snapshot
Logging in to Foundry
login selects an account by display name or user ID on the Foundry /join page, fills in the access password, and waits for /game to be ready. You must declare the exact Foundry origin with --origin (or ARCANE_FVTT_ORIGIN); the CLI strictly checks scheme, host, and port to avoid handing credentials to the wrong page. The target user is expected to be a Gamemaster. Logging in again as the same Gamemaster is idempotent; the CLI will not log out or switch users on its own.
For login, use a separate Chrome port and profile. For a local world:
.\scripts\start-fvtt-chrome-fast.ps1 `
-Port 9231 `
-UserDataDir "$env:LOCALAPPDATA\ArcaneDesk\ChromeFoundryLogin-9231" `
-Url "http://127.0.0.1:30000/game"
An account without an access password only needs the name:
npm run fvtt:cdp -- --port 9231 login --origin http://127.0.0.1:30000 --user GM2
Never put a plaintext password on the command line. Read it securely into an environment variable:
$securePassword = Read-Host "Foundry access password" -AsSecureString
$env:ARCANE_FVTT_PASSWORD = [System.Net.NetworkCredential]::new("", $securePassword).Password
try {
npm run fvtt:cdp -- --port 9231 login --origin http://127.0.0.1:30000 --user Gamemaster
} finally {
Remove-Item Env:ARCANE_FVTT_PASSWORD -ErrorAction SilentlyContinue
}
You can also read the password from a protected file with --password-file <path>; a single trailing newline is trimmed automatically. The CLI deliberately does not offer a plaintext --password argument because npm run echoes command arguments to logs. Any non-empty parsed password is recursively redacted in JSON output and error details. If you really need a non-GM account, pass --no-gm.
Development validation
Type checking and unit tests:
npm run fvtt:cdp:typecheck
npm run fvtt:cdp:test
Browser smoke tests:
npm run fvtt:cdp -- --port 9231 login --origin http://127.0.0.1:30000 --user GM2
npm run fvtt:cdp -- --port 9230 --target-url autofvtt.criticalrole.top doctor
npm run fvtt:cdp -- --port 9230 --target-url autofvtt.criticalrole.top combat-snapshot
Only run write smoke tests in acceptable test worlds. Never do fuzzy bulk cleanup in production worlds, and always require explicit confirmation for any bulk deletion.
